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:
355
packages/editor-core/src/Plugin/IPluginLoader.ts
Normal file
355
packages/editor-core/src/Plugin/IPluginLoader.ts
Normal file
@@ -0,0 +1,355 @@
|
||||
/**
|
||||
* 插件加载器接口
|
||||
* Plugin loader interfaces
|
||||
*/
|
||||
|
||||
import type { IScene, ServiceContainer, ComponentRegistry } from '@esengine/ecs-framework';
|
||||
import type { PluginDescriptor } from './PluginDescriptor';
|
||||
|
||||
/**
|
||||
* 系统创建上下文
|
||||
* System creation context
|
||||
*/
|
||||
export interface SystemContext {
|
||||
/** 是否为编辑器模式 | Is editor mode */
|
||||
isEditor: boolean;
|
||||
|
||||
/** 引擎桥接(如有) | Engine bridge (if available) */
|
||||
engineBridge?: any;
|
||||
|
||||
/** 渲染系统(如有) | Render system (if available) */
|
||||
renderSystem?: any;
|
||||
|
||||
/** 其他已创建的系统引用 | Other created system references */
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行时模块加载器
|
||||
* Runtime module loader
|
||||
*/
|
||||
export interface IRuntimeModuleLoader {
|
||||
/**
|
||||
* 注册组件到 ComponentRegistry
|
||||
* Register components to ComponentRegistry
|
||||
*/
|
||||
registerComponents(registry: typeof ComponentRegistry): void;
|
||||
|
||||
/**
|
||||
* 注册服务到 ServiceContainer
|
||||
* Register services to ServiceContainer
|
||||
*/
|
||||
registerServices?(services: ServiceContainer): void;
|
||||
|
||||
/**
|
||||
* 为场景创建系统
|
||||
* Create systems for scene
|
||||
*/
|
||||
createSystems?(scene: IScene, context: SystemContext): void;
|
||||
|
||||
/**
|
||||
* 模块初始化完成回调
|
||||
* Module initialization complete callback
|
||||
*/
|
||||
onInitialize?(): Promise<void>;
|
||||
|
||||
/**
|
||||
* 模块销毁回调
|
||||
* Module destroy callback
|
||||
*/
|
||||
onDestroy?(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 面板位置
|
||||
* Panel position
|
||||
*/
|
||||
export enum PanelPosition {
|
||||
Left = 'left',
|
||||
Right = 'right',
|
||||
Bottom = 'bottom',
|
||||
Center = 'center'
|
||||
}
|
||||
|
||||
/**
|
||||
* 面板描述符
|
||||
* Panel descriptor
|
||||
*/
|
||||
export interface PanelDescriptor {
|
||||
/** 面板ID | Panel ID */
|
||||
id: string;
|
||||
/** 面板标题 | Panel title */
|
||||
title: string;
|
||||
/** 面板图标 | Panel icon */
|
||||
icon?: string;
|
||||
/** 面板位置 | Panel position */
|
||||
position: PanelPosition;
|
||||
/** 渲染组件 | Render component */
|
||||
component?: any;
|
||||
/** 渲染函数 | Render function */
|
||||
render?: () => any;
|
||||
/** 默认大小 | Default size */
|
||||
defaultSize?: number;
|
||||
/** 是否可调整大小 | Is resizable */
|
||||
resizable?: boolean;
|
||||
/** 是否可关闭 | Is closable */
|
||||
closable?: boolean;
|
||||
/** 排序权重 | Order weight */
|
||||
order?: number;
|
||||
/** 是否为动态面板 | Is dynamic panel */
|
||||
isDynamic?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 菜单项描述符
|
||||
* Menu item descriptor
|
||||
*/
|
||||
export interface MenuItemDescriptor {
|
||||
/** 菜单ID | Menu ID */
|
||||
id: string;
|
||||
/** 菜单标签 | Menu label */
|
||||
label: string;
|
||||
/** 父菜单ID | Parent menu ID */
|
||||
parentId?: string;
|
||||
/** 图标 | Icon */
|
||||
icon?: string;
|
||||
/** 快捷键 | Shortcut */
|
||||
shortcut?: string;
|
||||
/** 执行函数 | Execute function */
|
||||
execute?: () => void;
|
||||
/** 子菜单 | Submenu items */
|
||||
children?: MenuItemDescriptor[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 工具栏项描述符
|
||||
* Toolbar item descriptor
|
||||
*/
|
||||
export interface ToolbarItemDescriptor {
|
||||
/** 工具栏项ID | Toolbar item ID */
|
||||
id: string;
|
||||
/** 标签 | Label */
|
||||
label: string;
|
||||
/** 图标 | Icon */
|
||||
icon: string;
|
||||
/** 提示 | Tooltip */
|
||||
tooltip?: string;
|
||||
/** 执行函数 | Execute function */
|
||||
execute: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件检视器提供者(简化版)
|
||||
* Component inspector provider (simplified)
|
||||
*/
|
||||
export interface ComponentInspectorProviderDef {
|
||||
/** 组件类型名 | Component type name */
|
||||
componentType: string;
|
||||
/** 优先级 | Priority */
|
||||
priority?: number;
|
||||
/** 渲染函数 | Render function */
|
||||
render: (component: any, entity: any, onChange: (key: string, value: any) => void) => any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gizmo 提供者注册
|
||||
* Gizmo provider registration
|
||||
*/
|
||||
export interface GizmoProviderRegistration {
|
||||
/** 组件类型 | Component type */
|
||||
componentType: any;
|
||||
/** 获取 Gizmo 数据 | Get gizmo data */
|
||||
getGizmoData: (component: any, entity: any, isSelected: boolean) => any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件操作处理器
|
||||
* File action handler
|
||||
*/
|
||||
export interface FileActionHandler {
|
||||
/** 支持的文件扩展名 | Supported file extensions */
|
||||
extensions: string[];
|
||||
/** 双击处理 | Double click handler */
|
||||
onDoubleClick?: (filePath: string) => void | Promise<void>;
|
||||
/** 打开处理 | Open handler */
|
||||
onOpen?: (filePath: string) => void | Promise<void>;
|
||||
/** 获取上下文菜单 | Get context menu */
|
||||
getContextMenuItems?: (filePath: string, parentPath: string) => any[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体创建模板
|
||||
* Entity creation template
|
||||
*/
|
||||
export interface EntityCreationTemplate {
|
||||
/** 模板ID | Template ID */
|
||||
id: string;
|
||||
/** 标签 | Label */
|
||||
label: string;
|
||||
/** 图标组件 | Icon component */
|
||||
icon?: any;
|
||||
/** 分类 | Category */
|
||||
category?: string;
|
||||
/** 排序权重 | Order weight */
|
||||
order?: number;
|
||||
/** 创建函数 | Create function */
|
||||
create: (parentEntityId?: number) => number | Promise<number>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件操作
|
||||
* Component action
|
||||
*/
|
||||
export interface ComponentAction {
|
||||
/** 操作ID | Action ID */
|
||||
id: string;
|
||||
/** 组件名 | Component name */
|
||||
componentName: string;
|
||||
/** 标签 | Label */
|
||||
label: string;
|
||||
/** 图标 | Icon */
|
||||
icon?: any;
|
||||
/** 排序权重 | Order weight */
|
||||
order?: number;
|
||||
/** 执行函数 | Execute function */
|
||||
execute: (component: any, entity: any) => void | Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化器接口
|
||||
* Serializer interface
|
||||
*/
|
||||
export interface ISerializer<T = any> {
|
||||
/** 获取支持的类型 | Get supported type */
|
||||
getSupportedType(): string;
|
||||
/** 序列化数据 | Serialize data */
|
||||
serialize(data: T): Uint8Array;
|
||||
/** 反序列化数据 | Deserialize data */
|
||||
deserialize(data: Uint8Array): T;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑器模块加载器
|
||||
* Editor module loader
|
||||
*/
|
||||
export interface IEditorModuleLoader {
|
||||
/**
|
||||
* 安装编辑器模块
|
||||
* Install editor module
|
||||
*/
|
||||
install(services: ServiceContainer): Promise<void>;
|
||||
|
||||
/**
|
||||
* 卸载编辑器模块
|
||||
* Uninstall editor module
|
||||
*/
|
||||
uninstall?(): Promise<void>;
|
||||
|
||||
/**
|
||||
* 返回面板描述列表
|
||||
* Get panel descriptors
|
||||
*/
|
||||
getPanels?(): PanelDescriptor[];
|
||||
|
||||
/**
|
||||
* 返回菜单项列表
|
||||
* Get menu items
|
||||
*/
|
||||
getMenuItems?(): MenuItemDescriptor[];
|
||||
|
||||
/**
|
||||
* 返回工具栏项列表
|
||||
* Get toolbar items
|
||||
*/
|
||||
getToolbarItems?(): ToolbarItemDescriptor[];
|
||||
|
||||
/**
|
||||
* 返回检视器提供者列表
|
||||
* Get inspector providers
|
||||
*/
|
||||
getInspectorProviders?(): ComponentInspectorProviderDef[];
|
||||
|
||||
/**
|
||||
* 返回 Gizmo 提供者列表
|
||||
* Get gizmo providers
|
||||
*/
|
||||
getGizmoProviders?(): GizmoProviderRegistration[];
|
||||
|
||||
/**
|
||||
* 返回文件操作处理器列表
|
||||
* Get file action handlers
|
||||
*/
|
||||
getFileActionHandlers?(): FileActionHandler[];
|
||||
|
||||
/**
|
||||
* 返回实体创建模板列表
|
||||
* Get entity creation templates
|
||||
*/
|
||||
getEntityCreationTemplates?(): EntityCreationTemplate[];
|
||||
|
||||
/**
|
||||
* 返回组件操作列表
|
||||
* Get component actions
|
||||
*/
|
||||
getComponentActions?(): ComponentAction[];
|
||||
|
||||
/**
|
||||
* 返回文件创建模板列表
|
||||
* Get file creation templates
|
||||
*/
|
||||
getFileCreationTemplates?(): FileCreationTemplate[];
|
||||
|
||||
// ===== 生命周期钩子 | Lifecycle hooks =====
|
||||
|
||||
/** 编辑器就绪 | Editor ready */
|
||||
onEditorReady?(): void | Promise<void>;
|
||||
|
||||
/** 项目打开 | Project open */
|
||||
onProjectOpen?(projectPath: string): void | Promise<void>;
|
||||
|
||||
/** 项目关闭 | Project close */
|
||||
onProjectClose?(): void | Promise<void>;
|
||||
|
||||
/** 场景加载 | Scene loaded */
|
||||
onSceneLoaded?(scenePath: string): void;
|
||||
|
||||
/** 场景保存前 | Before scene save */
|
||||
onSceneSaving?(scenePath: string): boolean | void;
|
||||
|
||||
/** 设置语言 | Set locale */
|
||||
setLocale?(locale: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一插件加载器
|
||||
* Unified plugin loader
|
||||
*/
|
||||
export interface IPluginLoader {
|
||||
/** 插件描述符 | Plugin descriptor */
|
||||
readonly descriptor: PluginDescriptor;
|
||||
|
||||
/** 运行时模块(可选) | Runtime module (optional) */
|
||||
readonly runtimeModule?: IRuntimeModuleLoader;
|
||||
|
||||
/** 编辑器模块(可选) | Editor module (optional) */
|
||||
readonly editorModule?: IEditorModuleLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文件创建模板
|
||||
* File creation template
|
||||
*/
|
||||
export interface FileCreationTemplate {
|
||||
/** 模板ID | Template ID */
|
||||
id: string;
|
||||
/** 标签 | Label */
|
||||
label: string;
|
||||
/** 扩展名 | Extension */
|
||||
extension: string;
|
||||
/** 图标 | Icon */
|
||||
icon?: string;
|
||||
/** 分类 | Category */
|
||||
category?: string;
|
||||
/** 创建函数 | Create function */
|
||||
create: (filePath: string) => Promise<void>;
|
||||
}
|
||||
163
packages/editor-core/src/Plugin/PluginDescriptor.ts
Normal file
163
packages/editor-core/src/Plugin/PluginDescriptor.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
/**
|
||||
* 插件系统类型定义
|
||||
* Plugin system type definitions
|
||||
*/
|
||||
|
||||
/**
|
||||
* 插件类别
|
||||
* Plugin category
|
||||
*/
|
||||
export type PluginCategory =
|
||||
| 'core' // 核心功能 | Core functionality
|
||||
| 'rendering' // 渲染相关 | Rendering
|
||||
| 'ui' // UI 系统 | UI System
|
||||
| 'ai' // AI/行为树 | AI/Behavior
|
||||
| 'physics' // 物理引擎 | Physics
|
||||
| 'audio' // 音频系统 | Audio
|
||||
| 'networking' // 网络功能 | Networking
|
||||
| 'tools' // 工具/编辑器扩展 | Tools/Editor extensions
|
||||
| 'content'; // 内容/资源 | Content/Assets
|
||||
|
||||
/**
|
||||
* 加载阶段 - 控制插件模块的加载顺序
|
||||
* Loading phase - controls the loading order of plugin modules
|
||||
*/
|
||||
export type LoadingPhase =
|
||||
| 'earliest' // 最早加载(核心模块) | Earliest (core modules)
|
||||
| 'preDefault' // 默认之前 | Before default
|
||||
| 'default' // 默认阶段 | Default phase
|
||||
| 'postDefault' // 默认之后 | After default
|
||||
| 'postEngine'; // 引擎初始化后 | After engine init
|
||||
|
||||
/**
|
||||
* 模块类型
|
||||
* Module type
|
||||
*/
|
||||
export type ModuleType = 'runtime' | 'editor';
|
||||
|
||||
/**
|
||||
* 模块描述符 - 描述插件内的一个模块
|
||||
* Module descriptor - describes a module within a plugin
|
||||
*/
|
||||
export interface ModuleDescriptor {
|
||||
/** 模块名称 | Module name */
|
||||
name: string;
|
||||
|
||||
/** 模块类型 | Module type */
|
||||
type: ModuleType;
|
||||
|
||||
/** 加载阶段 | Loading phase */
|
||||
loadingPhase?: LoadingPhase;
|
||||
|
||||
/** 模块入口文件(相对路径) | Module entry file (relative path) */
|
||||
entry?: string;
|
||||
|
||||
// ===== 运行时模块配置 | Runtime module config =====
|
||||
|
||||
/** 导出的组件类名列表 | Exported component class names */
|
||||
components?: string[];
|
||||
|
||||
/** 导出的系统类名列表 | Exported system class names */
|
||||
systems?: string[];
|
||||
|
||||
/** 导出的服务类名列表 | Exported service class names */
|
||||
services?: string[];
|
||||
|
||||
// ===== 编辑器模块配置 | Editor module config =====
|
||||
|
||||
/** 注册的面板ID列表 | Registered panel IDs */
|
||||
panels?: string[];
|
||||
|
||||
/** 注册的检视器类型列表 | Registered inspector types */
|
||||
inspectors?: string[];
|
||||
|
||||
/** 注册的 Gizmo 提供者列表 | Registered Gizmo providers */
|
||||
gizmoProviders?: string[];
|
||||
|
||||
/** 注册的编译器列表 | Registered compilers */
|
||||
compilers?: string[];
|
||||
|
||||
/** 注册的文件处理器扩展名 | Registered file handler extensions */
|
||||
fileHandlers?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件依赖
|
||||
* Plugin dependency
|
||||
*/
|
||||
export interface PluginDependency {
|
||||
/** 依赖的插件ID | Dependent plugin ID */
|
||||
id: string;
|
||||
|
||||
/** 版本要求(semver) | Version requirement (semver) */
|
||||
version?: string;
|
||||
|
||||
/** 是否可选 | Optional */
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件描述符 - 对应 plugin.json 文件
|
||||
* Plugin descriptor - corresponds to plugin.json
|
||||
*/
|
||||
export interface PluginDescriptor {
|
||||
/** 插件唯一标识符,如 "@esengine/tilemap" | Unique plugin ID */
|
||||
id: string;
|
||||
|
||||
/** 显示名称 | Display name */
|
||||
name: string;
|
||||
|
||||
/** 版本号 | Version */
|
||||
version: string;
|
||||
|
||||
/** 描述 | Description */
|
||||
description?: string;
|
||||
|
||||
/** 作者 | Author */
|
||||
author?: string;
|
||||
|
||||
/** 许可证 | License */
|
||||
license?: string;
|
||||
|
||||
/** 插件类别 | Plugin category */
|
||||
category: PluginCategory;
|
||||
|
||||
/** 标签(用于搜索) | Tags (for search) */
|
||||
tags?: string[];
|
||||
|
||||
/** 图标(Lucide 图标名) | Icon (Lucide icon name) */
|
||||
icon?: string;
|
||||
|
||||
/** 是否默认启用 | Enabled by default */
|
||||
enabledByDefault: boolean;
|
||||
|
||||
/** 是否可以包含内容资产 | Can contain content assets */
|
||||
canContainContent: boolean;
|
||||
|
||||
/** 是否为引擎内置插件 | Is engine built-in plugin */
|
||||
isEnginePlugin: boolean;
|
||||
|
||||
/** 是否为核心插件(不可禁用) | Is core plugin (cannot be disabled) */
|
||||
isCore?: boolean;
|
||||
|
||||
/** 模块列表 | Module list */
|
||||
modules: ModuleDescriptor[];
|
||||
|
||||
/** 依赖列表 | Dependency list */
|
||||
dependencies?: PluginDependency[];
|
||||
|
||||
/** 平台要求 | Platform requirements */
|
||||
platforms?: ('web' | 'desktop' | 'mobile')[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件状态
|
||||
* Plugin state
|
||||
*/
|
||||
export type PluginState =
|
||||
| 'unloaded' // 未加载 | Not loaded
|
||||
| 'loading' // 加载中 | Loading
|
||||
| 'loaded' // 已加载 | Loaded
|
||||
| 'active' // 已激活 | Active
|
||||
| 'error' // 错误 | Error
|
||||
| 'disabled'; // 已禁用 | Disabled
|
||||
775
packages/editor-core/src/Plugin/PluginManager.ts
Normal file
775
packages/editor-core/src/Plugin/PluginManager.ts
Normal file
@@ -0,0 +1,775 @@
|
||||
/**
|
||||
* 统一插件管理器
|
||||
* Unified Plugin Manager
|
||||
*/
|
||||
|
||||
import { createLogger, ComponentRegistry } from '@esengine/ecs-framework';
|
||||
import type { IScene, ServiceContainer, IService } from '@esengine/ecs-framework';
|
||||
import type {
|
||||
PluginDescriptor,
|
||||
PluginState,
|
||||
PluginCategory,
|
||||
LoadingPhase
|
||||
} from './PluginDescriptor';
|
||||
import type {
|
||||
IPluginLoader,
|
||||
SystemContext
|
||||
} from './IPluginLoader';
|
||||
import { EntityCreationRegistry } from '../Services/EntityCreationRegistry';
|
||||
import { ComponentActionRegistry } from '../Services/ComponentActionRegistry';
|
||||
import { FileActionRegistry } from '../Services/FileActionRegistry';
|
||||
import { UIRegistry } from '../Services/UIRegistry';
|
||||
|
||||
const logger = createLogger('PluginManager');
|
||||
|
||||
/**
|
||||
* PluginManager 服务标识符
|
||||
* PluginManager service identifier
|
||||
*/
|
||||
export const IPluginManager = Symbol.for('IPluginManager');
|
||||
|
||||
/**
|
||||
* 已注册的插件信息
|
||||
* Registered plugin info
|
||||
*/
|
||||
export interface RegisteredPlugin {
|
||||
/** 插件加载器 | Plugin loader */
|
||||
loader: IPluginLoader;
|
||||
/** 插件状态 | Plugin state */
|
||||
state: PluginState;
|
||||
/** 错误信息 | Error info */
|
||||
error?: Error;
|
||||
/** 是否启用 | Is enabled */
|
||||
enabled: boolean;
|
||||
/** 加载时间 | Load time */
|
||||
loadedAt?: number;
|
||||
/** 激活时间 | Activation time */
|
||||
activatedAt?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 插件配置
|
||||
* Plugin configuration
|
||||
*/
|
||||
export interface PluginConfig {
|
||||
/** 启用的插件ID列表 | Enabled plugin IDs */
|
||||
enabledPlugins: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载阶段顺序
|
||||
* Loading phase order
|
||||
*/
|
||||
const LOADING_PHASE_ORDER: LoadingPhase[] = [
|
||||
'earliest',
|
||||
'preDefault',
|
||||
'default',
|
||||
'postDefault',
|
||||
'postEngine'
|
||||
];
|
||||
|
||||
/**
|
||||
* 统一插件管理器
|
||||
* Unified Plugin Manager
|
||||
*
|
||||
* 使用方式:
|
||||
* 1. 在 ServiceContainer 中注册: services.registerInstance(IPluginManager, new PluginManager())
|
||||
* 2. 获取实例: services.resolve<PluginManager>(IPluginManager)
|
||||
*/
|
||||
export class PluginManager implements IService {
|
||||
private plugins: Map<string, RegisteredPlugin> = new Map();
|
||||
private initialized = false;
|
||||
private editorInitialized = false;
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* 释放资源
|
||||
* Dispose resources
|
||||
*/
|
||||
dispose(): void {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册插件
|
||||
* Register plugin
|
||||
*/
|
||||
register(loader: IPluginLoader): void {
|
||||
if (!loader) {
|
||||
logger.error('Cannot register plugin: loader is null or undefined');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!loader.descriptor) {
|
||||
logger.error('Cannot register plugin: descriptor is null or undefined', loader);
|
||||
return;
|
||||
}
|
||||
|
||||
const { id } = loader.descriptor;
|
||||
|
||||
if (!id) {
|
||||
logger.error('Cannot register plugin: descriptor.id is null or undefined', loader.descriptor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.plugins.has(id)) {
|
||||
logger.warn(`Plugin ${id} is already registered, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
const enabled = loader.descriptor.isCore || loader.descriptor.enabledByDefault;
|
||||
|
||||
this.plugins.set(id, {
|
||||
loader,
|
||||
state: 'loaded',
|
||||
enabled,
|
||||
loadedAt: Date.now()
|
||||
});
|
||||
|
||||
logger.info(`Plugin registered: ${id} (${loader.descriptor.name})`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用插件
|
||||
* Enable plugin
|
||||
*/
|
||||
enable(pluginId: string): boolean {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
if (!plugin) {
|
||||
logger.error(`Plugin ${pluginId} not found`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (plugin.loader.descriptor.isCore) {
|
||||
logger.warn(`Core plugin ${pluginId} cannot be disabled/enabled`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查依赖
|
||||
const deps = plugin.loader.descriptor.dependencies || [];
|
||||
for (const dep of deps) {
|
||||
if (dep.optional) continue;
|
||||
const depPlugin = this.plugins.get(dep.id);
|
||||
if (!depPlugin || !depPlugin.enabled) {
|
||||
logger.error(`Cannot enable ${pluginId}: dependency ${dep.id} is not enabled`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
plugin.enabled = true;
|
||||
plugin.state = 'loaded';
|
||||
logger.info(`Plugin enabled: ${pluginId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用插件
|
||||
* Disable plugin
|
||||
*/
|
||||
disable(pluginId: string): boolean {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
if (!plugin) {
|
||||
logger.error(`Plugin ${pluginId} not found`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (plugin.loader.descriptor.isCore) {
|
||||
logger.warn(`Core plugin ${pluginId} cannot be disabled`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否有其他插件依赖此插件
|
||||
for (const [id, p] of this.plugins) {
|
||||
if (!p.enabled || id === pluginId) continue;
|
||||
const deps = p.loader.descriptor.dependencies || [];
|
||||
const hasDep = deps.some(d => d.id === pluginId && !d.optional);
|
||||
if (hasDep) {
|
||||
logger.error(`Cannot disable ${pluginId}: plugin ${id} depends on it`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
plugin.enabled = false;
|
||||
plugin.state = 'disabled';
|
||||
logger.info(`Plugin disabled: ${pluginId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查插件是否启用
|
||||
* Check if plugin is enabled
|
||||
*/
|
||||
isEnabled(pluginId: string): boolean {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
return plugin?.enabled ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件状态
|
||||
* Get plugin state
|
||||
*/
|
||||
getState(pluginId: string): PluginState | undefined {
|
||||
return this.plugins.get(pluginId)?.state;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件
|
||||
* Get plugin
|
||||
*/
|
||||
getPlugin(pluginId: string): RegisteredPlugin | undefined {
|
||||
return this.plugins.get(pluginId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有插件
|
||||
* Get all plugins
|
||||
*/
|
||||
getAllPlugins(): RegisteredPlugin[] {
|
||||
return Array.from(this.plugins.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按类别获取插件
|
||||
* Get plugins by category
|
||||
*/
|
||||
getPluginsByCategory(category: PluginCategory): RegisteredPlugin[] {
|
||||
return this.getAllPlugins().filter(
|
||||
p => p.loader.descriptor.category === category
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已启用的插件
|
||||
* Get enabled plugins
|
||||
*/
|
||||
getEnabledPlugins(): RegisteredPlugin[] {
|
||||
return this.getAllPlugins().filter(p => p.enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化所有运行时模块
|
||||
* Initialize all runtime modules
|
||||
*/
|
||||
async initializeRuntime(services: ServiceContainer): Promise<void> {
|
||||
if (this.initialized) {
|
||||
logger.warn('Runtime already initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Initializing runtime modules...');
|
||||
|
||||
const sortedPlugins = this.sortByLoadingPhase('runtime');
|
||||
|
||||
// 注册组件
|
||||
for (const pluginId of sortedPlugins) {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
if (!plugin?.enabled) continue;
|
||||
|
||||
const runtimeModule = plugin.loader.runtimeModule;
|
||||
if (runtimeModule) {
|
||||
try {
|
||||
runtimeModule.registerComponents(ComponentRegistry);
|
||||
logger.debug(`Components registered for: ${pluginId}`);
|
||||
} catch (e) {
|
||||
logger.error(`Failed to register components for ${pluginId}:`, e);
|
||||
plugin.state = 'error';
|
||||
plugin.error = e as Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 注册服务
|
||||
for (const pluginId of sortedPlugins) {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
if (!plugin?.enabled || plugin.state === 'error') continue;
|
||||
|
||||
const runtimeModule = plugin.loader.runtimeModule;
|
||||
if (runtimeModule?.registerServices) {
|
||||
try {
|
||||
runtimeModule.registerServices(services);
|
||||
logger.debug(`Services registered for: ${pluginId}`);
|
||||
} catch (e) {
|
||||
logger.error(`Failed to register services for ${pluginId}:`, e);
|
||||
plugin.state = 'error';
|
||||
plugin.error = e as Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调用初始化回调 | Call initialization callbacks
|
||||
for (const pluginId of sortedPlugins) {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
if (!plugin?.enabled || plugin.state === 'error') continue;
|
||||
|
||||
const runtimeModule = plugin.loader.runtimeModule;
|
||||
if (runtimeModule?.onInitialize) {
|
||||
try {
|
||||
await runtimeModule.onInitialize();
|
||||
plugin.state = 'active';
|
||||
plugin.activatedAt = Date.now();
|
||||
logger.debug(`Initialized: ${pluginId}`);
|
||||
} catch (e) {
|
||||
logger.error(`Failed to initialize ${pluginId}:`, e);
|
||||
plugin.state = 'error';
|
||||
plugin.error = e as Error;
|
||||
}
|
||||
} else {
|
||||
// 没有初始化回调,直接激活 | No init callback, activate directly
|
||||
plugin.state = 'active';
|
||||
plugin.activatedAt = Date.now();
|
||||
}
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
logger.info('Runtime modules initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* 为场景创建系统
|
||||
* Create systems for scene
|
||||
*/
|
||||
createSystemsForScene(scene: IScene, context: SystemContext): void {
|
||||
logger.info('Creating systems for scene...');
|
||||
|
||||
const sortedPlugins = this.sortByLoadingPhase('runtime');
|
||||
|
||||
for (const pluginId of sortedPlugins) {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
if (!plugin?.enabled || plugin.state === 'error') continue;
|
||||
|
||||
const runtimeModule = plugin.loader.runtimeModule;
|
||||
if (runtimeModule?.createSystems) {
|
||||
try {
|
||||
runtimeModule.createSystems(scene, context);
|
||||
logger.debug(`Systems created for: ${pluginId}`);
|
||||
} catch (e) {
|
||||
logger.error(`Failed to create systems for ${pluginId}:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('Systems created for scene');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化所有编辑器模块
|
||||
* Initialize all editor modules
|
||||
*/
|
||||
async initializeEditor(services: ServiceContainer): Promise<void> {
|
||||
if (this.editorInitialized) {
|
||||
logger.warn('Editor already initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Initializing editor modules...');
|
||||
|
||||
const sortedPlugins = this.sortByLoadingPhase('editor');
|
||||
|
||||
// 获取注册表服务 | Get registry services
|
||||
const entityCreationRegistry = services.tryResolve(EntityCreationRegistry);
|
||||
const componentActionRegistry = services.tryResolve(ComponentActionRegistry);
|
||||
const fileActionRegistry = services.tryResolve(FileActionRegistry);
|
||||
const uiRegistry = services.tryResolve(UIRegistry);
|
||||
|
||||
for (const pluginId of sortedPlugins) {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
if (!plugin?.enabled) continue;
|
||||
|
||||
const editorModule = plugin.loader.editorModule;
|
||||
if (editorModule) {
|
||||
try {
|
||||
// 安装编辑器模块 | Install editor module
|
||||
await editorModule.install(services);
|
||||
logger.debug(`Editor module installed: ${pluginId}`);
|
||||
|
||||
// 注册实体创建模板 | Register entity creation templates
|
||||
if (entityCreationRegistry && editorModule.getEntityCreationTemplates) {
|
||||
const templates = editorModule.getEntityCreationTemplates();
|
||||
if (templates && templates.length > 0) {
|
||||
entityCreationRegistry.registerMany(templates);
|
||||
logger.debug(`Registered ${templates.length} entity templates from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册组件操作 | Register component actions
|
||||
if (componentActionRegistry && editorModule.getComponentActions) {
|
||||
const actions = editorModule.getComponentActions();
|
||||
if (actions && actions.length > 0) {
|
||||
for (const action of actions) {
|
||||
componentActionRegistry.register(action);
|
||||
}
|
||||
logger.debug(`Registered ${actions.length} component actions from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册文件操作处理器 | Register file action handlers
|
||||
if (fileActionRegistry && editorModule.getFileActionHandlers) {
|
||||
const handlers = editorModule.getFileActionHandlers();
|
||||
if (handlers && handlers.length > 0) {
|
||||
for (const handler of handlers) {
|
||||
fileActionRegistry.registerActionHandler(handler);
|
||||
}
|
||||
logger.debug(`Registered ${handlers.length} file action handlers from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册文件创建模板 | Register file creation templates
|
||||
if (fileActionRegistry && editorModule.getFileCreationTemplates) {
|
||||
const templates = editorModule.getFileCreationTemplates();
|
||||
if (templates && templates.length > 0) {
|
||||
for (const template of templates) {
|
||||
fileActionRegistry.registerCreationTemplate(template);
|
||||
}
|
||||
logger.debug(`Registered ${templates.length} file creation templates from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册面板 | Register panels
|
||||
if (uiRegistry && editorModule.getPanels) {
|
||||
const panels = editorModule.getPanels();
|
||||
if (panels && panels.length > 0) {
|
||||
uiRegistry.registerPanels(panels);
|
||||
logger.debug(`Registered ${panels.length} panels from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(`Failed to install editor module for ${pluginId}:`, e);
|
||||
plugin.state = 'error';
|
||||
plugin.error = e as Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.editorInitialized = true;
|
||||
logger.info('Editor modules initialized');
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化单个插件的编辑器模块(用于动态加载的项目插件)
|
||||
* Initialize a single plugin's editor module (for dynamically loaded project plugins)
|
||||
*/
|
||||
async initializePluginEditor(pluginId: string, services: ServiceContainer): Promise<void> {
|
||||
const plugin = this.plugins.get(pluginId);
|
||||
if (!plugin?.enabled) {
|
||||
logger.warn(`Plugin ${pluginId} not found or not enabled`);
|
||||
return;
|
||||
}
|
||||
|
||||
const editorModule = plugin.loader.editorModule;
|
||||
if (!editorModule) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取注册表服务 | Get registry services
|
||||
const entityCreationRegistry = services.tryResolve(EntityCreationRegistry);
|
||||
const componentActionRegistry = services.tryResolve(ComponentActionRegistry);
|
||||
const fileActionRegistry = services.tryResolve(FileActionRegistry);
|
||||
const uiRegistry = services.tryResolve(UIRegistry);
|
||||
|
||||
try {
|
||||
// 安装编辑器模块 | Install editor module
|
||||
await editorModule.install(services);
|
||||
logger.debug(`Editor module installed: ${pluginId}`);
|
||||
|
||||
// 注册实体创建模板 | Register entity creation templates
|
||||
if (entityCreationRegistry && editorModule.getEntityCreationTemplates) {
|
||||
const templates = editorModule.getEntityCreationTemplates();
|
||||
if (templates && templates.length > 0) {
|
||||
entityCreationRegistry.registerMany(templates);
|
||||
logger.debug(`Registered ${templates.length} entity templates from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册组件操作 | Register component actions
|
||||
if (componentActionRegistry && editorModule.getComponentActions) {
|
||||
const actions = editorModule.getComponentActions();
|
||||
if (actions && actions.length > 0) {
|
||||
for (const action of actions) {
|
||||
componentActionRegistry.register(action);
|
||||
}
|
||||
logger.debug(`Registered ${actions.length} component actions from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册文件操作处理器 | Register file action handlers
|
||||
if (fileActionRegistry && editorModule.getFileActionHandlers) {
|
||||
const handlers = editorModule.getFileActionHandlers();
|
||||
if (handlers && handlers.length > 0) {
|
||||
for (const handler of handlers) {
|
||||
fileActionRegistry.registerActionHandler(handler);
|
||||
}
|
||||
logger.debug(`Registered ${handlers.length} file action handlers from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册文件创建模板 | Register file creation templates
|
||||
if (fileActionRegistry && editorModule.getFileCreationTemplates) {
|
||||
const templates = editorModule.getFileCreationTemplates();
|
||||
if (templates && templates.length > 0) {
|
||||
for (const template of templates) {
|
||||
fileActionRegistry.registerCreationTemplate(template);
|
||||
}
|
||||
logger.debug(`Registered ${templates.length} file creation templates from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 注册面板 | Register panels
|
||||
if (uiRegistry && editorModule.getPanels) {
|
||||
const panels = editorModule.getPanels();
|
||||
if (panels && panels.length > 0) {
|
||||
uiRegistry.registerPanels(panels);
|
||||
logger.debug(`Registered ${panels.length} panels from: ${pluginId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 调用 onEditorReady(如果编辑器已就绪)
|
||||
if (this.editorInitialized && editorModule.onEditorReady) {
|
||||
await editorModule.onEditorReady();
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(`Failed to install editor module for ${pluginId}:`, e);
|
||||
plugin.state = 'error';
|
||||
plugin.error = e as Error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知编辑器就绪
|
||||
* Notify editor ready
|
||||
*/
|
||||
async notifyEditorReady(): Promise<void> {
|
||||
for (const [pluginId, plugin] of this.plugins) {
|
||||
if (!plugin.enabled) continue;
|
||||
const editorModule = plugin.loader.editorModule;
|
||||
if (editorModule?.onEditorReady) {
|
||||
try {
|
||||
await editorModule.onEditorReady();
|
||||
} catch (e) {
|
||||
logger.error(`Error in ${pluginId}.onEditorReady:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知项目打开
|
||||
* Notify project open
|
||||
*/
|
||||
async notifyProjectOpen(projectPath: string): Promise<void> {
|
||||
for (const [pluginId, plugin] of this.plugins) {
|
||||
if (!plugin.enabled) continue;
|
||||
const editorModule = plugin.loader.editorModule;
|
||||
if (editorModule?.onProjectOpen) {
|
||||
try {
|
||||
await editorModule.onProjectOpen(projectPath);
|
||||
} catch (e) {
|
||||
logger.error(`Error in ${pluginId}.onProjectOpen:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知项目关闭
|
||||
* Notify project close
|
||||
*/
|
||||
async notifyProjectClose(): Promise<void> {
|
||||
for (const [pluginId, plugin] of this.plugins) {
|
||||
if (!plugin.enabled) continue;
|
||||
const editorModule = plugin.loader.editorModule;
|
||||
if (editorModule?.onProjectClose) {
|
||||
try {
|
||||
await editorModule.onProjectClose();
|
||||
} catch (e) {
|
||||
logger.error(`Error in ${pluginId}.onProjectClose:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知场景加载
|
||||
* Notify scene loaded
|
||||
*/
|
||||
notifySceneLoaded(scenePath: string): void {
|
||||
for (const [pluginId, plugin] of this.plugins) {
|
||||
if (!plugin.enabled) continue;
|
||||
const editorModule = plugin.loader.editorModule;
|
||||
if (editorModule?.onSceneLoaded) {
|
||||
try {
|
||||
editorModule.onSceneLoaded(scenePath);
|
||||
} catch (e) {
|
||||
logger.error(`Error in ${pluginId}.onSceneLoaded:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知场景保存
|
||||
* Notify scene saving
|
||||
*/
|
||||
notifySceneSaving(scenePath: string): boolean {
|
||||
for (const [pluginId, plugin] of this.plugins) {
|
||||
if (!plugin.enabled) continue;
|
||||
const editorModule = plugin.loader.editorModule;
|
||||
if (editorModule?.onSceneSaving) {
|
||||
try {
|
||||
const result = editorModule.onSceneSaving(scenePath);
|
||||
if (result === false) {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error(`Error in ${pluginId}.onSceneSaving:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置所有插件的语言
|
||||
* Set locale for all plugins
|
||||
*/
|
||||
setLocale(locale: string): void {
|
||||
for (const [pluginId, plugin] of this.plugins) {
|
||||
if (!plugin.enabled) continue;
|
||||
const editorModule = plugin.loader.editorModule;
|
||||
if (editorModule?.setLocale) {
|
||||
try {
|
||||
editorModule.setLocale(locale);
|
||||
} catch (e) {
|
||||
logger.error(`Error in ${pluginId}.setLocale:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出配置
|
||||
* Export configuration
|
||||
*/
|
||||
exportConfig(): PluginConfig {
|
||||
const enabledPlugins: string[] = [];
|
||||
for (const [id, plugin] of this.plugins) {
|
||||
if (plugin.enabled && !plugin.loader.descriptor.isCore) {
|
||||
enabledPlugins.push(id);
|
||||
}
|
||||
}
|
||||
return { enabledPlugins };
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载配置
|
||||
* Load configuration
|
||||
*/
|
||||
loadConfig(config: PluginConfig): void {
|
||||
const { enabledPlugins } = config;
|
||||
|
||||
for (const [id, plugin] of this.plugins) {
|
||||
if (plugin.loader.descriptor.isCore) {
|
||||
plugin.enabled = true;
|
||||
} else {
|
||||
plugin.enabled = enabledPlugins.includes(id);
|
||||
plugin.state = plugin.enabled ? 'loaded' : 'disabled';
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`Loaded config: ${enabledPlugins.length} plugins enabled`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按加载阶段排序
|
||||
* Sort by loading phase
|
||||
*/
|
||||
private sortByLoadingPhase(moduleType: 'runtime' | 'editor'): string[] {
|
||||
const pluginIds = Array.from(this.plugins.keys());
|
||||
|
||||
// 先按依赖拓扑排序
|
||||
const sorted = this.topologicalSort(pluginIds);
|
||||
|
||||
// 再按加载阶段排序(稳定排序)
|
||||
sorted.sort((a, b) => {
|
||||
const pluginA = this.plugins.get(a);
|
||||
const pluginB = this.plugins.get(b);
|
||||
|
||||
const moduleA = moduleType === 'runtime'
|
||||
? pluginA?.loader.descriptor.modules.find(m => m.type === 'runtime')
|
||||
: pluginA?.loader.descriptor.modules.find(m => m.type === 'editor');
|
||||
|
||||
const moduleB = moduleType === 'runtime'
|
||||
? pluginB?.loader.descriptor.modules.find(m => m.type === 'runtime')
|
||||
: pluginB?.loader.descriptor.modules.find(m => m.type === 'editor');
|
||||
|
||||
const phaseA = moduleA?.loadingPhase || 'default';
|
||||
const phaseB = moduleB?.loadingPhase || 'default';
|
||||
|
||||
return LOADING_PHASE_ORDER.indexOf(phaseA) - LOADING_PHASE_ORDER.indexOf(phaseB);
|
||||
});
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 拓扑排序(处理依赖)
|
||||
* Topological sort (handle dependencies)
|
||||
*/
|
||||
private topologicalSort(pluginIds: string[]): string[] {
|
||||
const visited = new Set<string>();
|
||||
const result: string[] = [];
|
||||
|
||||
const visit = (id: string) => {
|
||||
if (visited.has(id)) return;
|
||||
visited.add(id);
|
||||
|
||||
const plugin = this.plugins.get(id);
|
||||
if (plugin) {
|
||||
const deps = plugin.loader.descriptor.dependencies || [];
|
||||
for (const dep of deps) {
|
||||
if (pluginIds.includes(dep.id)) {
|
||||
visit(dep.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push(id);
|
||||
};
|
||||
|
||||
for (const id of pluginIds) {
|
||||
visit(id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有场景系统
|
||||
* Clear scene systems
|
||||
*/
|
||||
clearSceneSystems(): void {
|
||||
for (const [pluginId, plugin] of this.plugins) {
|
||||
if (!plugin.enabled) continue;
|
||||
const runtimeModule = plugin.loader.runtimeModule;
|
||||
if (runtimeModule?.onDestroy) {
|
||||
try {
|
||||
runtimeModule.onDestroy();
|
||||
} catch (e) {
|
||||
logger.error(`Error in ${pluginId}.onDestroy:`, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置
|
||||
* Reset
|
||||
*/
|
||||
reset(): void {
|
||||
this.clearSceneSystems();
|
||||
this.plugins.clear();
|
||||
this.initialized = false;
|
||||
this.editorInitialized = false;
|
||||
logger.info('PluginManager reset');
|
||||
}
|
||||
}
|
||||
8
packages/editor-core/src/Plugin/index.ts
Normal file
8
packages/editor-core/src/Plugin/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* 插件系统
|
||||
* Plugin System
|
||||
*/
|
||||
|
||||
export * from './PluginDescriptor';
|
||||
export * from './IPluginLoader';
|
||||
export * from './PluginManager';
|
||||
Reference in New Issue
Block a user