Feature/runtime cdn and plugin loader (#240)
* feat(ui): 完善 UI 布局系统和编辑器可视化工具 * refactor: 移除 ModuleRegistry,统一使用 PluginManager 插件系统 * fix: 修复 CodeQL 警告并提升测试覆盖率 * refactor: 分离运行时入口点,解决 runtime bundle 包含 React 的问题 * fix(ci): 添加 editor-core 和 editor-runtime 到 CI 依赖构建步骤 * docs: 完善 ServiceContainer 文档,新增 Symbol.for 模式和 @InjectProperty 说明 * fix(ci): 修复 type-check 失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): 修复类型检查失败问题 * fix(ci): behavior-tree 构建添加 @tauri-apps 外部依赖 * fix(ci): behavior-tree 添加 @tauri-apps/plugin-fs 类型依赖 * fix(ci): platform-web 添加缺失的 behavior-tree 依赖 * fix(lint): 移除正则表达式中不必要的转义字符
This commit is contained in:
287
packages/behavior-tree/src/editor/index.ts
Normal file
287
packages/behavior-tree/src/editor/index.ts
Normal file
@@ -0,0 +1,287 @@
|
||||
/**
|
||||
* Behavior Tree Editor Module
|
||||
* 行为树编辑器模块
|
||||
*/
|
||||
|
||||
import type { ServiceContainer } from '@esengine/ecs-framework';
|
||||
import { TransformComponent } from '@esengine/ecs-components';
|
||||
import {
|
||||
type IEditorModuleLoader,
|
||||
type IPluginLoader,
|
||||
type PanelDescriptor,
|
||||
type EntityCreationTemplate,
|
||||
type FileCreationTemplate,
|
||||
type FileActionHandler,
|
||||
PanelPosition,
|
||||
CompilerRegistry,
|
||||
ICompilerRegistry,
|
||||
InspectorRegistry,
|
||||
IInspectorRegistry,
|
||||
MessageHub,
|
||||
IMessageHub,
|
||||
createLogger,
|
||||
PluginAPI,
|
||||
} from '@esengine/editor-runtime';
|
||||
|
||||
// Runtime imports (relative paths since we're in the same package)
|
||||
import { BehaviorTreeRuntimeComponent } from '../execution/BehaviorTreeRuntimeComponent';
|
||||
|
||||
// Editor components and services
|
||||
import { BehaviorTreeService } from './services/BehaviorTreeService';
|
||||
import { FileSystemService } from './services/FileSystemService';
|
||||
import { BehaviorTreeCompiler } from './compiler/BehaviorTreeCompiler';
|
||||
import { BehaviorTreeNodeInspectorProvider } from './providers/BehaviorTreeNodeInspectorProvider';
|
||||
import { BehaviorTreeEditorPanel } from './components/panels/BehaviorTreeEditorPanel';
|
||||
import { useBehaviorTreeDataStore } from './stores';
|
||||
import { createRootNode } from './domain/constants/RootNode';
|
||||
import { PluginContext } from './PluginContext';
|
||||
|
||||
// Import runtime module and descriptor
|
||||
import { BehaviorTreeRuntimeModule, descriptor } from './BehaviorTreePlugin';
|
||||
|
||||
// 导入编辑器 CSS 样式
|
||||
import './styles/BehaviorTreeNode.css';
|
||||
import './styles/Toast.css';
|
||||
import './components/panels/BehaviorTreeEditorPanel.css';
|
||||
import './components/panels/BehaviorTreePropertiesPanel.css';
|
||||
|
||||
const logger = createLogger('BehaviorTreeEditorModule');
|
||||
|
||||
/**
|
||||
* Behavior Tree Editor Module
|
||||
* 行为树编辑器模块加载器
|
||||
*/
|
||||
export class BehaviorTreeEditorModule implements IEditorModuleLoader {
|
||||
private services?: ServiceContainer;
|
||||
|
||||
async install(services: ServiceContainer): Promise<void> {
|
||||
this.services = services;
|
||||
|
||||
// 设置插件上下文
|
||||
PluginContext.setServices(services);
|
||||
|
||||
// 注册服务
|
||||
this.registerServices(services);
|
||||
|
||||
// 注册编译器
|
||||
this.registerCompilers(services);
|
||||
|
||||
// 注册节点检视器
|
||||
this.registerInspectorProviders(services);
|
||||
|
||||
logger.info('BehaviorTree editor module installed');
|
||||
}
|
||||
|
||||
async uninstall(): Promise<void> {
|
||||
if (this.services) {
|
||||
this.services.unregister(FileSystemService);
|
||||
this.services.unregister(BehaviorTreeService);
|
||||
}
|
||||
|
||||
useBehaviorTreeDataStore.getState().reset();
|
||||
PluginContext.clear();
|
||||
this.services = undefined;
|
||||
|
||||
logger.info('BehaviorTree editor module uninstalled');
|
||||
}
|
||||
|
||||
private registerServices(services: ServiceContainer): void {
|
||||
// FileSystemService (BehaviorTreeService depends on it)
|
||||
if (services.isRegistered(FileSystemService)) {
|
||||
services.unregister(FileSystemService);
|
||||
}
|
||||
services.registerSingleton(FileSystemService);
|
||||
|
||||
// BehaviorTreeService
|
||||
if (services.isRegistered(BehaviorTreeService)) {
|
||||
services.unregister(BehaviorTreeService);
|
||||
}
|
||||
services.registerSingleton(BehaviorTreeService);
|
||||
}
|
||||
|
||||
private registerCompilers(services: ServiceContainer): void {
|
||||
try {
|
||||
const compilerRegistry = services.resolve<CompilerRegistry>(ICompilerRegistry);
|
||||
const compiler = new BehaviorTreeCompiler();
|
||||
compilerRegistry.register(compiler);
|
||||
logger.info('BehaviorTreeCompiler registered');
|
||||
} catch (error) {
|
||||
logger.error('Failed to register compiler:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private registerInspectorProviders(services: ServiceContainer): void {
|
||||
try {
|
||||
const inspectorRegistry = services.resolve<InspectorRegistry>(IInspectorRegistry);
|
||||
if (!inspectorRegistry) {
|
||||
logger.error('InspectorRegistry not found in services');
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用 Symbol 解析 MessageHub(跨包访问需要使用 Symbol)
|
||||
const messageHub = services.resolve<MessageHub>(IMessageHub);
|
||||
if (!messageHub) {
|
||||
logger.error('MessageHub not found in services');
|
||||
return;
|
||||
}
|
||||
|
||||
const provider = new BehaviorTreeNodeInspectorProvider();
|
||||
provider.setMessageHub(messageHub);
|
||||
|
||||
inspectorRegistry.register(provider);
|
||||
logger.info('BehaviorTreeNodeInspectorProvider registered');
|
||||
} catch (error) {
|
||||
logger.error('Failed to register inspector provider:', error);
|
||||
}
|
||||
}
|
||||
|
||||
getPanels(): PanelDescriptor[] {
|
||||
return [
|
||||
{
|
||||
id: 'behavior-tree-editor',
|
||||
title: 'Behavior Tree Editor',
|
||||
position: PanelPosition.Center,
|
||||
closable: true,
|
||||
component: BehaviorTreeEditorPanel,
|
||||
order: 100,
|
||||
isDynamic: true
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
getFileCreationTemplates(): FileCreationTemplate[] {
|
||||
return [{
|
||||
id: 'behavior-tree',
|
||||
label: 'Behavior Tree',
|
||||
extension: 'btree',
|
||||
icon: 'GitBranch',
|
||||
create: async (filePath: string) => {
|
||||
const rootNode = createRootNode();
|
||||
const rootNodeData = {
|
||||
id: rootNode.id,
|
||||
type: rootNode.template.type,
|
||||
displayName: rootNode.template.displayName,
|
||||
data: rootNode.data,
|
||||
position: {
|
||||
x: rootNode.position.x,
|
||||
y: rootNode.position.y
|
||||
},
|
||||
children: []
|
||||
};
|
||||
|
||||
const emptyTree = {
|
||||
name: filePath.replace(/.*[/\\]/, '').replace('.btree', ''),
|
||||
nodes: [rootNodeData],
|
||||
connections: [],
|
||||
variables: {}
|
||||
};
|
||||
|
||||
const content = JSON.stringify(emptyTree, null, 2);
|
||||
// Write using Tauri FS API
|
||||
const { writeTextFile } = await import('@tauri-apps/plugin-fs');
|
||||
await writeTextFile(filePath, content);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
getFileActionHandlers(): FileActionHandler[] {
|
||||
return [{
|
||||
extensions: ['btree'],
|
||||
onDoubleClick: async (filePath: string) => {
|
||||
if (this.services) {
|
||||
const service = this.services.resolve(BehaviorTreeService);
|
||||
if (service) {
|
||||
await service.loadFromFile(filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
getEntityCreationTemplates(): EntityCreationTemplate[] {
|
||||
return [{
|
||||
id: 'behavior-tree-entity',
|
||||
label: 'AI Entity',
|
||||
icon: 'GitBranch',
|
||||
category: 'other',
|
||||
order: 100,
|
||||
create: (_parentEntityId?: number): number => {
|
||||
const scene = PluginAPI.scene;
|
||||
const entityStore = PluginAPI.entityStore;
|
||||
const messageHub = PluginAPI.messageHub;
|
||||
|
||||
// 统计现有 AI Entity 数量
|
||||
const aiEntityCount = entityStore.getAllEntities()
|
||||
.filter((e: any) => e.name.startsWith('AI Entity')).length;
|
||||
const entityName = `AI Entity ${aiEntityCount + 1}`;
|
||||
|
||||
// 创建实体
|
||||
const entity = scene.createEntity(entityName);
|
||||
|
||||
// 添加 Transform 组件
|
||||
entity.addComponent(new TransformComponent());
|
||||
|
||||
// 添加行为树运行时组件
|
||||
const btComponent = new BehaviorTreeRuntimeComponent();
|
||||
btComponent.autoStart = true;
|
||||
entity.addComponent(btComponent);
|
||||
|
||||
// 注册到实体存储
|
||||
entityStore.addEntity(entity);
|
||||
|
||||
// 发送通知
|
||||
messageHub.publish('entity:added', { entity });
|
||||
messageHub.publish('scene:modified', {});
|
||||
|
||||
// 选中新创建的实体
|
||||
entityStore.selectEntity(entity);
|
||||
|
||||
logger.info(`Created AI Entity: ${entity.id}`);
|
||||
return entity.id;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
// Create the complete plugin with editor module
|
||||
export const BehaviorTreePlugin: IPluginLoader = {
|
||||
descriptor,
|
||||
runtimeModule: new BehaviorTreeRuntimeModule(),
|
||||
editorModule: new BehaviorTreeEditorModule(),
|
||||
};
|
||||
|
||||
export { BehaviorTreeRuntimeModule };
|
||||
|
||||
// Re-exports for editor functionality
|
||||
export { PluginContext } from './PluginContext';
|
||||
export { BehaviorTreeEditorPanel } from './components/panels/BehaviorTreeEditorPanel';
|
||||
export * from './services/BehaviorTreeService';
|
||||
export * from './providers/BehaviorTreeNodeInspectorProvider';
|
||||
|
||||
export * from './domain';
|
||||
export * from './application/commands/tree';
|
||||
export * from './application/use-cases';
|
||||
export * from './application/services/BlackboardManager';
|
||||
export * from './application/services/ExecutionController';
|
||||
export * from './application/services/GlobalBlackboardService';
|
||||
export * from './application/interfaces/IExecutionHooks';
|
||||
export * from './application/state/BehaviorTreeDataStore';
|
||||
export * from './hooks';
|
||||
export * from './stores';
|
||||
export type { EditorConfig } from './types';
|
||||
export * from './infrastructure/factories/NodeFactory';
|
||||
export * from './infrastructure/serialization/BehaviorTreeSerializer';
|
||||
export * from './infrastructure/validation/BehaviorTreeValidator';
|
||||
export * from './infrastructure/events/EditorEventBus';
|
||||
export * from './infrastructure/services/NodeRegistryService';
|
||||
export * from './utils/BehaviorTreeExecutor';
|
||||
export * from './utils/DOMCache';
|
||||
export * from './utils/portUtils';
|
||||
export * from './utils/RuntimeLoader';
|
||||
export * from './compiler/BehaviorTreeCompiler';
|
||||
export {
|
||||
ICON_MAP,
|
||||
ROOT_NODE_TEMPLATE,
|
||||
DEFAULT_EDITOR_CONFIG
|
||||
} from './config/editorConstants';
|
||||
export * from './interfaces/IEditorExtensions';
|
||||
Reference in New Issue
Block a user