import { IGraphicAssetCollection, IRoomInstance, IRoomInstanceContainer, IRoomManager, IRoomManagerListener, IRoomObject, IRoomObjectController, IRoomObjectManager } from '@nitrots/api'; import { GetEventDispatcher, RoomContentLoadedEvent } from '@nitrots/events'; import { NitroLogger } from '@nitrots/utils'; import { GetRoomContentLoader } from './GetRoomContentLoader'; import { GetRoomObjectLogicFactory } from './GetRoomObjectLogicFactory'; import { GetRoomObjectVisualizationFactory } from './GetRoomObjectVisualizationFactory'; import { RoomInstance } from './RoomInstance'; import { RoomObjectManager } from './RoomObjectManager'; export class RoomManager implements IRoomManager, IRoomInstanceContainer { private _rooms: Map = new Map(); private _updateCategories: number[] = []; private _listener: IRoomManagerListener; private _pendingContentTypes: string[] = []; private _skipContentProcessing: boolean = false; private _contentLoadedCallback: (event: RoomContentLoadedEvent) => void = null; public async init(listener: IRoomManagerListener): Promise { this._listener = listener; // Store callback for cleanup this._contentLoadedCallback = (event: RoomContentLoadedEvent) => { if(!GetRoomContentLoader()) return; const contentType = event.contentType; if(this._pendingContentTypes.indexOf(contentType) >= 0) return; this._pendingContentTypes.push(contentType); }; GetEventDispatcher().addEventListener(RoomContentLoadedEvent.RCLE_SUCCESS, this._contentLoadedCallback); GetEventDispatcher().addEventListener(RoomContentLoadedEvent.RCLE_FAILURE, this._contentLoadedCallback); GetEventDispatcher().addEventListener(RoomContentLoadedEvent.RCLE_CANCEL, this._contentLoadedCallback); } public dispose(): void { // Remove event listeners if(this._contentLoadedCallback) { GetEventDispatcher().removeEventListener(RoomContentLoadedEvent.RCLE_SUCCESS, this._contentLoadedCallback); GetEventDispatcher().removeEventListener(RoomContentLoadedEvent.RCLE_FAILURE, this._contentLoadedCallback); GetEventDispatcher().removeEventListener(RoomContentLoadedEvent.RCLE_CANCEL, this._contentLoadedCallback); this._contentLoadedCallback = null; } // Dispose all room instances for(const room of this._rooms.values()) { room.dispose(); } this._rooms.clear(); this._pendingContentTypes = []; } public getRoomInstance(roomId: string): IRoomInstance { const existing = this._rooms.get(roomId); if(!existing) return null; return existing; } public createRoomInstance(roomId: string): IRoomInstance { if(this._rooms.get(roomId)) return null; const instance = new RoomInstance(roomId, this); this._rooms.set(instance.id, instance); if(this._updateCategories.length) { for(const category of this._updateCategories) { instance.addUpdateCategory(category); } } return instance; } public removeRoomInstance(roomId: string): boolean { const existing = this._rooms.get(roomId); if(!existing) return false; this._rooms.delete(roomId); existing.dispose(); return true; } public createRoomObjectAndInitalize(roomId: string, objectId: number, type: string, category: number): IRoomObject { const instance = this.getRoomInstance(roomId); if(!instance) return null; let visualization = type; let logic = type; let assetName = type; let asset: IGraphicAssetCollection = null; let isLoading = false; if(GetRoomContentLoader().isLoaderType(type)) { asset = GetRoomContentLoader().getCollection(type); if(!asset) { isLoading = true; GetRoomContentLoader().downloadAssetSync(type); assetName = GetRoomContentLoader().getPlaceholderName(type); asset = GetRoomContentLoader().getCollection(assetName); if(!asset) return null; } visualization = asset.data.visualizationType; logic = asset.data.logicType; } const object = (instance.createRoomObject(objectId, 1, type, category) as IRoomObjectController); if(!object) return null; const visualizationInstance = GetRoomObjectVisualizationFactory().getVisualization(visualization); if(!visualizationInstance) { instance.removeRoomObject(objectId, category); return null; } visualizationInstance.asset = asset; const visualizationData = GetRoomObjectVisualizationFactory().getVisualizationData(assetName, visualization, ((asset && asset.data) || null)); if(!visualizationData || !visualizationInstance.initialize(visualizationData)) { instance.removeRoomObject(objectId, category); return null; } object.setVisualization(visualizationInstance); const logicInstance = GetRoomObjectLogicFactory().getLogic(logic); object.setLogic(logicInstance); if(logicInstance) { logicInstance.initialize((asset && asset.data) || null); } if(!isLoading) object.isReady = true; GetRoomContentLoader().setRoomObjectRoomId(object, roomId); return object; } private reinitializeRoomObjectsByType(type: string): void { if(!type || !GetRoomContentLoader()) return; const asset = GetRoomContentLoader().getCollection(type); if(!asset) return; const visualization = asset.data.visualizationType; const logic = asset.data.logicType; const visualizationData = GetRoomObjectVisualizationFactory().getVisualizationData(type, visualization, asset.data); for(const room of this._rooms.values()) { if(!room) continue; for(const [category, manager] of room.managers.entries()) { if(!manager) continue; for(const object of manager.objects.getValues()) { if(!object || object.type !== type) continue; const visualizationInstance = GetRoomObjectVisualizationFactory().getVisualization(visualization); if(visualizationInstance) { visualizationInstance.asset = asset; if(!visualizationData || !visualizationInstance.initialize(visualizationData)) { manager.removeObject(object.id); } else { object.setVisualization(visualizationInstance); const logicInstance = GetRoomObjectLogicFactory().getLogic(logic); object.setLogic(logicInstance); if(logicInstance) { logicInstance.initialize(asset.data); } object.isReady = true; if(this._listener) this._listener.objectInitialized(room.id, object.id, category); } } else { manager.removeObject(object.id); } } } } } public addUpdateCategory(category: number): void { const index = this._updateCategories.indexOf(category); if(index >= 0) return; this._updateCategories.push(category); if(!this._rooms.size) return; for(const room of this._rooms.values()) { if(!room) continue; room.addUpdateCategory(category); } } public removeUpdateCategory(category: number): void { const index = this._updateCategories.indexOf(category); if(index === -1) return; this._updateCategories.splice(index, 1); if(!this._rooms.size) return; for(const room of this._rooms.values()) { if(!room) continue; room.removeUpdateCategory(category); } } private processPendingContentTypes(time: number): void { if(this._skipContentProcessing) { this._skipContentProcessing = false; return; } while(this._pendingContentTypes.length) { const type = this._pendingContentTypes.shift(); const collection = GetRoomContentLoader().getCollection(type); if(!collection) { if(this._listener) { this._listener.initalizeTemporaryObjectsByType(type, false); } NitroLogger.log('Invalid Collection', type); continue; } this.reinitializeRoomObjectsByType(type); if(this._listener) this._listener.initalizeTemporaryObjectsByType(type, true); } } public update(time: number, update: boolean = false): void { this.processPendingContentTypes(time); if(!this._rooms.size) return; for(const room of this._rooms.values()) room && room.update(time, update); } public createRoomObjectManager(category: number): IRoomObjectManager { return new RoomObjectManager(); } public get rooms(): Map { return this._rooms; } }