You've already forked Nitro_Render_V3
mirror of
https://github.com/duckietm/Nitro_Render_V3.git
synced 2026-06-19 23:16:20 +00:00
eb4fe80612
- Added Test Coverage - Fix Potential Memory Leaks
327 lines
9.7 KiB
TypeScript
327 lines
9.7 KiB
TypeScript
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<string, IRoomInstance> = 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<void>
|
|
{
|
|
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<string, IRoomInstance>
|
|
{
|
|
return this._rooms;
|
|
}
|
|
}
|