/** * 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 { this.services = services; // 设置插件上下文 PluginContext.setServices(services); // 注册服务 this.registerServices(services); // 注册编译器 this.registerCompilers(services); // 注册节点检视器 this.registerInspectorProviders(services); logger.info('BehaviorTree editor module installed'); } async uninstall(): Promise { 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(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(IInspectorRegistry); if (!inspectorRegistry) { logger.error('InspectorRegistry not found in services'); return; } // 使用 Symbol 解析 MessageHub(跨包访问需要使用 Symbol) const messageHub = services.resolve(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';