import { UniqueObject } from "./UniqueObject" ; import { GraphicAsset } from "../graphics/GraphicAsset" ; import { Debug } from "../io/Debug" ; import { IOUtils , AssetChangeAction } from "../io/IOUtils" ; import { Asset } from "../assets/Asset" ; import { EngineEvents } from "./EngineEvents" ; import { AssetIdDatabase } from "../assets/AssetIdDatabase" ; import { IFileInternal } from "../io/File/IFile" ; import { IFactoryInternal } from "../serialization/IFactory" ; import { IObjectManager , IObjectManagerInternal } from "./IObjectManager" ; interface ObjectLoadingCallbacks { loaded : ( obj : UniqueObject , fromCache : boolean ) => void ; error : ( err : any ) => void ; } namespace Private { export let objectCache = { } ; export const objectLoadingInProgress = { } ; } export namespace ObjectManagerInternal { export function loadObject ( path , loaded , error , ignoreCache ) { const existingInstance = Private . objectCache [ path ] ; if ( existingInstance ) { if ( ignoreCache ) { delete Private . objectCache [ path ] ; } else { loaded ( existingInstance , true ) ; return ; } } const loadingCallbacks = { loaded , error } ; const existingCallbacks = Private . objectLoadingInProgress [ path ] ; if ( existingCallbacks ) { existingCallbacks . push ( loadingCallbacks ) ; return ; } Private . objectLoadingInProgress [ path ] = [ loadingCallbacks ] ; IFileInternal . instance . read ( path ) . then ( data => loadObjectFromData ( path , data ) ) . then ( instance => { for ( const pendingCallback of Private . objectLoadingInProgress [ path ] ) { pendingCallback . loaded ( instance as UniqueObject , false ) ; } delete Private . objectLoadingInProgress [ path ] ; } ) . catch ( err => { for ( const pendingCallback of Private . objectLoadingInProgress [ path ] ) { pendingCallback . error ( err ) ; } delete Private . objectLoadingInProgress [ path ] ; } ) ; } export function loadObjectFromData ( path , data ) { const json = JSON . parse ( data ) ; const instance = IFactoryInternal . instance . createObject ( json . typeName ) as UniqueObject ; Private . objectCache [ path ] = instance ; if ( instance . isA ( Asset ) ) { ( instance as Asset ) . isPersistent = true ; } instance . templatePath = path ; return instance . deserialize ( json ) ; } export function loadObjectById ( id , loaded , error ) { const path = AssetIdDatabase . getPath ( id ) ; if ( path ) { loadObject ( path , loaded , error , false ) ; } else { error ( ) ; } } export function loadObjectIgnoreCache ( path ) { return new Promise < [ UniqueObject , boolean ] > ( ( resolve , reject ) => { loadObject ( path , obj => resolve ( [ obj , false ] ) , reject , true ) ; } ) ; } export const objectCache = ( ) => Private . objectCache ; } export class ObjectManager implements IObjectManager { loadObject ( path ) { return new Promise < [ UniqueObject , boolean ] > ( ( resolve , reject ) => { ObjectManagerInternal . loadObject ( path , ( obj , fromCache ) => resolve ( [ obj , fromCache ] ) , reject , false ) ; } ) ; } loadObjectById ( id ) { return new Promise < [ UniqueObject , boolean ] > ( ( resolve , reject ) => { ObjectManagerInternal . loadObjectById ( id , ( obj , fromCache ) => resolve ( [ obj , fromCache ] ) , reject ) ; } ) ; } getObject ( path ) { return Private . objectCache [ path ] ?? null ; } getObjectById ( id ) { const path = AssetIdDatabase . getPath ( id ) ; return path ? this . getObject ( path ) : null ; } saveObject ( obj ) { const id = obj . id ; const path = AssetIdDatabase . getPath ( id ) ; if ( path ) { return this . saveObjectAtPath ( obj , path ) ; } else { Debug . log ( `Could not save object with id '${ id }' (could not resolve its path)` ) ; return Promise . reject ( ) ; } } saveObjectAtPath ( obj , path , recordWriteTime ? ) { return new Promise < void > ( ( resolve , reject ) => { try { const data = JSON . stringify ( obj . serialize ( ) , null , 2 ) ; IFileInternal . instance . write ( path , data ) . then ( ( ) => { if ( recordWriteTime !== false ) { IOUtils . recordAssetChange ( AssetChangeAction . update , [ obj . id ] ) ; } EngineEvents . objectSaved . post ( obj ) ; resolve ( ) ; } ) . catch ( reject ) ; } catch ( e ) { reject ( e ) ; } } ) ; } renameObject ( oldPath , newPath ) { IOUtils . parseFilePath ( newPath , ( newName , typeName ) => { if ( oldPath in Private . objectCache ) { const obj = Private . objectCache [ oldPath ] ; delete Private . objectCache [ oldPath ] ; obj . templatePath = newPath ; Private . objectCache [ newPath ] = obj ; obj . name = newName ; this . saveObjectAtPath ( obj , newPath ) ; } else { IOUtils . parseFilePath ( oldPath , ( oldName , _typeName ) => { if ( oldName !== newName ) { this . loadObject ( newPath ) . then ( ( [ obj ] ) => { obj . name = newName ; this . saveObjectAtPath ( obj , newPath ) ; } ) ; } } ) ; } } ) ; } deleteObject ( obj ) { obj . destroy ( ) ; if ( obj . templatePath ) { delete Private . objectCache [ obj . templatePath ] ; } } deleteObjectById ( id ) { const path = AssetIdDatabase . getPath ( id ) ; if ( path ) { this . deleteObjectByPath ( path ) ; } } deleteObjectByPath ( path ) { const obj = Private . objectCache [ path ] ; if ( obj ) { this . deleteObject ( obj ) ; delete Private . objectCache [ path ] ; } } loadGraphicObjects ( ) { for ( const obj of Object . values ( Private . objectCache ) ) { if ( obj . isA ( GraphicAsset ) ) { ( obj as GraphicAsset ) . graphicLoad ( ) ; } } } unloadGraphicObjects ( ) { for ( const obj of Object . values ( Private . objectCache ) ) { if ( obj . isA ( GraphicAsset ) ) { ( obj as GraphicAsset ) . graphicUnload ( ) ; } } } addToCache ( obj ) { const path = obj . templatePath as string ; console . assert ( path ) ; console . assert ( ! ( path in Private . objectCache ) ) ; Private . objectCache [ path ] = obj ; } clearCache ( ) { for ( const obj of Object . values ( Private . objectCache ) ) { obj . destroy ( ) ; } Private . objectCache = { } ; } forEach ( handler ) { for ( const obj of Object . values ( Private . objectCache ) ) { handler ( obj ) ; } } } hidden export namespace ObjectManagerInternal { export function create ( ) { IObjectManagerInternal . instance = new ObjectManager ( ) ; } }