import { inject, injectable, optional } from 'inversify'; import type { AbleRegistry, Entity, EntityDataRegistry, EntityJSON, EntityOpts, EntityRegistry } from './'; import { AbleManager } from './able-manager'; import { Disposable, DisposableCollection, Emitter } from '@gedit/utils'; import { ConfigEntity } from './config-entity'; import { PlaygroundContext } from './playground-context'; import { PlaygroundSchedule } from './playground-schedule'; /** * TODO registry改成decorator * Entity 管理器, 全局唯一 */ @injectable() export class EntityManager implements Disposable { readonly toDispose = new DisposableCollection(); protected onEntityChangedEmitter = new Emitter(); /** * Entity的类缓存,便于在fromJSON时候查询对应的类 */ protected registryMap: Map = new Map; /** * Entity数据类缓存,便于 fromJSON使用 */ protected dataRegistryMap: Map = new Map; /** * Entity的所有实例缓存 */ protected entityInstanceMap: Map = new Map; // By entity id /** * Entity的实例按类型缓存,便于查询优化 */ protected entityInstanceMapByType: Map = new Map; // By entity type /** * 所有配置实体的缓存 */ protected configEntities: Map = new Map; /** * 当对应的实体类型变化后触发 */ readonly onEntityChanged = this.onEntityChangedEmitter.event; /** * 暂停触发实体类型变化 */ pauseEntityChanged = false; constructor( /** * Able管理器 */ @inject(AbleManager) readonly ableManager: AbleManager, @inject(PlaygroundContext) @optional() readonly context: PlaygroundContext ) { this.toDispose.pushAll([ this.onEntityChangedEmitter, this.schedule ]); } /** * 创建实体 * @param Registry * @param opts */ createEntity(Registry: EntityRegistry, opts?: Omit): T { this.registerEntity(Registry); // ConfigEntity 默认为单例 if (this.configEntities.has(Registry.type)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any return this.configEntities.get(Registry.type) as any; } const entityOpts: EntityOpts = { entityManager: this, savedInManager: true, ...opts }; const entity = new Registry(entityOpts) as T; if (entityOpts.savedInManager) { this.saveEntity(entity); } return entity; } isConfigEntity(type: string): boolean { return this.configEntities.has(type); } /** * 批量删除实体 * @param Registry */ removeEntities(Registry: EntityRegistry): void { for (const e of this.getEntities(Registry).values()) { e.dispose(); } } removeEntityById(id: string): boolean { const entity = this.getEntityById(id); if (entity) { entity.dispose(); return true; } return false; } /** * 触发实体reset * @param registry */ resetEntities(registry: EntityRegistry): void { const entities = this.getEntities(registry); entities.forEach(entity => { entity.reset(); }); } resetEntity(registry: EntityRegistry, autoCreate?: boolean): void { const entity = this.getEntity(registry, autoCreate)!; entity.reset(); } updateConfigEntity(registry: EntityRegistry, config: Partial): void { const entity = this.configEntities.get(registry.type); if (entity) { entity.updateConfig(config); } } /** * @param type */ getRegistryByType(type: string): EntityRegistry | undefined { return this.registryMap.get(type); } /** * @param Registry */ registerEntity(Registry: EntityRegistry): void { if (!Registry.type) throw new Error('Registry entity need a type: ' + Registry.name); const oldRegistry = this.registryMap.get(Registry.type); if (oldRegistry) { if (oldRegistry !== Registry) { throw new Error(`Entity registry ${Registry.type} need a new type`); } return; } this.registryMap.set(Registry.type, Registry); } registerEntityData(Registry: EntityDataRegistry): void { if (!Registry.type) throw new Error('Registry entity data need a type: ' + Registry.name); const oldRegistry = this.dataRegistryMap.get(Registry.type); if (oldRegistry) { if (oldRegistry !== Registry) { throw new Error(`Entity data registry ${Registry.type} need a new type`); } return; } this.dataRegistryMap.set(Registry.type, Registry); } getDataRegistryByType(type: string): EntityDataRegistry | undefined { return this.dataRegistryMap.get(type); } getEntityById(id: string): T | undefined { return this.entityInstanceMap.get(id) as T; } /** * * @param registry * @param autoCreate 是否要自动创建, 默认false */ getEntity(registry: EntityRegistry, autoCreate?: boolean): T | undefined { const entity = this.getEntities(registry)[0]; if (!entity && autoCreate) { return this.createEntity(registry); } return entity; } /** * * @param registry */ getEntities(registry: EntityRegistry): T[] { return this.entityInstanceMapByType.get(registry.type) as T[] || []; } getEntitiesByAble(registry: AbleRegistry): T[] { return this.ableManager.getEntitiesByAble(registry); } getEntitiesByAbles(...registries: AbleRegistry[]): T[] { return this.ableManager.getEntitiesByAbles(...registries); } getEntityByAble(registry: AbleRegistry): T | undefined { return this.ableManager.getEntityByAble(registry); } /** * @param registry */ hasEntity(registry: EntityRegistry): boolean { return !!this.getEntity(registry); } /** * 只存储config数据,忽略动态数据 */ storeState(): object { const data: object[] = []; for (const e of this.entityInstanceMap.values()) { if (e instanceof ConfigEntity && e.toJSON) { if (e.toJSON) { const d = e.toJSON(); if (d) { data.push(d); } } } } return data; } /** * @param data */ restoreState(data: object): void { if (!data || !Array.isArray(data)) return; data.forEach((s: EntityJSON) => { if (!s || !s.type || !s.id) return; const register = this.getRegistryByType(s.type); // 如果没有注册,则忽略掉 if (!register) { console.warn('Playground entity registry lost: ' + s.type); return; } // eslint-disable-next-line @typescript-eslint/no-explicit-any const entity = this.createEntity(register, { id: data ? (data as any).id : undefined }); if (entity.fromJSON) { entity.fromJSON(s); } }); } protected saveEntity(entity: Entity): void { const id = entity.id; // 无法重复创建 if (id && this.entityInstanceMap.has(id)) { console.error(`Entity ${entity.type} ${id} is created before`); return; } this.entityInstanceMap.set(entity.id, entity); let entities = this.entityInstanceMapByType.get(entity.type); if (!entities) { entities = []; this.entityInstanceMapByType.set(entity.type, entities); } if (entity instanceof ConfigEntity) { this.configEntities.set(entity.type, entity); } entities.push(entity); entity.onEntityChanged(() => this.fireEntityChanged(entity.type)); entity.toDispose.push(Disposable.create(() => { this.removeEntity(entity); })); this.fireEntityChanged(entity.type); } protected removeEntity(entity: Entity): void { if (this.entityInstanceMap.has(entity.id)) { const entities = this.entityInstanceMapByType.get(entity.type) || []; const index = entities.indexOf(entity); if (index !== -1) { entities.splice(index, 1); this.entityInstanceMap.delete(entity.id); this.fireEntityChanged(entity.type); } } } /** * 重制所有entity为初始化状态 */ reset(): void { for (const entity of this.entityInstanceMap.values()) { entity.reset(); } } private schedule = new PlaygroundSchedule(); fireEntityChanged(entityType: string): void { if (this.pauseEntityChanged) return; this.schedule.push(entityType, () => { this.onEntityChangedEmitter.fire(entityType); }); } dispose(): void { this.toDispose.dispose(); } }