@@ -5,9 +5,9 @@ import { Logger } from '../logger';
55import { EntityMeta } from './BitECSComponents' ;
66import { componentRegistry , ComponentRegistry } from './ComponentRegistry' ;
77import {
8+ clearPersistentIdMaps ,
89 generatePersistentId ,
910 PersistentIdSchema ,
10- clearPersistentIdMaps ,
1111} from './components/definitions/PersistentIdComponent' ;
1212import { getEntityName , getEntityParent , setEntityMeta } from './DataConversion' ;
1313import { IEntity } from './IEntity' ;
@@ -28,62 +28,79 @@ export class EntityManager {
2828 private eventListeners : EntityEventListener [ ] = [ ] ;
2929 private entityCache : Map < EntityId , IEntity > = new Map ( ) ;
3030 private existingPersistentIds : Set < string > = new Set ( ) ;
31- private queries : EntityQueries ;
31+ private queries : EntityQueries | null = null ;
3232 private world : any ; // BitECS world - using any for compatibility with bitecs
3333 private componentRegistry : ComponentRegistry ;
3434 private logger = Logger . create ( 'EntityManager' ) ;
35+ private isInstanceMode = false ;
3536
3637 constructor ( world ?: any , componentManager ?: ComponentRegistry ) {
3738 if ( world ) {
3839 // Instance mode with injected world and optional component manager
40+ this . isInstanceMode = true ;
3941 this . world = world ;
40- this . queries = new EntityQueries ( world ) ;
42+ // Note: EntityQueries will be set later via setEntityQueries to avoid circular dependency
4143 this . componentRegistry = componentManager || componentRegistry ;
4244 } else {
4345 // Singleton mode (backward compatibility)
46+ this . isInstanceMode = false ;
4447 this . world = ECSWorld . getInstance ( ) . getWorld ( ) ;
4548 this . queries = EntityQueries . getInstance ( ) ;
4649 this . componentRegistry = componentRegistry ;
4750 }
4851 }
4952
53+ /**
54+ * Set the EntityQueries instance for this manager (used in instance mode to avoid circular dependency)
55+ */
56+ public setEntityQueries ( queries : EntityQueries ) : void {
57+ this . queries = queries ;
58+ }
59+
5060 public static getInstance ( ) : EntityManager {
5161 if ( ! EntityManager . instance ) {
5262 EntityManager . instance = new EntityManager ( ) ;
5363 }
5464 return EntityManager . instance ;
5565 }
5666
57- public reset ( ) : void {
67+ public reset ( newWorld ?: any ) : void {
5868 this . entityCache . clear ( ) ;
5969 this . existingPersistentIds . clear ( ) ;
60- this . eventListeners = [ ] ;
70+ // Note: We do NOT clear eventListeners here because external components
71+ // (like EntityQueries/IndexEventAdapter) need to stay attached to receive
72+ // events about new entities created after the reset.
73+ // this.eventListeners = [];
6174
6275 // Clear persistent ID mappings
6376 clearPersistentIdMaps ( ) ;
6477
65- // Refresh world reference in case ECSWorld singleton was reset
66- this . refreshWorld ( ) ;
78+ if ( this . isInstanceMode ) {
79+ // For instance mode, optionally update world and rebuild indices
80+ if ( newWorld ) {
81+ this . world = newWorld ;
82+ }
83+ this . queries ?. reset ( ) ;
84+ return ;
85+ }
6786
68- // Reset EntityQueries to rebuild indices with new world state
69- this . queries . reset ( ) ;
87+ // Singleton mode: refresh world reference and reset indices
88+ this . refreshWorld ( ) ;
89+ this . queries ?. reset ( ) ;
7090 }
7191
7292 /**
7393 * Refresh world reference from singleton (used after ECSWorld reset)
7494 */
7595 public refreshWorld ( ) : void {
76- // Always refresh for singleton instances (instance was created without injected world)
77- // We can't reliably check if this.world === ECSWorld.getInstance().getWorld()
78- // because the world may have been reset, so we check if we're a singleton instance
79- const currentSingletonWorld = ECSWorld . getInstance ( ) . getWorld ( ) ;
96+ if ( this . isInstanceMode ) {
97+ // In instance mode, world is managed externally by the factory; no-op here
98+ return ;
99+ }
80100
81- // Force refresh the world reference to current singleton world
101+ const currentSingletonWorld = ECSWorld . getInstance ( ) . getWorld ( ) ;
82102 this . world = currentSingletonWorld ;
83- // Also update queries with new world
84103 this . queries = EntityQueries . getInstance ( ) ;
85-
86- // Clear cache and rebuild persistent ID tracking
87104 this . entityCache . clear ( ) ;
88105 this . rebuildPersistentIdCache ( ) ;
89106 }
@@ -202,7 +219,7 @@ export class EntityManager {
202219
203220 const entities = this . getAllEntities ( ) ;
204221 entities . forEach ( ( entity ) => {
205- const persistentIdData = componentRegistry . getComponentData < { id : string } > (
222+ const persistentIdData = this . componentRegistry . getComponentData < { id : string } > (
206223 entity . id ,
207224 'PersistentId' ,
208225 ) ;
@@ -253,6 +270,18 @@ export class EntityManager {
253270 return entity ;
254271 }
255272
273+ /**
274+ * Create entity for adapter interface (returns { id: number })
275+ */
276+ createEntityForAdapter (
277+ name : string ,
278+ parentId ?: number | null ,
279+ persistentId ?: string ,
280+ ) : { id : number } {
281+ const entity = this . createEntity ( name , parentId , persistentId ) ;
282+ return { id : entity . id } ;
283+ }
284+
256285 getEntity ( id : EntityId ) : IEntity | undefined {
257286 if ( this . entityCache . has ( id ) ) {
258287 return this . entityCache . get ( id ) ;
@@ -269,10 +298,34 @@ export class EntityManager {
269298 // Rebuild cache from BitECS world using efficient indexed lookup
270299 this . entityCache . clear ( ) ;
271300
301+ // Return entities for adapter interface
302+ return this . getAllEntitiesForAdapter ( ) ;
303+ }
304+
305+ /**
306+ * Get all entities in adapter format (for SceneDeserializer compatibility)
307+ */
308+ private getAllEntitiesForAdapter ( ) : Array < {
309+ id : number ;
310+ name : string ;
311+ parentId ?: number | null ;
312+ } > {
313+ const entities = this . getAllEntitiesInternal ( ) ;
314+ return entities . map ( ( entity ) => ( {
315+ id : entity . id ,
316+ name : entity . name ,
317+ parentId : entity . parentId ,
318+ } ) ) ;
319+ }
320+
321+ /**
322+ * Internal method to get all entities
323+ */
324+ private getAllEntitiesInternal ( ) : IEntity [ ] {
272325 // Get all entity IDs - use scan as fallback if queries not ready
273326 let entityIds : number [ ] ;
274327 try {
275- entityIds = this . queries . listAllEntities ( ) ;
328+ entityIds = this . queries ? .listAllEntities ( ) || [ ] ;
276329
277330 // If queries return empty but we know entities exist, fall back to scan
278331 if ( entityIds . length === 0 ) {
@@ -322,7 +375,7 @@ export class EntityManager {
322375 try {
323376 const queriesAvailable = this . queries && this . queries . listAllEntities ( ) . length > 0 ;
324377 entities . forEach ( ( entity ) => {
325- if ( queriesAvailable ) {
378+ if ( queriesAvailable && this . queries ) {
326379 entity . children = this . queries . getChildren ( entity . id ) ;
327380 } else {
328381 // Fall back to filtering
@@ -352,7 +405,10 @@ export class EntityManager {
352405 if ( ! entity ) return false ;
353406
354407 // Remove persistent ID from tracking
355- const persistentIdData = componentRegistry . getComponentData < { id : string } > ( id , 'PersistentId' ) ;
408+ const persistentIdData = this . componentRegistry . getComponentData < { id : string } > (
409+ id ,
410+ 'PersistentId' ,
411+ ) ;
356412 if ( persistentIdData && persistentIdData . id ) {
357413 this . existingPersistentIds . delete ( persistentIdData . id ) ;
358414 }
@@ -426,7 +482,7 @@ export class EntityManager {
426482 getRootEntities ( ) : IEntity [ ] {
427483 // Try to use efficient indexed query, fall back if queries not ready
428484 try {
429- const rootEntityIds = this . queries . getRootEntities ( ) ;
485+ const rootEntityIds = this . queries ? .getRootEntities ( ) || [ ] ;
430486
431487 // If result is empty, verify with fallback to avoid race conditions
432488 if ( rootEntityIds . length === 0 ) {
@@ -469,6 +525,13 @@ export class EntityManager {
469525 return true ;
470526 }
471527
528+ /**
529+ * Set parent for adapter interface (void return)
530+ */
531+ setParentForAdapter ( childId : number , parentId ?: number | null ) : void {
532+ this . setParent ( childId , parentId ) ;
533+ }
534+
472535 setParent ( entityId : EntityId , newParentId ?: EntityId ) : boolean {
473536 const entity = this . getEntity ( entityId ) ;
474537 if ( ! entity ) return false ;
@@ -522,6 +585,13 @@ export class EntityManager {
522585 return true ;
523586 }
524587
588+ /**
589+ * Clear entities for adapter interface
590+ */
591+ clearEntitiesForAdapter ( ) : void {
592+ this . clearEntities ( ) ;
593+ }
594+
525595 private wouldCreateCircularDependency ( entityId : EntityId , potentialParentId : EntityId ) : boolean {
526596 let currentId : EntityId | null = potentialParentId ;
527597
@@ -548,7 +618,7 @@ export class EntityManager {
548618 * @returns The persistent ID string, or undefined if not found
549619 */
550620 getEntityPersistentId ( entityId : EntityId ) : string | undefined {
551- const persistentIdData = componentRegistry . getComponentData < { id : string } > (
621+ const persistentIdData = this . componentRegistry . getComponentData < { id : string } > (
552622 entityId ,
553623 'PersistentId' ,
554624 ) ;
@@ -581,7 +651,7 @@ export class EntityManager {
581651 const entities = this . getAllEntities ( ) ;
582652
583653 for ( const entity of entities ) {
584- const persistentIdData = componentRegistry . getComponentData < { id : string } > (
654+ const persistentIdData = this . componentRegistry . getComponentData < { id : string } > (
585655 entity . id ,
586656 'PersistentId' ,
587657 ) ;
0 commit comments