refactor(editor): 统一配置管理、完善插件卸载和热更新同步 (#298)

主要变更:

1. 统一配置管理 (EditorConfig)
   - 新增 EditorConfig 集中管理路径、文件名、全局变量等配置
   - 添加 SDK 模块配置系统 (ISDKModuleConfig)
   - 重构 PluginSDKRegistry 使用配置而非硬编码

2. 完善插件卸载机制
   - 扩展 PluginRegisteredResources 追踪运行时资源
   - 实现完整的 deactivatePluginRuntime 清理流程
   - ComponentRegistry 添加 unregister/getRegisteredComponents 方法

3. 热更新同步机制
   - 新增 HotReloadCoordinator 协调热更新过程
   - 热更新期间暂停 ECS 循环避免竞态条件
   - 支持超时保护和失败恢复
This commit is contained in:
YHH
2025-12-09 09:06:29 +08:00
committed by GitHub
parent 40a38b8b88
commit 6c99b811ec
12 changed files with 1270 additions and 172 deletions

View File

@@ -21,6 +21,8 @@ import { FileActionRegistry } from '../Services/FileActionRegistry';
import { UIRegistry } from '../Services/UIRegistry';
import { MessageHub } from '../Services/MessageHub';
import { moduleRegistry } from '../Services/Module/ModuleRegistry';
import { SerializerRegistry } from '../Services/SerializerRegistry';
import { ComponentRegistry as EditorComponentRegistry } from '../Services/ComponentRegistry';
const logger = createLogger('PluginManager');
@@ -71,6 +73,9 @@ export interface NormalizedPlugin {
* Resources registered by plugin (for cleanup on unload)
*/
export interface PluginRegisteredResources {
// ==================== 编辑器资源 ====================
// Editor resources
/** 注册的面板ID | Registered panel IDs */
panelIds: string[];
/** 注册的菜单ID | Registered menu IDs */
@@ -85,6 +90,16 @@ export interface PluginRegisteredResources {
fileHandlers: any[];
/** 注册的文件模板 | Registered file templates */
fileTemplates: any[];
// ==================== 运行时资源 ====================
// Runtime resources
/** 注册的组件类型名称 | Registered component type names */
componentTypeNames: string[];
/** 注册的系统实例 | Registered system instances */
systemInstances: any[];
/** 注册的序列化器类型 | Registered serializer types */
serializerTypes: Array<{ pluginName: string; type: string }>;
}
/**
@@ -407,14 +422,20 @@ export class PluginManager implements IService {
logger.info(`activatePluginEditor: activating ${pluginId}`);
// 初始化资源跟踪
// Initialize resource tracking
const resources: PluginRegisteredResources = {
// 编辑器资源 | Editor resources
panelIds: [],
menuIds: [],
toolbarIds: [],
entityTemplateIds: [],
componentActions: [],
fileHandlers: [],
fileTemplates: []
fileTemplates: [],
// 运行时资源 | Runtime resources
componentTypeNames: [],
systemInstances: [],
serializerTypes: [],
};
// 获取注册表服务
@@ -627,31 +648,73 @@ export class PluginManager implements IService {
const runtimeModule = plugin.plugin.runtimeModule;
if (!runtimeModule) return;
// 注册组件
if (runtimeModule.registerComponents) {
runtimeModule.registerComponents(ComponentRegistry);
logger.debug(`Components registered for: ${pluginId}`);
// 确保资源跟踪对象存在
// Ensure resource tracking object exists
if (!plugin.registeredResources) {
plugin.registeredResources = {
panelIds: [],
menuIds: [],
toolbarIds: [],
entityTemplateIds: [],
componentActions: [],
fileHandlers: [],
fileTemplates: [],
componentTypeNames: [],
systemInstances: [],
serializerTypes: [],
};
}
// 注册服务
const resources = plugin.registeredResources;
// 注册组件(使用包装的 Registry 来跟踪)
// Register components (use wrapped registry to track)
if (runtimeModule.registerComponents) {
const componentsBefore = new Set(ComponentRegistry.getRegisteredComponents().map(c => c.name));
runtimeModule.registerComponents(ComponentRegistry);
const componentsAfter = ComponentRegistry.getRegisteredComponents();
// 跟踪新注册的组件
// Track newly registered components
for (const comp of componentsAfter) {
if (!componentsBefore.has(comp.name)) {
resources.componentTypeNames.push(comp.name);
}
}
logger.debug(`Components registered for: ${pluginId} (${resources.componentTypeNames.length} new)`);
}
// 注册服务(服务目前无法卸载,记录日志即可)
// Register services (services cannot be unloaded currently, just log)
if (runtimeModule.registerServices) {
runtimeModule.registerServices(this.services);
logger.debug(`Services registered for: ${pluginId}`);
}
// 创建系统
// 创建系统(跟踪创建的系统)
// Create systems (track created systems)
if (runtimeModule.createSystems) {
const systemsBefore = this.currentScene.systems.length;
runtimeModule.createSystems(this.currentScene, this.currentContext);
logger.debug(`Systems created for: ${pluginId}`);
const systemsAfter = this.currentScene.systems;
// 跟踪新创建的系统
// Track newly created systems
for (let i = systemsBefore; i < systemsAfter.length; i++) {
resources.systemInstances.push(systemsAfter[i]);
}
logger.debug(`Systems created for: ${pluginId} (${systemsAfter.length - systemsBefore} new)`);
}
// 调用系统创建后回调
// Call post-creation callback
if (runtimeModule.onSystemsCreated) {
runtimeModule.onSystemsCreated(this.currentScene, this.currentContext);
logger.debug(`Systems wired for: ${pluginId}`);
}
// 调用初始化
// Call initialization
if (runtimeModule.onInitialize) {
await runtimeModule.onInitialize();
logger.debug(`Runtime initialized for: ${pluginId}`);
@@ -661,22 +724,104 @@ export class PluginManager implements IService {
/**
* 动态卸载插件的运行时模块
* Dynamically deactivate plugin's runtime module
*
* 卸载顺序(与激活相反):
* 1. 调用 onDestroy 回调
* 2. 移除系统
* 3. 注销组件
* 4. 清理序列化器
*
* Unload order (reverse of activation):
* 1. Call onDestroy callback
* 2. Remove systems
* 3. Unregister components
* 4. Cleanup serializers
*/
private deactivatePluginRuntime(pluginId: string): void {
const plugin = this.plugins.get(pluginId);
if (!plugin) return;
const runtimeModule = plugin.plugin.runtimeModule;
if (!runtimeModule) return;
const resources = plugin.registeredResources;
// 调用销毁回调
if (runtimeModule.onDestroy) {
runtimeModule.onDestroy();
logger.debug(`Runtime destroyed for: ${pluginId}`);
// 1. 调用销毁回调
// Step 1: Call destroy callback
if (runtimeModule?.onDestroy) {
try {
runtimeModule.onDestroy();
logger.debug(`Runtime onDestroy called for: ${pluginId}`);
} catch (e) {
logger.error(`Error in onDestroy for ${pluginId}:`, e);
}
}
// 注意:组件和服务无法动态注销,这是设计限制
// 系统的移除需要场景支持,暂时只调用 onDestroy
if (!resources) {
logger.debug(`No resources to cleanup for: ${pluginId}`);
return;
}
// 2. 移除系统
// Step 2: Remove systems
if (this.currentScene && resources.systemInstances.length > 0) {
for (const system of resources.systemInstances) {
try {
this.currentScene.removeSystem(system);
logger.debug(`System removed: ${system.constructor?.name || 'Unknown'}`);
} catch (e) {
logger.error(`Failed to remove system:`, e);
}
}
resources.systemInstances = [];
}
// 3. 注销组件(从 Core 的 ComponentRegistry
// Step 3: Unregister components (from Core's ComponentRegistry)
if (resources.componentTypeNames.length > 0) {
for (const componentName of resources.componentTypeNames) {
try {
ComponentRegistry.unregister(componentName);
logger.debug(`Component unregistered: ${componentName}`);
} catch (e) {
logger.error(`Failed to unregister component ${componentName}:`, e);
}
}
// 同时从编辑器的 ComponentRegistry 注销
// Also unregister from editor's ComponentRegistry
if (this.services) {
const editorComponentRegistry = this.services.tryResolve(EditorComponentRegistry);
if (editorComponentRegistry) {
for (const componentName of resources.componentTypeNames) {
try {
editorComponentRegistry.unregister(componentName);
} catch (e) {
// 忽略,可能未注册到编辑器注册表
// Ignore, might not be registered in editor registry
}
}
}
}
resources.componentTypeNames = [];
}
// 4. 清理序列化器
// Step 4: Cleanup serializers
if (this.services && resources.serializerTypes.length > 0) {
const serializerRegistry = this.services.tryResolve(SerializerRegistry);
if (serializerRegistry) {
for (const { pluginName, type } of resources.serializerTypes) {
try {
serializerRegistry.unregister(pluginName, type);
logger.debug(`Serializer unregistered: ${pluginName}/${type}`);
} catch (e) {
logger.error(`Failed to unregister serializer ${pluginName}/${type}:`, e);
}
}
}
resources.serializerTypes = [];
}
logger.info(`Runtime deactivated for: ${pluginId}`);
}
/**