File tree Expand file tree Collapse file tree 2 files changed +45
-10
lines changed
Expand file tree Collapse file tree 2 files changed +45
-10
lines changed Original file line number Diff line number Diff line change @@ -377,16 +377,17 @@ export function createQueryInstance<T extends QueryParameter[]>(
377377 traitMatches =
378378 ( oldMask & bitflag ) === 0 && ( currentMask & bitflag ) === bitflag ;
379379 break ;
380- case 'remove' :
381- traitMatches =
382- ( ( oldMask & bitflag ) === bitflag && ( currentMask & bitflag ) === 0 ) ||
383- ( ( oldMask & bitflag ) === 0 &&
384- ( currentMask & bitflag ) === 0 &&
385- ( dirtyMask [ generationId ] [ eid ] & bitflag ) === bitflag ) ;
386- break ;
387- case 'change' :
388- traitMatches = ( changedMask [ generationId ] [ eid ] & bitflag ) === bitflag ;
389- break ;
380+ case 'remove' :
381+ traitMatches =
382+ ( ( oldMask & bitflag ) === bitflag && ( currentMask & bitflag ) === 0 ) ||
383+ ( ( oldMask & bitflag ) === 0 &&
384+ ( currentMask & bitflag ) === 0 &&
385+ ( ( dirtyMask [ generationId ] ?. [ eid ] ?? 0 ) & bitflag ) === bitflag ) ;
386+ break ;
387+ case 'change' :
388+ traitMatches =
389+ ( ( changedMask [ generationId ] ?. [ eid ] ?? 0 ) & bitflag ) === bitflag ;
390+ break ;
390391 }
391392
392393 if ( ! traitMatches ) {
Original file line number Diff line number Diff line change @@ -574,4 +574,38 @@ describe('Query modifiers', () => {
574574
575575 expect ( entity . get ( Name ) ! . name ) . toBe ( 'modified' ) ;
576576 } ) ;
577+
578+ // @internal Tests internal implementation edge case with generation overflow
579+ it ( '[internal] should handle Changed modifier when trait registration causes generation overflow' , ( ) => {
580+ // Create a fresh world to control trait registration count
581+ const testWorld = createWorld ( ) ;
582+ testWorld . init ( ) ;
583+
584+ // IsExcluded is already registered (bitflag=2 after), register 29 more to get bitflag=2^30
585+ const fillerTraits = Array . from ( { length : 29 } , ( ) => trait ( ) ) ;
586+ for ( const t of fillerTraits ) {
587+ testWorld . spawn ( t ) ;
588+ }
589+
590+ // Create Changed modifier - snapshots entityMasks with 1 generation
591+ const Changed = createChanged ( ) ;
592+
593+ // Spawn an entity so entityIndex.dense is not empty (required to trigger the bug)
594+ const entity = testWorld . spawn ( ) ;
595+
596+ // Register the 31st trait to trigger overflow (bitflag 2^30 -> 2^31 -> overflow)
597+ const Trait31 = trait ( ) ;
598+ entity . add ( Trait31 ) ;
599+
600+ // Now entityMasks has 2 generations, but changedMask only has 1
601+
602+ // Create the 32nd trait - will be in generation 1
603+ const NewTrait = trait ( ) ;
604+
605+ // This should not throw "cannot read properties of undefined (reading '0')"
606+ // when accessing changedMask[generationId][eid] where generationId is 1 but changedMask only has index 0
607+ expect ( ( ) => {
608+ testWorld . query ( Changed ( NewTrait ) ) ;
609+ } ) . not . toThrow ( ) ;
610+ } ) ;
577611} ) ;
You can’t perform that action at this time.
0 commit comments