Feature/tilemap editor (#237)
* feat: 添加 Tilemap 编辑器插件和组件生命周期支持 * feat(editor-core): 添加声明式插件注册 API * feat(editor-core): 改进tiledmap结构合并tileset进tiledmapeditor * feat: 添加 editor-runtime SDK 和插件系统改进 * fix(ci): 修复SceneResourceManager里变量未使用问题
This commit is contained in:
5550
packages/core/pnpm-lock.yaml
generated
Normal file
5550
packages/core/pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,13 @@ export interface IService {
|
||||
*/
|
||||
export type ServiceType<T extends IService> = new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* 服务标识符
|
||||
*
|
||||
* 支持类构造函数或 Symbol 作为服务标识符
|
||||
*/
|
||||
export type ServiceIdentifier<T extends IService = IService> = ServiceType<T> | symbol;
|
||||
|
||||
/**
|
||||
* 服务生命周期
|
||||
*/
|
||||
@@ -43,9 +50,14 @@ export enum ServiceLifetime {
|
||||
*/
|
||||
interface ServiceRegistration<T extends IService> {
|
||||
/**
|
||||
* 服务类型
|
||||
* 服务标识符
|
||||
*/
|
||||
type: ServiceType<T>;
|
||||
identifier: ServiceIdentifier<T>;
|
||||
|
||||
/**
|
||||
* 服务类型(用于构造实例)
|
||||
*/
|
||||
type?: ServiceType<T>;
|
||||
|
||||
/**
|
||||
* 服务实例(单例模式)
|
||||
@@ -96,12 +108,12 @@ export class ServiceContainer {
|
||||
/**
|
||||
* 服务注册表
|
||||
*/
|
||||
private _services: Map<ServiceType<IService>, ServiceRegistration<IService>> = new Map();
|
||||
private _services: Map<ServiceIdentifier, ServiceRegistration<IService>> = new Map();
|
||||
|
||||
/**
|
||||
* 正在解析的服务栈(用于循环依赖检测)
|
||||
*/
|
||||
private _resolving: Set<ServiceType<IService>> = new Set();
|
||||
private _resolving: Set<ServiceIdentifier> = new Set();
|
||||
|
||||
/**
|
||||
* 可更新的服务列表
|
||||
@@ -132,12 +144,13 @@ export class ServiceContainer {
|
||||
type: ServiceType<T>,
|
||||
factory?: (container: ServiceContainer) => T
|
||||
): void {
|
||||
if (this._services.has(type as ServiceType<IService>)) {
|
||||
if (this._services.has(type as ServiceIdentifier)) {
|
||||
logger.warn(`Service ${type.name} is already registered`);
|
||||
return;
|
||||
}
|
||||
|
||||
this._services.set(type as ServiceType<IService>, {
|
||||
this._services.set(type as ServiceIdentifier, {
|
||||
identifier: type as ServiceIdentifier,
|
||||
type: type as ServiceType<IService>,
|
||||
...(factory && { factory: factory as (container: ServiceContainer) => IService }),
|
||||
lifetime: ServiceLifetime.Singleton
|
||||
@@ -164,12 +177,13 @@ export class ServiceContainer {
|
||||
type: ServiceType<T>,
|
||||
factory?: (container: ServiceContainer) => T
|
||||
): void {
|
||||
if (this._services.has(type as ServiceType<IService>)) {
|
||||
if (this._services.has(type as ServiceIdentifier)) {
|
||||
logger.warn(`Service ${type.name} is already registered`);
|
||||
return;
|
||||
}
|
||||
|
||||
this._services.set(type as ServiceType<IService>, {
|
||||
this._services.set(type as ServiceIdentifier, {
|
||||
identifier: type as ServiceIdentifier,
|
||||
type: type as ServiceType<IService>,
|
||||
...(factory && { factory: factory as (container: ServiceContainer) => IService }),
|
||||
lifetime: ServiceLifetime.Transient
|
||||
@@ -183,65 +197,77 @@ export class ServiceContainer {
|
||||
*
|
||||
* 直接注册已创建的实例,自动视为单例。
|
||||
*
|
||||
* @param type - 服务类型(构造函数,仅用作标识)
|
||||
* @param identifier - 服务标识符(构造函数或 Symbol)
|
||||
* @param instance - 服务实例
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const config = new Config();
|
||||
* container.registerInstance(Config, config);
|
||||
*
|
||||
* // 使用 Symbol 作为标识符(适用于接口)
|
||||
* const IFileSystem = Symbol('IFileSystem');
|
||||
* container.registerInstance(IFileSystem, new TauriFileSystem());
|
||||
* ```
|
||||
*/
|
||||
public registerInstance<T extends IService>(type: ServiceType<T>, instance: T): void {
|
||||
if (this._services.has(type as ServiceType<IService>)) {
|
||||
logger.warn(`Service ${type.name} is already registered`);
|
||||
public registerInstance<T extends IService>(identifier: ServiceIdentifier<T>, instance: T): void {
|
||||
if (this._services.has(identifier)) {
|
||||
const name = typeof identifier === 'symbol' ? identifier.description : identifier.name;
|
||||
logger.warn(`Service ${name} is already registered`);
|
||||
return;
|
||||
}
|
||||
|
||||
this._services.set(type as ServiceType<IService>, {
|
||||
type: type as ServiceType<IService>,
|
||||
this._services.set(identifier, {
|
||||
identifier,
|
||||
instance: instance as IService,
|
||||
lifetime: ServiceLifetime.Singleton
|
||||
});
|
||||
|
||||
// 如果使用了@Updatable装饰器,添加到可更新列表
|
||||
if (checkUpdatable(type)) {
|
||||
const metadata = getUpdatableMetadata(type);
|
||||
if (typeof identifier !== 'symbol' && checkUpdatable(identifier)) {
|
||||
const metadata = getUpdatableMetadata(identifier);
|
||||
const priority = metadata?.priority ?? 0;
|
||||
this._updatableServices.push({ instance, priority });
|
||||
|
||||
// 按优先级排序(数值越小越先执行)
|
||||
this._updatableServices.sort((a, b) => a.priority - b.priority);
|
||||
|
||||
logger.debug(`Service ${type.name} is updatable (priority: ${priority}), added to update list`);
|
||||
logger.debug(`Service ${identifier.name} is updatable (priority: ${priority}), added to update list`);
|
||||
}
|
||||
|
||||
logger.debug(`Registered service instance: ${type.name}`);
|
||||
const name = typeof identifier === 'symbol' ? identifier.description : identifier.name;
|
||||
logger.debug(`Registered service instance: ${name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析服务
|
||||
*
|
||||
* @param type - 服务类型
|
||||
* @param identifier - 服务标识符(构造函数或 Symbol)
|
||||
* @returns 服务实例
|
||||
* @throws 如果服务未注册或存在循环依赖
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const timer = container.resolve(TimerManager);
|
||||
*
|
||||
* // 使用 Symbol
|
||||
* const fileSystem = container.resolve(IFileSystem);
|
||||
* ```
|
||||
*/
|
||||
public resolve<T extends IService>(type: ServiceType<T>): T {
|
||||
const registration = this._services.get(type as ServiceType<IService>);
|
||||
public resolve<T extends IService>(identifier: ServiceIdentifier<T>): T {
|
||||
const registration = this._services.get(identifier);
|
||||
const name = typeof identifier === 'symbol' ? identifier.description : identifier.name;
|
||||
|
||||
if (!registration) {
|
||||
throw new Error(`Service ${type.name} is not registered`);
|
||||
throw new Error(`Service ${name} is not registered`);
|
||||
}
|
||||
|
||||
// 检测循环依赖
|
||||
if (this._resolving.has(type as ServiceType<IService>)) {
|
||||
const chain = Array.from(this._resolving).map((t) => t.name).join(' -> ');
|
||||
throw new Error(`Circular dependency detected: ${chain} -> ${type.name}`);
|
||||
if (this._resolving.has(identifier)) {
|
||||
const chain = Array.from(this._resolving).map((t) =>
|
||||
typeof t === 'symbol' ? t.description : t.name
|
||||
).join(' -> ');
|
||||
throw new Error(`Circular dependency detected: ${chain} -> ${name}`);
|
||||
}
|
||||
|
||||
// 如果是单例且已经有实例,直接返回
|
||||
@@ -250,7 +276,7 @@ export class ServiceContainer {
|
||||
}
|
||||
|
||||
// 添加到解析栈
|
||||
this._resolving.add(type as ServiceType<IService>);
|
||||
this._resolving.add(identifier);
|
||||
|
||||
try {
|
||||
// 创建实例
|
||||
@@ -259,9 +285,11 @@ export class ServiceContainer {
|
||||
if (registration.factory) {
|
||||
// 使用工厂函数
|
||||
instance = registration.factory(this);
|
||||
} else {
|
||||
} else if (registration.type) {
|
||||
// 直接构造
|
||||
instance = new (registration.type)();
|
||||
} else {
|
||||
throw new Error(`Service ${name} has no factory or type to construct`);
|
||||
}
|
||||
|
||||
// 如果是单例,缓存实例
|
||||
@@ -269,7 +297,7 @@ export class ServiceContainer {
|
||||
registration.instance = instance;
|
||||
|
||||
// 如果使用了@Updatable装饰器,添加到可更新列表
|
||||
if (checkUpdatable(registration.type)) {
|
||||
if (registration.type && checkUpdatable(registration.type)) {
|
||||
const metadata = getUpdatableMetadata(registration.type);
|
||||
const priority = metadata?.priority ?? 0;
|
||||
this._updatableServices.push({ instance, priority });
|
||||
@@ -277,14 +305,14 @@ export class ServiceContainer {
|
||||
// 按优先级排序(数值越小越先执行)
|
||||
this._updatableServices.sort((a, b) => a.priority - b.priority);
|
||||
|
||||
logger.debug(`Service ${type.name} is updatable (priority: ${priority}), added to update list`);
|
||||
logger.debug(`Service ${name} is updatable (priority: ${priority}), added to update list`);
|
||||
}
|
||||
}
|
||||
|
||||
return instance as T;
|
||||
} finally {
|
||||
// 从解析栈移除
|
||||
this._resolving.delete(type as ServiceType<IService>);
|
||||
this._resolving.delete(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,7 +321,7 @@ export class ServiceContainer {
|
||||
*
|
||||
* 如果服务未注册,返回null而不是抛出异常。
|
||||
*
|
||||
* @param type - 服务类型
|
||||
* @param identifier - 服务标识符(构造函数或 Symbol)
|
||||
* @returns 服务实例或null
|
||||
*
|
||||
* @example
|
||||
@@ -304,9 +332,9 @@ export class ServiceContainer {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public tryResolve<T extends IService>(type: ServiceType<T>): T | null {
|
||||
public tryResolve<T extends IService>(identifier: ServiceIdentifier<T>): T | null {
|
||||
try {
|
||||
return this.resolve(type);
|
||||
return this.resolve(identifier);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -315,21 +343,21 @@ export class ServiceContainer {
|
||||
/**
|
||||
* 检查服务是否已注册
|
||||
*
|
||||
* @param type - 服务类型
|
||||
* @param identifier - 服务标识符(构造函数或 Symbol)
|
||||
* @returns 是否已注册
|
||||
*/
|
||||
public isRegistered<T extends IService>(type: ServiceType<T>): boolean {
|
||||
return this._services.has(type as ServiceType<IService>);
|
||||
public isRegistered<T extends IService>(identifier: ServiceIdentifier<T>): boolean {
|
||||
return this._services.has(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销服务
|
||||
*
|
||||
* @param type - 服务类型
|
||||
* @param identifier - 服务标识符(构造函数或 Symbol)
|
||||
* @returns 是否成功注销
|
||||
*/
|
||||
public unregister<T extends IService>(type: ServiceType<T>): boolean {
|
||||
const registration = this._services.get(type as ServiceType<IService>);
|
||||
public unregister<T extends IService>(identifier: ServiceIdentifier<T>): boolean {
|
||||
const registration = this._services.get(identifier);
|
||||
if (!registration) {
|
||||
return false;
|
||||
}
|
||||
@@ -345,8 +373,9 @@ export class ServiceContainer {
|
||||
registration.instance.dispose();
|
||||
}
|
||||
|
||||
this._services.delete(type as ServiceType<IService>);
|
||||
logger.debug(`Unregistered service: ${type.name}`);
|
||||
this._services.delete(identifier);
|
||||
const name = typeof identifier === 'symbol' ? identifier.description : identifier.name;
|
||||
logger.debug(`Unregistered service: ${name}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -367,11 +396,11 @@ export class ServiceContainer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已注册的服务类型
|
||||
* 获取所有已注册的服务标识符
|
||||
*
|
||||
* @returns 服务类型数组
|
||||
* @returns 服务标识符数组
|
||||
*/
|
||||
public getRegisteredServices(): ServiceType<IService>[] {
|
||||
public getRegisteredServices(): ServiceIdentifier[] {
|
||||
return Array.from(this._services.keys());
|
||||
}
|
||||
|
||||
|
||||
@@ -84,4 +84,31 @@ export abstract class Component implements IComponent {
|
||||
* 虽然保留此方法,但建议将复杂的清理逻辑放在 System 中处理。
|
||||
*/
|
||||
public onRemovedFromEntity(): void {}
|
||||
|
||||
/**
|
||||
* 组件反序列化后的回调
|
||||
*
|
||||
* 当组件从场景文件加载或快照恢复后调用,可以在此方法中恢复运行时数据。
|
||||
*
|
||||
* @remarks
|
||||
* 这是一个生命周期钩子,用于恢复无法序列化的运行时数据。
|
||||
* 例如:从图片路径重新加载图片尺寸信息,重建缓存等。
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class TilemapComponent extends Component {
|
||||
* public tilesetImage: string = '';
|
||||
* private _tilesetData: TilesetData | undefined;
|
||||
*
|
||||
* public async onDeserialized(): Promise<void> {
|
||||
* if (this.tilesetImage) {
|
||||
* // 重新加载 tileset 图片并恢复运行时数据
|
||||
* const img = await loadImage(this.tilesetImage);
|
||||
* this.setTilesetInfo(img.width, img.height, ...);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public onDeserialized(): void | Promise<void> {}
|
||||
}
|
||||
|
||||
@@ -281,6 +281,40 @@ export class SceneSerializer {
|
||||
if (serializedScene.sceneData) {
|
||||
this.deserializeSceneData(serializedScene.sceneData, scene.sceneData);
|
||||
}
|
||||
|
||||
// 调用所有组件的 onDeserialized 生命周期方法
|
||||
// Call onDeserialized lifecycle method on all components
|
||||
const deserializedPromises: Promise<void>[] = [];
|
||||
for (const entity of entities) {
|
||||
this.callOnDeserializedRecursively(entity, deserializedPromises);
|
||||
}
|
||||
|
||||
// 如果有异步的 onDeserialized,在后台执行
|
||||
if (deserializedPromises.length > 0) {
|
||||
Promise.all(deserializedPromises).catch(error => {
|
||||
console.error('Error in onDeserialized:', error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归调用实体及其子实体所有组件的 onDeserialized 方法
|
||||
*/
|
||||
private static callOnDeserializedRecursively(entity: Entity, promises: Promise<void>[]): void {
|
||||
for (const component of entity.components) {
|
||||
try {
|
||||
const result = component.onDeserialized();
|
||||
if (result instanceof Promise) {
|
||||
promises.push(result);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error calling onDeserialized on component ${component.constructor.name}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
for (const child of entity.children) {
|
||||
this.callOnDeserializedRecursively(child, promises);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,8 @@ export interface IComponent {
|
||||
onAddedToEntity(): void;
|
||||
/** 组件从实体移除时的回调 */
|
||||
onRemovedFromEntity(): void;
|
||||
/** 组件反序列化后的回调 */
|
||||
onDeserialized(): void | Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// 核心模块
|
||||
export { Core } from './Core';
|
||||
export { ServiceContainer, ServiceLifetime } from './Core/ServiceContainer';
|
||||
export type { IService, ServiceType } from './Core/ServiceContainer';
|
||||
export type { IService, ServiceType, ServiceIdentifier } from './Core/ServiceContainer';
|
||||
|
||||
// 插件系统
|
||||
export { PluginManager } from './Core/PluginManager';
|
||||
|
||||
Reference in New Issue
Block a user