feat(editor-core): 添加用户系统自动注册功能 (#283)
* feat(editor-core): 添加用户系统自动注册功能 - IUserCodeService 新增 registerSystems/unregisterSystems/getRegisteredSystems 方法 - UserCodeService 实现系统检测、实例化和场景注册逻辑 - ServiceRegistry 在预览开始时注册用户系统,停止时移除 - 热更新时自动重新加载用户系统 - 更新 System 脚本模板添加 @ECSSystem 装饰器 * feat(editor-core): 添加编辑器脚本支持(Inspector/Gizmo) - registerEditorExtensions 实际注册用户 Inspector 和 Gizmo - 添加 unregisterEditorExtensions 方法 - ServiceRegistry 在项目加载时编译并加载编辑器脚本 - 项目关闭时自动清理编辑器扩展 - 添加 Inspector 和 Gizmo 脚本创建模板
This commit is contained in:
@@ -295,26 +295,39 @@ export class ServiceRegistry {
|
|||||||
PluginSDKRegistry.initialize();
|
PluginSDKRegistry.initialize();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Compile runtime scripts | 编译运行时脚本
|
// 1. 编译运行时脚本 | Compile runtime scripts
|
||||||
const compileResult = await userCodeService.compile({
|
const runtimeResult = await userCodeService.compile({
|
||||||
projectPath: projectPath,
|
projectPath: projectPath,
|
||||||
target: UserCodeTarget.Runtime
|
target: UserCodeTarget.Runtime
|
||||||
});
|
});
|
||||||
|
|
||||||
if (compileResult.success && compileResult.outputPath) {
|
if (runtimeResult.success && runtimeResult.outputPath) {
|
||||||
// Load compiled module | 加载编译后的模块
|
const module = await userCodeService.load(runtimeResult.outputPath, UserCodeTarget.Runtime);
|
||||||
const module = await userCodeService.load(compileResult.outputPath, UserCodeTarget.Runtime);
|
|
||||||
|
|
||||||
// Register user components to editor | 注册用户组件到编辑器
|
|
||||||
userCodeService.registerComponents(module, componentRegistry);
|
userCodeService.registerComponents(module, componentRegistry);
|
||||||
|
|
||||||
// Notify that user code has been reloaded | 通知用户代码已重新加载
|
|
||||||
messageHub.publish('usercode:reloaded', {
|
messageHub.publish('usercode:reloaded', {
|
||||||
projectPath,
|
projectPath,
|
||||||
exports: Object.keys(module.exports)
|
exports: Object.keys(module.exports)
|
||||||
});
|
});
|
||||||
} else if (compileResult.errors.length > 0) {
|
} else if (runtimeResult.errors.length > 0) {
|
||||||
console.warn('[UserCodeService] Compilation errors:', compileResult.errors);
|
console.warn('[UserCodeService] Runtime compilation errors:', runtimeResult.errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 编译编辑器脚本 | Compile editor scripts
|
||||||
|
const editorResult = await userCodeService.compile({
|
||||||
|
projectPath: projectPath,
|
||||||
|
target: UserCodeTarget.Editor
|
||||||
|
});
|
||||||
|
|
||||||
|
if (editorResult.success && editorResult.outputPath) {
|
||||||
|
const editorModule = await userCodeService.load(editorResult.outputPath, UserCodeTarget.Editor);
|
||||||
|
userCodeService.registerEditorExtensions(editorModule, componentInspectorRegistry);
|
||||||
|
messageHub.publish('usercode:editor-reloaded', {
|
||||||
|
projectPath,
|
||||||
|
exports: Object.keys(editorModule.exports)
|
||||||
|
});
|
||||||
|
} else if (editorResult.errors.length > 0) {
|
||||||
|
// 编辑器脚本编译错误只记录,不影响运行时
|
||||||
|
console.warn('[UserCodeService] Editor compilation errors:', editorResult.errors);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[UserCodeService] Failed to compile/load:', error);
|
console.error('[UserCodeService] Failed to compile/load:', error);
|
||||||
@@ -333,17 +346,20 @@ export class ServiceRegistry {
|
|||||||
console.log('[UserCodeService] Hot reload event:', event.changedFiles);
|
console.log('[UserCodeService] Hot reload event:', event.changedFiles);
|
||||||
|
|
||||||
if (event.newModule) {
|
if (event.newModule) {
|
||||||
// 1. Register new/updated components to registries
|
|
||||||
// 1. 注册新的/更新的组件到注册表
|
// 1. 注册新的/更新的组件到注册表
|
||||||
userCodeService.registerComponents(event.newModule, componentRegistry);
|
userCodeService.registerComponents(event.newModule, componentRegistry);
|
||||||
|
|
||||||
// 2. Hot reload: update prototype chain of existing instances
|
|
||||||
// 2. 热更新:更新现有实例的原型链
|
// 2. 热更新:更新现有实例的原型链
|
||||||
const updatedCount = userCodeService.hotReloadInstances(event.newModule);
|
const updatedCount = userCodeService.hotReloadInstances(event.newModule);
|
||||||
console.log(`[UserCodeService] Hot reloaded ${updatedCount} component instances`);
|
console.log(`[UserCodeService] Hot reloaded ${updatedCount} component instances`);
|
||||||
|
|
||||||
// 3. Notify that user code has been reloaded
|
// 3. 如果正在预览,热更新用户系统
|
||||||
// 3. 通知用户代码已重新加载
|
const scene = Core.scene;
|
||||||
|
if (scene && !scene.isEditorMode) {
|
||||||
|
userCodeService.hotReloadSystems(event.newModule, scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 通知用户代码已重新加载
|
||||||
messageHub.publish('usercode:reloaded', {
|
messageHub.publish('usercode:reloaded', {
|
||||||
projectPath: data.path,
|
projectPath: data.path,
|
||||||
exports: Object.keys(event.newModule.exports),
|
exports: Object.keys(event.newModule.exports),
|
||||||
@@ -355,11 +371,12 @@ export class ServiceRegistry {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Subscribe to project:closed to stop watching
|
// Subscribe to project:closed to stop watching and cleanup
|
||||||
// 订阅 project:closed 以停止监视
|
// 订阅 project:closed 以停止监视和清理
|
||||||
messageHub.subscribe('project:closed', async () => {
|
messageHub.subscribe('project:closed', async () => {
|
||||||
currentProjectPath = null;
|
currentProjectPath = null;
|
||||||
await userCodeService.stopWatch();
|
await userCodeService.stopWatch();
|
||||||
|
userCodeService.unregisterEditorExtensions(componentInspectorRegistry);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Subscribe to script file changes (create/delete) from editor operations
|
// Subscribe to script file changes (create/delete) from editor operations
|
||||||
@@ -378,6 +395,27 @@ export class ServiceRegistry {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 预览开始时注册用户系统
|
||||||
|
// Register user systems when preview starts
|
||||||
|
messageHub.subscribe('preview:start', () => {
|
||||||
|
const runtimeModule = userCodeService.getModule(UserCodeTarget.Runtime);
|
||||||
|
if (runtimeModule) {
|
||||||
|
const scene = Core.scene;
|
||||||
|
if (scene) {
|
||||||
|
userCodeService.registerSystems(runtimeModule, scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 预览停止时移除用户系统
|
||||||
|
// Unregister user systems when preview stops
|
||||||
|
messageHub.subscribe('preview:stop', () => {
|
||||||
|
const scene = Core.scene;
|
||||||
|
if (scene) {
|
||||||
|
userCodeService.unregisterSystems(scene);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// 注册默认场景模板 - 创建默认相机
|
// 注册默认场景模板 - 创建默认相机
|
||||||
// Register default scene template - creates default camera
|
// Register default scene template - creates default camera
|
||||||
this.registerDefaultSceneTemplate();
|
this.registerDefaultSceneTemplate();
|
||||||
|
|||||||
@@ -275,31 +275,22 @@ export class ${className} extends Component {
|
|||||||
category: 'Script',
|
category: 'Script',
|
||||||
getContent: (fileName: string) => {
|
getContent: (fileName: string) => {
|
||||||
const className = fileName.replace(/\.ts$/, '');
|
const className = fileName.replace(/\.ts$/, '');
|
||||||
return `import { EntitySystem, Matcher, type Entity } from '@esengine/ecs-framework';
|
return `import { EntitySystem, Matcher, ECSSystem, type Entity } from '@esengine/ecs-framework';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ${className}
|
* ${className}
|
||||||
*/
|
*/
|
||||||
|
@ECSSystem('${className}')
|
||||||
export class ${className} extends EntitySystem {
|
export class ${className} extends EntitySystem {
|
||||||
// 定义系统处理的组件类型
|
constructor() {
|
||||||
// Define component types this system processes
|
// 定义系统处理的组件类型 | Define component types this system processes
|
||||||
protected getMatcher(): Matcher {
|
// super(Matcher.all(SomeComponent));
|
||||||
// 返回匹配器,指定需要哪些组件
|
super(Matcher.empty());
|
||||||
// Return matcher specifying required components
|
|
||||||
// return Matcher.all(SomeComponent);
|
|
||||||
return Matcher.empty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected updateEntity(entity: Entity, deltaTime: number): void {
|
protected updateEntity(entity: Entity, deltaTime: number): void {
|
||||||
// 处理每个实体
|
// 处理每个实体 | Process each entity
|
||||||
// Process each entity
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 可选:系统初始化
|
|
||||||
// Optional: System initialization
|
|
||||||
// onInitialize(): void {
|
|
||||||
// super.onInitialize();
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -320,6 +311,86 @@ export function ${name.charAt(0).toLowerCase() + name.slice(1)}(): void {
|
|||||||
// 在这里编写代码
|
// 在这里编写代码
|
||||||
// Write your code here
|
// Write your code here
|
||||||
}
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ts-inspector',
|
||||||
|
label: 'Inspector',
|
||||||
|
extension: '.ts',
|
||||||
|
icon: 'FileCode',
|
||||||
|
category: 'Editor',
|
||||||
|
getContent: (fileName: string) => {
|
||||||
|
const className = fileName.replace(/\.ts$/, '');
|
||||||
|
return `import React from 'react';
|
||||||
|
import type { Component } from '@esengine/ecs-framework';
|
||||||
|
import type { IComponentInspector, ComponentInspectorContext } from '@esengine/editor-core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ${className}
|
||||||
|
*
|
||||||
|
* 自定义组件检查器 | Custom component inspector
|
||||||
|
* 放置在 scripts/editor/ 目录下 | Place in scripts/editor/ directory
|
||||||
|
*/
|
||||||
|
export class ${className} implements IComponentInspector {
|
||||||
|
readonly id = '${className.toLowerCase()}';
|
||||||
|
readonly name = '${className}';
|
||||||
|
readonly priority = 10;
|
||||||
|
// 目标组件类型名称 | Target component type names
|
||||||
|
readonly targetComponents = ['YourComponent'];
|
||||||
|
|
||||||
|
canHandle(component: Component): boolean {
|
||||||
|
return this.targetComponents.includes(component.constructor.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(context: ComponentInspectorContext): React.ReactElement {
|
||||||
|
const { component } = context;
|
||||||
|
|
||||||
|
return React.createElement('div', { className: 'custom-inspector' },
|
||||||
|
React.createElement('h4', null, '${className}'),
|
||||||
|
React.createElement('pre', null, JSON.stringify(component, null, 2))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'ts-gizmo',
|
||||||
|
label: 'Gizmo',
|
||||||
|
extension: '.ts',
|
||||||
|
icon: 'FileCode',
|
||||||
|
category: 'Editor',
|
||||||
|
getContent: (fileName: string) => {
|
||||||
|
const className = fileName.replace(/\.ts$/, '');
|
||||||
|
return `import type { Component, Entity } from '@esengine/ecs-framework';
|
||||||
|
import type { IGizmoRenderData } from '@esengine/editor-core';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ${className}
|
||||||
|
*
|
||||||
|
* 自定义 Gizmo 提供者 | Custom Gizmo provider
|
||||||
|
* 放置在 scripts/editor/ 目录下 | Place in scripts/editor/ directory
|
||||||
|
*/
|
||||||
|
export class ${className} {
|
||||||
|
// 目标组件类型 | Target component type
|
||||||
|
// 需要替换为实际的组件类 | Replace with actual component class
|
||||||
|
readonly targetComponent = null; // YourComponent
|
||||||
|
|
||||||
|
draw(component: Component, entity: Entity, isSelected: boolean): IGizmoRenderData[] {
|
||||||
|
// 返回要绘制的 Gizmo 数据 | Return gizmo data to draw
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
type: 'circle',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
radius: 10,
|
||||||
|
strokeColor: isSelected ? '#00ff00' : '#ffffff',
|
||||||
|
strokeWidth: 2
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,6 +219,39 @@ export interface IUserCodeService {
|
|||||||
*/
|
*/
|
||||||
registerComponents(module: UserCodeModule, componentRegistry?: any): void;
|
registerComponents(module: UserCodeModule, componentRegistry?: any): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register user systems to scene.
|
||||||
|
* 注册用户系统到场景。
|
||||||
|
*
|
||||||
|
* Automatically detects and instantiates System subclasses from user module,
|
||||||
|
* then adds them to the scene.
|
||||||
|
* 自动检测用户模块中的 System 子类并实例化,然后添加到场景。
|
||||||
|
*
|
||||||
|
* @param module - User code module | 用户代码模块
|
||||||
|
* @param scene - Scene to add systems | 要添加系统的场景
|
||||||
|
* @returns Array of registered system instances | 注册的系统实例数组
|
||||||
|
*/
|
||||||
|
registerSystems(module: UserCodeModule, scene: any): any[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister user systems from scene.
|
||||||
|
* 从场景注销用户系统。
|
||||||
|
*
|
||||||
|
* Removes previously registered user systems from the scene.
|
||||||
|
* 从场景移除之前注册的用户系统。
|
||||||
|
*
|
||||||
|
* @param scene - Scene to remove systems | 要移除系统的场景
|
||||||
|
*/
|
||||||
|
unregisterSystems(scene: any): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get registered user systems.
|
||||||
|
* 获取已注册的用户系统。
|
||||||
|
*
|
||||||
|
* @returns Array of registered system instances | 注册的系统实例数组
|
||||||
|
*/
|
||||||
|
getRegisteredSystems(): any[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register editor extensions from user module.
|
* Register editor extensions from user module.
|
||||||
* 从用户模块注册编辑器扩展。
|
* 从用户模块注册编辑器扩展。
|
||||||
@@ -227,11 +260,19 @@ export interface IUserCodeService {
|
|||||||
* 自动检测并注册:
|
* 自动检测并注册:
|
||||||
* - Component inspectors
|
* - Component inspectors
|
||||||
* - Gizmo providers
|
* - Gizmo providers
|
||||||
* - Editor panels
|
|
||||||
*
|
*
|
||||||
* @param module - User code module | 用户代码模块
|
* @param module - User code module | 用户代码模块
|
||||||
|
* @param inspectorRegistry - Component inspector registry | 组件检查器注册表
|
||||||
*/
|
*/
|
||||||
registerEditorExtensions(module: UserCodeModule): void;
|
registerEditorExtensions(module: UserCodeModule, inspectorRegistry?: any): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister editor extensions.
|
||||||
|
* 注销编辑器扩展。
|
||||||
|
*
|
||||||
|
* @param inspectorRegistry - Component inspector registry | 组件检查器注册表
|
||||||
|
*/
|
||||||
|
unregisterEditorExtensions(inspectorRegistry?: any): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start watching for file changes (hot reload).
|
* Start watching for file changes (hot reload).
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import {
|
|||||||
USER_CODE_OUTPUT_DIR
|
USER_CODE_OUTPUT_DIR
|
||||||
} from './IUserCodeService';
|
} from './IUserCodeService';
|
||||||
import type { IFileSystem, FileEntry } from '../IFileSystem';
|
import type { IFileSystem, FileEntry } from '../IFileSystem';
|
||||||
|
import type { ComponentInspectorRegistry, IComponentInspector } from '../ComponentInspectorRegistry';
|
||||||
|
import { GizmoRegistry } from '../../Gizmos/GizmoRegistry';
|
||||||
|
|
||||||
const logger = createLogger('UserCodeService');
|
const logger = createLogger('UserCodeService');
|
||||||
|
|
||||||
@@ -44,6 +46,24 @@ export class UserCodeService implements IService, IUserCodeService {
|
|||||||
private _currentProjectPath: string | undefined;
|
private _currentProjectPath: string | undefined;
|
||||||
private _eventUnlisten: (() => void) | undefined;
|
private _eventUnlisten: (() => void) | undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已注册的用户系统实例
|
||||||
|
* Registered user system instances
|
||||||
|
*/
|
||||||
|
private _registeredSystems: any[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已注册的用户 Inspector ID 列表
|
||||||
|
* Registered user inspector IDs
|
||||||
|
*/
|
||||||
|
private _registeredInspectorIds: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已注册的用户 Gizmo 组件类型
|
||||||
|
* Registered user gizmo component types
|
||||||
|
*/
|
||||||
|
private _registeredGizmoTypes: any[] = [];
|
||||||
|
|
||||||
constructor(fileSystem: IFileSystem) {
|
constructor(fileSystem: IFileSystem) {
|
||||||
this._fileSystem = fileSystem;
|
this._fileSystem = fileSystem;
|
||||||
}
|
}
|
||||||
@@ -459,52 +479,213 @@ export class UserCodeService implements IService, IUserCodeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register editor extensions from user module.
|
* Register user systems to scene.
|
||||||
* 从用户模块注册编辑器扩展。
|
* 注册用户系统到场景。
|
||||||
*
|
*
|
||||||
* @param module - User code module | 用户代码模块
|
* @param module - User code module | 用户代码模块
|
||||||
|
* @param scene - Scene to add systems | 要添加系统的场景
|
||||||
|
* @returns Array of registered system instances | 注册的系统实例数组
|
||||||
*/
|
*/
|
||||||
registerEditorExtensions(module: UserCodeModule): void {
|
registerSystems(module: UserCodeModule, scene: any): any[] {
|
||||||
if (module.target !== UserCodeTarget.Editor) {
|
if (module.target !== UserCodeTarget.Runtime) {
|
||||||
logger.warn('Cannot register editor extensions from runtime module | 无法从运行时模块注册编辑器扩展');
|
logger.warn('Cannot register systems from editor module | 无法从编辑器模块注册系统');
|
||||||
return;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
let inspectorCount = 0;
|
if (!scene) {
|
||||||
let gizmoCount = 0;
|
logger.warn('No scene provided for system registration | 未提供场景用于系统注册');
|
||||||
let panelCount = 0;
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先移除之前注册的用户系统 | Remove previously registered user systems first
|
||||||
|
this.unregisterSystems(scene);
|
||||||
|
|
||||||
|
const registeredSystems: any[] = [];
|
||||||
|
|
||||||
for (const [name, exported] of Object.entries(module.exports)) {
|
for (const [name, exported] of Object.entries(module.exports)) {
|
||||||
if (typeof exported !== 'function') {
|
if (typeof exported !== 'function') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for inspector | 检查检查器
|
// 检查是否是 System 子类 | Check if it's a System subclass
|
||||||
|
if (this._isSystemClass(exported)) {
|
||||||
|
try {
|
||||||
|
// 获取系统元数据 | Get system metadata
|
||||||
|
const metadata = (exported as any).__systemMetadata__;
|
||||||
|
const updateOrder = metadata?.updateOrder ?? 0;
|
||||||
|
const enabled = metadata?.enabled !== false;
|
||||||
|
|
||||||
|
// 实例化系统 | Instantiate system
|
||||||
|
const systemInstance = new (exported as any)();
|
||||||
|
|
||||||
|
// 设置系统属性 | Set system properties
|
||||||
|
if (typeof systemInstance.updateOrder !== 'undefined') {
|
||||||
|
systemInstance.updateOrder = updateOrder;
|
||||||
|
}
|
||||||
|
if (typeof systemInstance.enabled !== 'undefined') {
|
||||||
|
systemInstance.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记为用户系统,便于后续识别和移除 | Mark as user system for later identification and removal
|
||||||
|
systemInstance.__isUserSystem__ = true;
|
||||||
|
systemInstance.__userSystemName__ = name;
|
||||||
|
|
||||||
|
// 添加到场景 | Add to scene
|
||||||
|
scene.addSystem(systemInstance);
|
||||||
|
registeredSystems.push(systemInstance);
|
||||||
|
|
||||||
|
logger.info(`Registered user system: ${name} | 注册用户系统: ${name}`, {
|
||||||
|
updateOrder,
|
||||||
|
enabled
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Failed to register system ${name} | 注册系统 ${name} 失败:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._registeredSystems = registeredSystems;
|
||||||
|
|
||||||
|
logger.info(`Registered ${registeredSystems.length} user systems | 注册了 ${registeredSystems.length} 个用户系统`);
|
||||||
|
|
||||||
|
return registeredSystems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister user systems from scene.
|
||||||
|
* 从场景注销用户系统。
|
||||||
|
*
|
||||||
|
* @param scene - Scene to remove systems | 要移除系统的场景
|
||||||
|
*/
|
||||||
|
unregisterSystems(scene: any): void {
|
||||||
|
if (!scene) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const system of this._registeredSystems) {
|
||||||
|
try {
|
||||||
|
scene.removeSystem(system);
|
||||||
|
logger.debug(`Removed user system: ${system.__userSystemName__} | 移除用户系统: ${system.__userSystemName__}`);
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(`Failed to remove system ${system.__userSystemName__}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._registeredSystems = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get registered user systems.
|
||||||
|
* 获取已注册的用户系统。
|
||||||
|
*
|
||||||
|
* @returns Array of registered system instances | 注册的系统实例数组
|
||||||
|
*/
|
||||||
|
getRegisteredSystems(): any[] {
|
||||||
|
return [...this._registeredSystems];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hot reload user systems.
|
||||||
|
* 热更新用户系统。
|
||||||
|
*
|
||||||
|
* Removes old systems and registers new ones from the updated module.
|
||||||
|
* 移除旧系统并从更新的模块注册新系统。
|
||||||
|
*
|
||||||
|
* @param module - New user code module | 新的用户代码模块
|
||||||
|
* @param scene - Scene to update systems | 要更新系统的场景
|
||||||
|
* @returns Array of newly registered system instances | 新注册的系统实例数组
|
||||||
|
*/
|
||||||
|
hotReloadSystems(module: UserCodeModule, scene: any): any[] {
|
||||||
|
logger.info('Hot reloading user systems | 热更新用户系统');
|
||||||
|
return this.registerSystems(module, scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register editor extensions from user module.
|
||||||
|
* 从用户模块注册编辑器扩展。
|
||||||
|
*
|
||||||
|
* @param module - User code module | 用户代码模块
|
||||||
|
* @param inspectorRegistry - Component inspector registry | 组件检查器注册表
|
||||||
|
*/
|
||||||
|
registerEditorExtensions(module: UserCodeModule, inspectorRegistry?: ComponentInspectorRegistry): void {
|
||||||
|
if (module.target !== UserCodeTarget.Editor) {
|
||||||
|
logger.warn('Cannot register editor extensions from runtime module | 无法从运行时模块注册编辑器扩展');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先移除之前注册的扩展
|
||||||
|
this.unregisterEditorExtensions(inspectorRegistry);
|
||||||
|
|
||||||
|
let inspectorCount = 0;
|
||||||
|
let gizmoCount = 0;
|
||||||
|
|
||||||
|
for (const [name, exported] of Object.entries(module.exports)) {
|
||||||
|
if (typeof exported !== 'function') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册 Inspector
|
||||||
if (this._isInspectorClass(exported)) {
|
if (this._isInspectorClass(exported)) {
|
||||||
logger.debug(`Found inspector: ${name} | 发现检查器: ${name}`);
|
try {
|
||||||
inspectorCount++;
|
const inspector = new (exported as any)() as IComponentInspector;
|
||||||
|
if (inspectorRegistry) {
|
||||||
|
inspectorRegistry.register(inspector);
|
||||||
|
this._registeredInspectorIds.push(inspector.id);
|
||||||
|
logger.info(`Registered user inspector: ${name} (${inspector.id})`);
|
||||||
|
inspectorCount++;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Failed to register inspector ${name}:`, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for gizmo | 检查 Gizmo
|
// 注册 Gizmo
|
||||||
if (this._isGizmoClass(exported)) {
|
if (this._isGizmoClass(exported)) {
|
||||||
logger.debug(`Found gizmo: ${name} | 发现 Gizmo: ${name}`);
|
try {
|
||||||
gizmoCount++;
|
const gizmoProvider = new (exported as any)();
|
||||||
}
|
const targetComponent = gizmoProvider.targetComponent;
|
||||||
|
if (targetComponent) {
|
||||||
// Check for panel | 检查面板
|
GizmoRegistry.register(targetComponent, (component, entity, isSelected) => {
|
||||||
if (this._isPanelComponent(exported)) {
|
return gizmoProvider.draw(component, entity, isSelected);
|
||||||
logger.debug(`Found panel: ${name} | 发现面板: ${name}`);
|
});
|
||||||
panelCount++;
|
this._registeredGizmoTypes.push(targetComponent);
|
||||||
|
logger.info(`Registered user gizmo for: ${targetComponent.name || name}`);
|
||||||
|
gizmoCount++;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Failed to register gizmo ${name}:`, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Registered editor extensions | 注册编辑器扩展`, {
|
logger.info(`Registered editor extensions | 注册编辑器扩展`, {
|
||||||
inspectors: inspectorCount,
|
inspectors: inspectorCount,
|
||||||
gizmos: gizmoCount,
|
gizmos: gizmoCount
|
||||||
panels: panelCount
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister editor extensions.
|
||||||
|
* 注销编辑器扩展。
|
||||||
|
*
|
||||||
|
* @param inspectorRegistry - Component inspector registry | 组件检查器注册表
|
||||||
|
*/
|
||||||
|
unregisterEditorExtensions(inspectorRegistry?: ComponentInspectorRegistry): void {
|
||||||
|
// 注销 Inspector
|
||||||
|
if (inspectorRegistry) {
|
||||||
|
for (const id of this._registeredInspectorIds) {
|
||||||
|
inspectorRegistry.unregister(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._registeredInspectorIds = [];
|
||||||
|
|
||||||
|
// 注销 Gizmo
|
||||||
|
for (const componentType of this._registeredGizmoTypes) {
|
||||||
|
GizmoRegistry.unregister(componentType);
|
||||||
|
}
|
||||||
|
this._registeredGizmoTypes = [];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start watching for file changes (hot reload).
|
* Start watching for file changes (hot reload).
|
||||||
* 开始监视文件变更(热更新)。
|
* 开始监视文件变更(热更新)。
|
||||||
|
|||||||
Reference in New Issue
Block a user