Files
esengine/packages/editor-core/src/Services/BaseRegistry.ts
YHH dbc6793dc4 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

302 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @zh 通用注册表基类
* @en Generic Registry Base Class
*
* @zh 提供注册表的通用实现,消除 16+ 个 Registry 类中的重复代码。
* @en Provides common registry implementation, eliminating duplicate code in 16+ Registry classes.
*
* @example
* ```typescript
* interface IMyItem { id: string; name: string; }
*
* class MyRegistry extends BaseRegistry<IMyItem> {
* constructor() {
* super('MyRegistry');
* }
*
* protected getItemId(item: IMyItem): string {
* return item.id;
* }
* }
* ```
*/
import { IService, createLogger, type ILogger } from '@esengine/ecs-framework';
/**
* @zh 可注册项的基础接口
* @en Base interface for registrable items
*/
export interface IRegistrable {
/** @zh 唯一标识符 @en Unique identifier */
readonly id?: string;
}
/**
* @zh 带优先级的可注册项
* @en Registrable item with priority
*/
export interface IPrioritized {
/** @zh 优先级(越高越先匹配) @en Priority (higher = matched first) */
readonly priority?: number;
}
/**
* @zh 带顺序的可注册项
* @en Registrable item with order
*/
export interface IOrdered {
/** @zh 排序权重(越小越靠前) @en Sort order (lower = first) */
readonly order?: number;
}
/**
* @zh 注册表配置
* @en Registry configuration
*/
export interface RegistryOptions {
/** @zh 是否允许覆盖已存在的项 @en Allow overwriting existing items */
allowOverwrite?: boolean;
/** @zh 是否在覆盖时发出警告 @en Warn when overwriting */
warnOnOverwrite?: boolean;
}
/**
* @zh 通用注册表基类
* @en Generic Registry Base Class
*
* @typeParam T - @zh 注册项类型 @en Registered item type
* @typeParam K - @zh 键类型(默认 string @en Key type (default string)
*/
export abstract class BaseRegistry<T, K extends string = string> implements IService {
protected readonly _items: Map<K, T> = new Map();
protected readonly _logger: ILogger;
protected readonly _options: Required<RegistryOptions>;
constructor(
protected readonly _name: string,
options: RegistryOptions = {}
) {
this._logger = createLogger(_name);
this._options = {
allowOverwrite: options.allowOverwrite ?? true,
warnOnOverwrite: options.warnOnOverwrite ?? true
};
}
/**
* @zh 获取项的键
* @en Get item key
*/
protected abstract getItemKey(item: T): K;
/**
* @zh 获取项的显示名称(用于日志)
* @en Get item display name (for logging)
*/
protected getItemDisplayName(item: T): string {
const key = this.getItemKey(item);
return String(key);
}
// ========== 核心 CRUD 操作 | Core CRUD Operations ==========
/**
* @zh 注册项
* @en Register item
*/
register(item: T): boolean {
const key = this.getItemKey(item);
const displayName = this.getItemDisplayName(item);
if (this._items.has(key)) {
if (this._options.warnOnOverwrite) {
this._logger.warn(`Overwriting: ${displayName}`);
}
if (!this._options.allowOverwrite) {
return false;
}
}
this._items.set(key, item);
this._logger.debug(`Registered: ${displayName}`);
return true;
}
/**
* @zh 批量注册
* @en Register multiple items
*/
registerMany(items: T[]): number {
let count = 0;
for (const item of items) {
if (this.register(item)) count++;
}
return count;
}
/**
* @zh 注销项
* @en Unregister item
*/
unregister(key: K): boolean {
if (this._items.delete(key)) {
this._logger.debug(`Unregistered: ${key}`);
return true;
}
return false;
}
/**
* @zh 获取项
* @en Get item
*/
get(key: K): T | undefined {
return this._items.get(key);
}
/**
* @zh 检查是否存在
* @en Check if exists
*/
has(key: K): boolean {
return this._items.has(key);
}
// ========== 查询操作 | Query Operations ==========
/**
* @zh 获取所有项
* @en Get all items
*/
getAll(): T[] {
return Array.from(this._items.values());
}
/**
* @zh 获取所有键
* @en Get all keys
*/
getAllKeys(): K[] {
return Array.from(this._items.keys());
}
/**
* @zh 获取项数量
* @en Get item count
*/
get size(): number {
return this._items.size;
}
/**
* @zh 是否为空
* @en Is empty
*/
get isEmpty(): boolean {
return this._items.size === 0;
}
/**
* @zh 按条件过滤
* @en Filter by predicate
*/
filter(predicate: (item: T, key: K) => boolean): T[] {
const result: T[] = [];
for (const [key, item] of this._items) {
if (predicate(item, key)) {
result.push(item);
}
}
return result;
}
/**
* @zh 查找第一个匹配项
* @en Find first matching item
*/
find(predicate: (item: T, key: K) => boolean): T | undefined {
for (const [key, item] of this._items) {
if (predicate(item, key)) {
return item;
}
}
return undefined;
}
// ========== 排序操作 | Sorting Operations ==========
/**
* @zh 按 order 字段排序(越小越靠前)
* @en Sort items by order field (lower = first)
*/
protected sortByOrder<U extends IOrdered>(items: U[], defaultOrder = 0): U[] {
return items.sort((a, b) => (a.order ?? defaultOrder) - (b.order ?? defaultOrder));
}
// ========== 生命周期 | Lifecycle ==========
/**
* @zh 清空注册表
* @en Clear registry
*/
clear(): void {
this._items.clear();
this._logger.debug('Cleared');
}
/**
* @zh 释放资源
* @en Dispose resources
*/
dispose(): void {
this.clear();
this._logger.debug('Disposed');
}
}
/**
* @zh 带优先级查找的注册表基类
* @en Registry base class with priority-based lookup
*
* @zh 适用于需要按优先级匹配的场景(如 Inspector、FieldEditor
* @en For scenarios requiring priority-based matching (e.g., Inspector, FieldEditor)
*/
export abstract class PrioritizedRegistry<T extends IPrioritized, K extends string = string>
extends BaseRegistry<T, K> {
/**
* @zh 按优先级排序获取所有项
* @en Get all items sorted by priority
*/
getAllSorted(): T[] {
return this.getAll().sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
}
/**
* @zh 查找第一个能处理目标的项
* @en Find first item that can handle the target
*
* @param canHandle - @zh 判断函数 @en Predicate function
*/
findByPriority(canHandle: (item: T) => boolean): T | undefined {
for (const item of this.getAllSorted()) {
if (canHandle(item)) {
return item;
}
}
return undefined;
}
}
/**
* @zh 创建注册表服务标识符
* @en Create registry service identifier
*
* @zh 使用 Symbol.for 确保跨包共享同一个 Symbol
* @en Uses Symbol.for to ensure same Symbol is shared across packages
*/
export function createRegistryToken<T>(name: string): symbol {
return Symbol.for(`IRegistry:${name}`);
}