Files
esengine/packages/engine/runtime-core/src/UserCodeRealm.ts

642 lines
18 KiB
TypeScript
Raw Normal View History

refactor: 代码规范化与依赖清理 (#317) * refactor(deps): 统一编辑器包依赖配置 & 优化分层架构 - 将 ecs-engine-bindgen 提升为 Layer 1 核心包 - 统一 9 个编辑器包的依赖声明模式 - 清理废弃的包目录 (ui, ui-editor, network-*) * refactor(tokens): 修复 PrefabService 令牌冲突 & 补充 module.json - 将 editor-core 的 PrefabServiceToken 改名为 EditorPrefabServiceToken 避免与 asset-system 的 PrefabServiceToken 冲突 (Symbol.for 冲突) - 为 mesh-3d 添加 module.json - 为 world-streaming 添加 module.json * refactor(editor-core): 整理导出结构 & 添加 blueprint tokens.ts - 按功能分组整理 editor-core 的 65 行导出 - 添加清晰的分组注释 (中英双语) - 为 blueprint 添加占位符 tokens.ts * chore(editor): 为 14 个编辑器插件包添加 module.json 统一编辑器包的模块配置,包含: - isEditorPlugin 标识 - runtimeModule 关联 - exports 导出清单 (inspectors, panels, gizmos) * refactor(core): 改进类型安全 - 减少 as any 使用 - 添加 GlobalTypes.ts 定义小游戏平台和 Chrome API 类型 - SoAStorage 使用 IComponentTypeMetadata 替代 as any - PlatformDetector 使用类型安全的平台检测 - 添加 ISoAStorageStats/ISoAFieldStats 接口 * feat(editor): 添加 EditorServicesContext 解决 prop drilling - 新增 contexts/EditorServicesContext.tsx 提供统一服务访问 - App.tsx 包裹 EditorServicesProvider - 提供 useEditorServices/useMessageHub 等便捷 hooks - SceneHierarchy 添加迁移注释,后续可移除 props * docs(editor): 澄清 inspector 目录架构关系 - inspector/ 标记为内部实现,添加 @deprecated 警告 - inspectors/ 标记为公共 API 入口点 - 添加架构说明文档 * refactor(editor): 添加全局类型声明消除 window as any - 创建 editor-app/src/global.d.ts 声明 Window 接口扩展 - 创建 editor-core/src/global.d.ts 声明 Window 接口扩展 - 更新 App.tsx 使用类型安全的 window 属性访问 - 更新 PluginLoader.ts 使用 window.__ESENGINE_PLUGINS__ - 更新 PluginSDKRegistry.ts 使用 window.__ESENGINE_SDK__ - 更新 UserCodeService.ts 使用类型安全的全局变量访问 * refactor(editor): 提取项目和场景操作到独立 hooks - 创建 useProjectActions hook 封装项目操作 - 创建 useSceneActions hook 封装场景操作 - 为渐进式重构 App.tsx 做准备 * refactor(editor): 清理冗余代码和未使用文件 删除的目录和文件: - application/state/ - 重复的状态管理(与 stores/ 重复) - 8 个孤立 CSS 文件(对应组件不存在) - AssetBrowser.tsx - 仅为 ContentBrowser 的向后兼容包装 - AssetPicker.tsx - 未被使用 - AssetPickerDialog.tsx (顶级) - 已被 dialogs/ 版本取代 - EntityInspector.tsx (顶级) - 已被 inspectors/views/ 版本取代 修复: - 移除 App.tsx 中未使用的导入 - 更新 application/index.ts 移除已删除模块 - 修复 useProjectActions.ts 的 MutableRefObject 类型 * refactor(editor): 统一 inspectors 模块导出结构 - 在 inspectors/index.ts 重新导出 PropertyInspector - 创建 inspectors/fields/index.ts barrel export - 导出 views、fields、common 子模块 - 更新 EntityInspector 使用统一入口导入 * refactor(editor): 删除废弃的 Profiler 组件 删除未使用的组件(共 1059 行): - ProfilerPanel.tsx (229 行) - ProfilerWindow.tsx (589 行) - ProfilerDockPanel.tsx (241 行) - ProfilerPanel.css - ProfilerDockPanel.css 保留:AdvancedProfiler + AdvancedProfilerWindow(正在使用) * refactor(runtime-core): 统一依赖处理与插件状态管理 - 新增 DependencyUtils 统一拓扑排序和依赖验证 - 新增 PluginState 定义插件生命周期状态机 - 合并 UnifiedPluginLoader 到 PluginLoader - 清理 index.ts 移除不必要的 Token re-exports - 新增 RuntimeMode/UserCodeRealm/ImportMapGenerator * refactor(editor-core): 使用统一的 ImportMapGenerator - WebBuildPipeline 使用 runtime-core 的 generateImportMap - UserCodeService 添加 ImportMap 相关接口 * feat(compiler): 增强 esbuild 查找策略 - 支持本地 node_modules、pnpm exec、npx、全局多种来源 - EngineService 使用 RuntimeMode * refactor(runtime-core): 简化 GameRuntime 代码 - 合并 _disableGameLogicSystems/_enableGameLogicSystems 为 _setGameLogicSystemsEnabled - 精简本地 Token 定义的注释 * refactor(editor-core): 引入 BaseRegistry 基类消除代码重复 - 新增 BaseRegistry 和 PrioritizedRegistry 基类 - 重构 CompilerRegistry, InspectorRegistry, FieldEditorRegistry - 统一注册表的日志记录和错误处理 * refactor(editor-core): 扩展 BaseRegistry 重构 - ComponentInspectorRegistry 继承 PrioritizedRegistry - EditorComponentRegistry 继承 BaseRegistry - EntityCreationRegistry 继承 BaseRegistry - PropertyRendererRegistry 继承 PrioritizedRegistry - 导出 BaseRegistry 基类供外部使用 - 统一双语注释格式 * refactor(editor-core): 代码优雅性优化 CommandManager: - 提取 tryMergeWithLast() 和 pushToUndoStack() 消除重复代码 - 统一双语注释格式 FileActionRegistry: - 提取 normalizeExtension() 消除扩展名规范化重复 - 统一私有属性命名风格(_前缀) - 使用 createRegistryToken 统一 Token 创建 BaseRegistry: - 添加 IOrdered 接口 - 添加 sortByOrder() 排序辅助方法 EntityCreationRegistry: - 使用 sortByOrder() 简化排序逻辑 * refactor(editor-core): 统一日志系统 & 代码规范优化 - GizmoRegistry: 使用 createLogger 替代 console.warn - VirtualNodeRegistry: 使用 createLogger 替代 console.warn - WindowRegistry: 使用 logger、添加 _ 前缀、导出 IWindowRegistry token - EditorViewportService: 使用 createLogger 替代 console.warn - ComponentActionRegistry: 使用 logger、添加 _ 前缀、返回值改进 - SettingsRegistry: 使用 logger、提取 ensureCategory/ensureSection 方法 - 添加 WindowRegistry 到主导出 * refactor(editor-core): ModuleRegistry 使用 logger 替代 console * refactor(editor-core): SerializerRegistry/UIRegistry 添加 token 和 _ 前缀 * refactor(editor-core): UIRegistry 代码优雅性 & Token 命名统一 - UIRegistry: 提取 _sortByOrder 消除 6 处重复排序逻辑 - UIRegistry: 添加分节注释和双语文档 - FieldEditorRegistry: Token 重命名为 FieldEditorRegistryToken - PropertyRendererRegistry: Token 重命名为 PropertyRendererRegistryToken * refactor(core): 统一日志系统 - console 替换为 logger - ComponentSerializer: 使用 logger 替代 console.warn - ComponentRegistry: console.warn → logger.warn (已有 logger) - SceneSerializer: 添加 logger,替换 console.warn/error - SystemScheduler: 添加 logger,替换 console.warn - VersionMigration: 添加 logger,替换所有 console.warn - RuntimeModeService: console.error → logger.error - Core.ts: _logger 改为 readonly,双语错误消息 - SceneSerializer 修复:使用 getComponentTypeName 替代 constructor.name * fix(core): 修复 constructor.name 压缩后失效问题 - Scene.ts: 使用 system.systemName 替代 system.constructor.name - CommandBuffer.ts: 使用 getComponentTypeName() 替代 constructor.name * refactor(editor-core): 代码规范优化 - 私有方法命名 & 日志统一 - BuildService: console → logger - FileActionRegistry: 添加 logger, 私有方法 _ 前缀 - SettingsRegistry: 私有方法 _ 前缀 (ensureCategory → _ensureCategory) * refactor(core): Scene.ts 私有变量命名规范化 - logger → _logger (遵循私有变量 _ 前缀规范) * refactor(editor-core): 服务类私有成员命名规范化 - CommandManager: 私有变量/方法添加 _ 前缀 - undoStack/redoStack/config/isExecuting - tryMergeWithLast/pushToUndoStack - LocaleService: 私有变量/方法添加 _ 前缀 - currentLocale/translations/changeListeners - deepMerge/getNestedValue/loadSavedLocale/saveLocale * refactor(core): 私有成员命名规范化 & 单例模式优化 - Component.ts: _idGenerator 私有静态变量规范化 - PlatformManager.ts: _instance, _adapter, _logger 规范化 - AutoProfiler.ts: _instance, _config 及所有私有方法规范化 - ProfilerSDK.ts: _instance, _config 及所有私有方法规范化 - ComponentPoolManager: _instance, _pools, _usageTracker 规范化 - GlobalEventBus: _instance 规范化 - 添加中英双语 JSDoc 注释 * refactor(editor-app,behavior-tree-editor): 私有成员 & 单例模式命名规范化 editor-app: - EngineService: private static instance → _instance - EditorEngineSync: 所有私有成员添加 _ 前缀 - RuntimeResolver: 所有私有成员和方法添加 _ 前缀 - SettingsService: 所有私有成员和方法添加 _ 前缀 behavior-tree-editor: - GlobalBlackboardService: 所有私有成员和方法添加 _ 前缀 - NotificationService: private static instance → _instance - NodeRegistryService: 所有私有成员和方法添加 _ 前缀 - TreeStateAdapter: private static instance → _instance * fix(editor-runtime): 添加 editor-core 到 external 避免传递依赖问题 将 @esengine/editor-core 添加到 vite external 配置, 避免 editor-core → runtime-core → ecs-engine-bindgen 的传递依赖 被错误地打包进 editor-runtime.js,导致 CI 构建失败。 * fix(core): 修复空接口 lint 错误 将 IByteDanceMiniGameAPI、IAlipayMiniGameAPI、IBaiduMiniGameAPI 从空接口改为类型别名,修复 no-empty-object-type 规则报错
2025-12-24 20:57:08 +08:00
/**
* @zh
* @en User Code Realm
*
* @zh
* @en Provides isolation between user code (components, systems, services) and engine core.
*
* @zh | Design goals:
* @en
* 1. - /
* 2. -
* 3. - 使 ServiceToken 访
* 4. - /
*/
import type { Component, EntitySystem, IScene } from '@esengine/ecs-framework';
import {
ComponentRegistry,
GlobalComponentRegistry,
PluginServiceRegistry,
createServiceToken,
type ServiceToken
} from '@esengine/ecs-framework';
import { createLogger } from '@esengine/ecs-framework';
const logger = createLogger('UserCodeRealm');
/**
* @zh
* @en User Code Realm configuration
*/
export interface UserCodeRealmConfig {
/**
* @zh
* @en Whether to enable hot reload mode
* @default true
*/
hotReloadEnabled?: boolean;
/**
* @zh
* @en Whether to inherit components from global registry on initialization
* @default true
*/
inheritGlobalComponents?: boolean;
}
/**
* @zh
* @en Registered user system info
*/
export interface UserSystemInfo {
/**
* @zh
* @en System name
*/
name: string;
/**
* @zh
* @en System class
*/
systemClass: new (...args: unknown[]) => EntitySystem;
/**
* @zh
* @en System instance
*/
instance: EntitySystem;
/**
* @zh
* @en Scene the system belongs to
*/
scene: IScene;
/**
* @zh
* @en Update order
*/
updateOrder: number;
}
/**
* @zh
* @en Registered user component info
*/
export interface UserComponentInfo {
/**
* @zh
* @en Component name
*/
name: string;
/**
* @zh
* @en Component class
*/
componentClass: new (...args: unknown[]) => Component;
/**
* @zh
* @en Allocated bit index
*/
bitIndex: number;
}
/**
* @zh
* @en User Code Realm
*
* @zh
* @en Manages user-defined components, systems, and services with isolation from engine core.
*
* @example
* ```typescript
* const realm = new UserCodeRealm();
*
* // 注册用户组件 | Register user component
* realm.registerComponent(MyComponent);
*
* // 创建用户系统实例 | Create user system instance
* const system = realm.createSystem(MySystem, scene);
*
* // 注册用户服务 | Register user service
* realm.registerService(MyServiceToken, myServiceInstance);
*
* // 清理所有用户代码 | Clean up all user code
* realm.dispose();
* ```
*/
export class UserCodeRealm {
/**
* @zh
* @en User component registry
*/
private _componentRegistry: ComponentRegistry;
/**
* @zh
* @en User service registry
*/
private _serviceRegistry: PluginServiceRegistry;
/**
* @zh
* @en Registered user systems
*/
private _systems: UserSystemInfo[] = [];
/**
* @zh
* @en Registered user component info
*/
private _components: Map<string, UserComponentInfo> = new Map();
/**
* @zh
* @en Configuration
*/
private _config: Required<UserCodeRealmConfig>;
/**
* @zh
* @en Whether disposed
*/
private _disposed = false;
/**
* @zh
* @en Create user code realm
*
* @param config - @zh @en Configuration options
*/
constructor(config?: UserCodeRealmConfig) {
this._config = {
hotReloadEnabled: config?.hotReloadEnabled ?? true,
inheritGlobalComponents: config?.inheritGlobalComponents ?? true
};
this._componentRegistry = new ComponentRegistry();
this._serviceRegistry = new PluginServiceRegistry();
if (this._config.hotReloadEnabled) {
this._componentRegistry.enableHotReload();
}
if (this._config.inheritGlobalComponents) {
this._componentRegistry.cloneFrom(GlobalComponentRegistry);
}
logger.debug('UserCodeRealm created', {
hotReloadEnabled: this._config.hotReloadEnabled,
inheritGlobalComponents: this._config.inheritGlobalComponents
});
}
// ============================================================================
// 组件管理 | Component Management
// ============================================================================
/**
* @zh
* @en Register user component class
*
* @param componentClass - @zh @en Component class
* @returns @zh @en Allocated bit index
*/
registerComponent<T extends Component>(
componentClass: new (...args: unknown[]) => T
): number {
this._ensureNotDisposed();
const name = componentClass.name;
const bitIndex = this._componentRegistry.register(componentClass as any);
// 同时注册到全局注册表(用于序列化/反序列化)
// Also register to global registry (for serialization/deserialization)
try {
GlobalComponentRegistry.register(componentClass as any);
} catch {
// 已注册则忽略 | Ignore if already registered
}
this._components.set(name, {
name,
componentClass,
bitIndex
});
logger.debug(`Component registered: ${name}`, { bitIndex });
return bitIndex;
}
/**
* @zh
* @en Unregister user component
*
* @param componentName - @zh @en Component name
*/
unregisterComponent(componentName: string): void {
this._ensureNotDisposed();
this._componentRegistry.unregister(componentName);
this._components.delete(componentName);
logger.debug(`Component unregistered: ${componentName}`);
}
/**
* @zh
* @en Get user component class
*
* @param componentName - @zh @en Component name
* @returns @zh undefined @en Component class or undefined
*/
getComponent(componentName: string): UserComponentInfo | undefined {
return this._components.get(componentName);
}
/**
* @zh
* @en Get all registered user components
*/
getAllComponents(): UserComponentInfo[] {
return Array.from(this._components.values());
}
/**
* @zh
* @en Get user component registry
*/
get componentRegistry(): ComponentRegistry {
return this._componentRegistry;
}
// ============================================================================
// 系统管理 | System Management
// ============================================================================
/**
* @zh
* @en Create and register user system
*
* @param systemClass - @zh @en System class
* @param scene - @zh @en Target scene
* @param updateOrder - @zh @en Update order
* @returns @zh @en Created system instance
*/
createSystem<T extends EntitySystem>(
systemClass: new (...args: unknown[]) => T,
scene: IScene,
updateOrder = 0
): T {
this._ensureNotDisposed();
const instance = new systemClass();
const name = systemClass.name;
// 设置系统属性 | Set system properties
if ('updateOrder' in instance) {
(instance as any).updateOrder = updateOrder;
}
// 添加到场景 | Add to scene
scene.addSystem(instance);
// 记录系统信息 | Record system info
this._systems.push({
name,
systemClass,
instance,
scene,
updateOrder
});
logger.debug(`System created: ${name}`, { updateOrder });
return instance;
}
/**
* @zh
* @en Remove user system
*
* @param system - @zh @en System instance
*/
removeSystem(system: EntitySystem): void {
this._ensureNotDisposed();
const index = this._systems.findIndex(s => s.instance === system);
if (index !== -1) {
const info = this._systems[index];
// 从场景移除 | Remove from scene
try {
info.scene.removeSystem(system);
} catch (err) {
logger.warn(`Failed to remove system from scene: ${info.name}`, err);
}
this._systems.splice(index, 1);
logger.debug(`System removed: ${info.name}`);
}
}
/**
* @zh
* @en Remove all user systems from a scene
*
* @param scene - @zh @en Target scene
*/
removeSystemsFromScene(scene: IScene): void {
this._ensureNotDisposed();
const toRemove = this._systems.filter(s => s.scene === scene);
for (const info of toRemove) {
try {
scene.removeSystem(info.instance);
} catch (err) {
logger.warn(`Failed to remove system from scene: ${info.name}`, err);
}
}
this._systems = this._systems.filter(s => s.scene !== scene);
logger.debug(`Removed ${toRemove.length} systems from scene`);
}
/**
* @zh
* @en Get all user systems
*/
getAllSystems(): UserSystemInfo[] {
return [...this._systems];
}
/**
* @zh
* @en Get user systems of a scene
*
* @param scene - @zh @en Target scene
*/
getSystemsForScene(scene: IScene): UserSystemInfo[] {
return this._systems.filter(s => s.scene === scene);
}
// ============================================================================
// 服务管理 | Service Management
// ============================================================================
/**
* @zh
* @en Register user service
*
* @param token - @zh @en Service token
* @param service - @zh @en Service instance
*/
registerService<T>(token: ServiceToken<T>, service: T): void {
this._ensureNotDisposed();
this._serviceRegistry.register(token, service);
logger.debug(`Service registered: ${token.name}`);
}
/**
* @zh
* @en Get user service
*
* @param token - @zh @en Service token
* @returns @zh undefined @en Service instance or undefined
*/
getService<T>(token: ServiceToken<T>): T | undefined {
return this._serviceRegistry.get(token);
}
/**
* @zh
* @en Get user service (required)
*
* @param token - @zh @en Service token
* @throws @zh @en If service not registered
*/
requireService<T>(token: ServiceToken<T>): T {
return this._serviceRegistry.require(token);
}
/**
* @zh
* @en Check if service is registered
*
* @param token - @zh @en Service token
*/
hasService<T>(token: ServiceToken<T>): boolean {
return this._serviceRegistry.has(token);
}
/**
* @zh
* @en Unregister user service
*
* @param token - @zh @en Service token
*/
unregisterService<T>(token: ServiceToken<T>): boolean {
const result = this._serviceRegistry.unregister(token);
if (result) {
logger.debug(`Service unregistered: ${token.name}`);
}
return result;
}
/**
* @zh
* @en Get user service registry
*/
get serviceRegistry(): PluginServiceRegistry {
return this._serviceRegistry;
}
// ============================================================================
// 热更新 | Hot Reload
// ============================================================================
/**
* @zh
* @en Hot reload component class
*
* @zh
* @en Update registered component class definition while keeping bit index unchanged.
*
* @param componentClass - @zh @en New component class
* @returns @zh @en Whether update succeeded
*/
hotReloadComponent<T extends Component>(
componentClass: new (...args: unknown[]) => T
): boolean {
this._ensureNotDisposed();
if (!this._config.hotReloadEnabled) {
logger.warn('Hot reload is disabled');
return false;
}
const name = componentClass.name;
const existing = this._components.get(name);
if (!existing) {
// 新组件,直接注册 | New component, register directly
this.registerComponent(componentClass);
return true;
}
// 复用位索引,更新类引用 | Reuse bit index, update class reference
const bitIndex = this._componentRegistry.register(componentClass as any);
this._components.set(name, {
name,
componentClass,
bitIndex
});
// 更新全局注册表 | Update global registry
try {
GlobalComponentRegistry.register(componentClass as any);
} catch {
// 忽略 | Ignore
}
logger.debug(`Component hot reloaded: ${name}`, { bitIndex });
return true;
}
/**
* @zh
* @en Hot reload systems
*
* @zh
* @en Remove old system instances and create new ones.
*
* @param systemClasses - @zh @en New system class list
* @param scene - @zh @en Target scene
* @returns @zh @en Newly created system instances
*/
hotReloadSystems<T extends EntitySystem>(
systemClasses: Array<new (...args: unknown[]) => T>,
scene: IScene
): T[] {
this._ensureNotDisposed();
// 移除场景的旧系统 | Remove old systems from scene
this.removeSystemsFromScene(scene);
// 创建新系统 | Create new systems
const newSystems: T[] = [];
for (const systemClass of systemClasses) {
const metadata = (systemClass as any).__systemMetadata__;
const updateOrder = metadata?.updateOrder ?? 0;
const system = this.createSystem(systemClass, scene, updateOrder);
newSystems.push(system);
}
logger.info(`Hot reloaded ${newSystems.length} systems`);
return newSystems;
}
// ============================================================================
// 生命周期 | Lifecycle
// ============================================================================
/**
* @zh
* @en Reset the realm
*
* @zh
* @en Clear all user components, systems, and services without disposing the realm.
*/
reset(): void {
this._ensureNotDisposed();
// 移除所有系统 | Remove all systems
for (const info of this._systems) {
try {
info.scene.removeSystem(info.instance);
} catch {
// 忽略错误 | Ignore errors
}
}
this._systems = [];
// 清除组件记录(不重置注册表,保持引擎组件)
// Clear component records (don't reset registry, keep engine components)
this._components.clear();
// 清除服务 | Clear services
this._serviceRegistry.clear();
logger.info('UserCodeRealm reset');
}
/**
* @zh
* @en Dispose the realm
*
* @zh 使
* @en Completely clean up all resources. Realm cannot be used after disposal.
*/
dispose(): void {
if (this._disposed) {
return;
}
// 移除所有系统 | Remove all systems
for (const info of this._systems) {
try {
info.scene.removeSystem(info.instance);
} catch {
// 忽略错误 | Ignore errors
}
}
this._systems = [];
// 清除组件 | Clear components
this._components.clear();
this._componentRegistry.reset();
// 清除服务 | Clear services
this._serviceRegistry.dispose();
this._disposed = true;
logger.info('UserCodeRealm disposed');
}
/**
* @zh
* @en Check if disposed
*/
get isDisposed(): boolean {
return this._disposed;
}
// ============================================================================
// 私有方法 | Private Methods
// ============================================================================
/**
* @zh
* @en Ensure not disposed
*/
private _ensureNotDisposed(): void {
if (this._disposed) {
throw new Error('UserCodeRealm has been disposed');
}
}
}
/**
* @zh
* @en User Code Realm service token
*/
export const UserCodeRealmToken = createServiceToken<UserCodeRealm>('userCodeRealm');