refactor(plugin): 重构插件系统架构,统一类型导入路径 (#296)
* refactor(plugin): 重构插件系统架构,统一类型导入路径 ## 主要更改 ### 新增 @esengine/plugin-types 包 - 提供打破循环依赖的最小类型定义 - 包含 ServiceToken, createServiceToken, PluginServiceRegistry, IEditorModuleBase ### engine-core 类型统一 - IPlugin<T> 泛型参数改为 T = unknown - 所有运行时类型(IRuntimeModule, ModuleManifest, SystemContext)在此定义 - 新增 PluginServiceRegistry.ts 导出服务令牌相关类型 ### editor-core 类型优化 - 重命名 IPluginLoader.ts 为 EditorModule.ts - 新增 IEditorPlugin = IPlugin<IEditorModuleLoader> 类型别名 - 移除弃用别名 IPluginLoader, IRuntimeModuleLoader ### 编辑器插件更新 - 所有 9 个编辑器插件使用 IEditorPlugin 类型 - 统一从 editor-core 导入编辑器类型 ### 服务令牌规范 - 各模块在 tokens.ts 中定义自己的服务接口和令牌 - 遵循"谁定义接口,谁导出 Token"原则 ## 导入规范 | 场景 | 导入来源 | |------|---------| | 运行时模块 | @esengine/engine-core | | 编辑器插件 | @esengine/editor-core | | 服务令牌 | @esengine/engine-core | * refactor(plugin): 完善服务令牌规范,统一运行时模块 ## 更改内容 ### 运行时模块优化 - particle: 使用 PluginServiceRegistry 获取依赖服务 - physics-rapier2d: 通过服务令牌注册/获取物理查询接口 - tilemap: 使用服务令牌获取物理系统依赖 - sprite: 导出服务令牌 - ui: 导出服务令牌 - behavior-tree: 使用服务令牌系统 ### 资产系统增强 - IAssetManager 接口扩展 - 加载器使用服务令牌获取依赖 ### 运行时核心 - GameRuntime 使用 PluginServiceRegistry - 导出服务令牌相关类型 ### 编辑器服务 - EngineService 适配新的服务令牌系统 - AssetRegistryService 优化 * fix: 修复 editor-app 和 behavior-tree-editor 中的类型引用 - editor-app/PluginLoader.ts: 使用 IPlugin 替代 IPluginLoader - behavior-tree-editor: 使用 IEditorPlugin 替代 IPluginLoader * fix(ui): 添加缺失的 ecs-engine-bindgen 依赖 UIRuntimeModule 使用 EngineBridgeToken,需要声明对 ecs-engine-bindgen 的依赖 * fix(type): 解决 ServiceToken 跨包类型兼容性问题 - 在 engine-core 中直接定义 ServiceToken 和 PluginServiceRegistry 而不是从 plugin-types 重新导出,确保 tsup 生成的类型声明 以 engine-core 作为类型来源 - 移除 RuntimeResolver.ts 中的硬编码模块 ID 检查, 改用 module.json 中的 name 配置 - 修复 pnpm-lock.yaml 中的依赖记录 * refactor(arch): 改进架构设计,移除硬编码 - 统一类型导出:editor-core 从 engine-core 导入 IEditorModuleBase - RuntimeResolver: 将硬编码路径改为配置常量和搜索路径列表 - 添加跨平台安装路径支持(Windows/macOS/Linux) - 使用 ENGINE_WASM_CONFIG 配置引擎 WASM 文件信息 - IBundlePackOptions 添加 preloadBundles 配置项 * fix(particle): 添加缺失的 ecs-engine-bindgen 依赖 ParticleRuntimeModule 导入了 @esengine/ecs-engine-bindgen 的 tokens, 但 package.json 中未声明该依赖,导致 CI 构建失败。 * fix(physics-rapier2d): 移除不存在的 PhysicsSystemContext 导出 PhysicsRuntimeModule 中不存在该类型,导致 type-check 失败
This commit is contained in:
@@ -204,6 +204,12 @@ export interface IBundlePackOptions {
|
||||
groupByType?: boolean;
|
||||
/** Include asset names in bundle | 在包中包含资产名称 */
|
||||
includeNames?: boolean;
|
||||
/**
|
||||
* 需要预加载的包名列表 | List of bundle names to preload
|
||||
* 如果未指定,默认预加载 'core' 和 'main' 包
|
||||
* If not specified, defaults to preloading 'core' and 'main' bundles
|
||||
*/
|
||||
preloadBundles?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
* For editor-side functionality (meta files, packing), use @esengine/asset-system-editor
|
||||
*/
|
||||
|
||||
// Service tokens (谁定义接口,谁导出 Token)
|
||||
export { AssetManagerToken, type IAssetManager } from './tokens';
|
||||
|
||||
// Types
|
||||
export * from './types/AssetTypes';
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ import {
|
||||
IAssetLoadProgress,
|
||||
IAssetCatalog
|
||||
} from '../types/AssetTypes';
|
||||
import { IAssetLoader } from './IAssetLoader';
|
||||
import { IAssetLoader, IAssetLoaderFactory } from './IAssetLoader';
|
||||
import { IAssetReader } from './IAssetReader';
|
||||
import type { AssetDatabase } from '../core/AssetDatabase';
|
||||
|
||||
/**
|
||||
* Asset manager interface
|
||||
@@ -167,6 +168,24 @@ export interface IAssetManager {
|
||||
* 从目录加载资产元数据,用于运行时资产解析。
|
||||
*/
|
||||
initializeFromCatalog(catalog: IAssetCatalog): void;
|
||||
|
||||
/**
|
||||
* Get the asset database
|
||||
* 获取资产数据库
|
||||
*/
|
||||
getDatabase(): AssetDatabase;
|
||||
|
||||
/**
|
||||
* Get the loader factory
|
||||
* 获取加载器工厂
|
||||
*/
|
||||
getLoaderFactory(): IAssetLoaderFactory;
|
||||
|
||||
/**
|
||||
* Set project root path
|
||||
* 设置项目根路径
|
||||
*/
|
||||
setProjectRoot(path: string): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,7 +27,14 @@ export class AudioLoader implements IAssetLoader<IAudioAsset> {
|
||||
*/
|
||||
private static getAudioContext(): AudioContext {
|
||||
if (!AudioLoader._audioContext) {
|
||||
AudioLoader._audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();
|
||||
// 兼容旧版 Safari 的 webkitAudioContext
|
||||
// Support legacy Safari webkitAudioContext
|
||||
const AudioContextClass = window.AudioContext ||
|
||||
(window as Window & { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
|
||||
if (!AudioContextClass) {
|
||||
throw new Error('AudioContext is not supported in this browser');
|
||||
}
|
||||
AudioLoader._audioContext = new AudioContextClass();
|
||||
}
|
||||
return AudioLoader._audioContext;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ export class BinaryLoader implements IAssetLoader<IBinaryAsset> {
|
||||
* 释放已加载的资产
|
||||
*/
|
||||
dispose(asset: IBinaryAsset): void {
|
||||
(asset as any).data = null;
|
||||
// 释放二进制数据引用以允许垃圾回收
|
||||
// Release binary data reference to allow garbage collection
|
||||
(asset as { data: ArrayBuffer | null }).data = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ export class JsonLoader implements IAssetLoader<IJsonAsset> {
|
||||
* 释放已加载的资产
|
||||
*/
|
||||
dispose(asset: IJsonAsset): void {
|
||||
(asset as any).data = null;
|
||||
// 清空 JSON 数据 | Clear JSON data
|
||||
asset.data = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ export class TextLoader implements IAssetLoader<ITextAsset> {
|
||||
* 释放已加载的资产
|
||||
*/
|
||||
dispose(asset: ITextAsset): void {
|
||||
(asset as any).content = '';
|
||||
// 清空文本内容 | Clear text content
|
||||
asset.content = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,26 @@ import { AssetType } from '../types/AssetTypes';
|
||||
import { IAssetLoader, ITextureAsset, IAssetParseContext } from '../interfaces/IAssetLoader';
|
||||
import { IAssetContent, AssetContentType } from '../interfaces/IAssetReader';
|
||||
|
||||
/**
|
||||
* 全局引擎桥接接口(运行时挂载到 window)
|
||||
* Global engine bridge interface (mounted to window at runtime)
|
||||
*/
|
||||
interface IEngineBridgeGlobal {
|
||||
loadTexture?(textureId: number, path: string): Promise<void>;
|
||||
unloadTexture?(textureId: number): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局引擎桥接
|
||||
* Get global engine bridge
|
||||
*/
|
||||
function getEngineBridge(): IEngineBridgeGlobal | undefined {
|
||||
if (typeof window !== 'undefined' && 'engineBridge' in window) {
|
||||
return (window as Window & { engineBridge?: IEngineBridgeGlobal }).engineBridge;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Texture loader implementation
|
||||
* 纹理加载器实现
|
||||
@@ -39,35 +59,23 @@ export class TextureLoader implements IAssetLoader<ITextureAsset> {
|
||||
};
|
||||
|
||||
// Upload to GPU if bridge exists.
|
||||
if (typeof window !== 'undefined' && (window as any).engineBridge) {
|
||||
await this.uploadToGPU(textureAsset, context.metadata.path);
|
||||
const bridge = getEngineBridge();
|
||||
if (bridge?.loadTexture) {
|
||||
await bridge.loadTexture(textureAsset.textureId, context.metadata.path);
|
||||
}
|
||||
|
||||
return textureAsset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload texture to GPU
|
||||
* 上传纹理到GPU
|
||||
*/
|
||||
private async uploadToGPU(textureAsset: ITextureAsset, path: string): Promise<void> {
|
||||
const bridge = (window as any).engineBridge;
|
||||
if (bridge && bridge.loadTexture) {
|
||||
await bridge.loadTexture(textureAsset.textureId, path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose loaded asset
|
||||
* 释放已加载的资产
|
||||
*/
|
||||
dispose(asset: ITextureAsset): void {
|
||||
// Release GPU resources.
|
||||
if (typeof window !== 'undefined' && (window as any).engineBridge) {
|
||||
const bridge = (window as any).engineBridge;
|
||||
if (bridge.unloadTexture) {
|
||||
bridge.unloadTexture(asset.textureId);
|
||||
}
|
||||
const bridge = getEngineBridge();
|
||||
if (bridge?.unloadTexture) {
|
||||
bridge.unloadTexture(asset.textureId);
|
||||
}
|
||||
|
||||
// Clean up image data.
|
||||
|
||||
32
packages/asset-system/src/tokens.ts
Normal file
32
packages/asset-system/src/tokens.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Asset System 服务令牌
|
||||
* Asset System service tokens
|
||||
*
|
||||
* 定义 asset-system 模块导出的服务令牌和接口。
|
||||
* Defines service tokens and interfaces exported by asset-system module.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 消费方导入 Token | Consumer imports Token
|
||||
* import { AssetManagerToken, type IAssetManager } from '@esengine/asset-system';
|
||||
*
|
||||
* // 获取服务 | Get service
|
||||
* const assetManager = context.services.get(AssetManagerToken);
|
||||
* ```
|
||||
*/
|
||||
|
||||
import { createServiceToken } from '@esengine/engine-core';
|
||||
import type { IAssetManager } from './interfaces/IAssetManager';
|
||||
|
||||
// 重新导出接口方便使用 | Re-export interface for convenience
|
||||
export type { IAssetManager } from './interfaces/IAssetManager';
|
||||
export type { IAssetLoadResult } from './types/AssetTypes';
|
||||
|
||||
/**
|
||||
* 资产管理器服务令牌
|
||||
* Asset manager service token
|
||||
*
|
||||
* 用于注册和获取资产管理器服务。
|
||||
* For registering and getting asset manager service.
|
||||
*/
|
||||
export const AssetManagerToken = createServiceToken<IAssetManager>('assetManager');
|
||||
Reference in New Issue
Block a user