@@ -33,6 +33,8 @@ import {
3333 validateSchema ,
3434} from '../storage' ;
3535import type { World } from '../world' ;
36+ import { BitSet } from '../utils/bit-set' ;
37+ import { ensureEntityMaskSize } from '../world/utils/ensure-entity-mask-size' ;
3638import { incrementWorldBitflag } from '../world/utils/increment-world-bit-flag' ;
3739import { getTraitInstance , hasTraitInstance , setTraitInstance } from './trait-instance' ;
3840import type {
@@ -100,11 +102,12 @@ export function registerTrait(world: World, trait: Trait) {
100102 bitflag : ctx . bitflag ,
101103 trait,
102104 store : traitCtx . createStore ( ) ,
103- queries : new Set ( ) ,
104- trackingQueries : new Set ( ) ,
105- notQueries : new Set ( ) ,
106- relationQueries : new Set ( ) ,
105+ queries : [ ] ,
106+ trackingQueries : [ ] ,
107+ notQueries : [ ] ,
108+ relationQueries : [ ] ,
107109 schema : trait . schema ,
110+ entityBitSet : new BitSet ( ) ,
108111 changeSubscriptions : new Set ( ) ,
109112 addSubscriptions : new Set ( ) ,
110113 removeSubscriptions : new Set ( ) ,
@@ -453,37 +456,43 @@ export function getTrait(world: World, entity: Entity, trait: Trait | RelationPa
453456
454457 // Add bitflag to entity bitmask
455458 const eid = getEntityId ( entity ) ;
459+ ensureEntityMaskSize ( ctx . entityMasks , generationId , eid ) ;
456460 ctx . entityMasks [ generationId ] [ eid ] |= bitflag ;
461+ instance . entityBitSet . add ( eid ) ;
457462
458463 // Set the entity as dirty
459464 for ( const dirtyMask of ctx . dirtyMasks . values ( ) ) {
460465 if ( ! dirtyMask [ generationId ] ) dirtyMask [ generationId ] = [ ] ;
461466 dirtyMask [ generationId ] [ eid ] |= bitflag ;
462467 }
463468
464- // Update non-tracking queries (no event data needed)
465- for ( const query of queries ) {
466- query . toRemove . remove ( entity ) ;
467- // Use checkQueryWithRelations if query has relation filters, otherwise use checkQuery
468- const match =
469- query . relationFilters && query . relationFilters . length > 0
470- ? checkQueryWithRelations ( world , query , entity )
471- : query . check ( world , entity ) ;
472- if ( match ) query . add ( entity ) ;
473- else query . remove ( world , entity ) ;
474- }
475-
476- // Update tracking queries (with event data)
477- for ( const query of trackingQueries ) {
478- query . toRemove . remove ( entity ) ;
479- // Use checkQueryTrackingWithRelations if query has relation filters, otherwise use checkQueryTracking
480- const match =
481- query . relationFilters && query . relationFilters . length > 0
482- ? checkQueryTrackingWithRelations ( world , query , entity , 'add' , generationId , bitflag )
483- : query . checkTracking ( world , entity , 'add' , generationId , bitflag ) ;
484- if ( match ) query . add ( entity ) ;
485- else query . remove ( world , entity ) ;
486- }
469+ // Update non-tracking queries (no event data needed)
470+ // PERF: Use indexed loop instead of for...of to avoid iterator overhead
471+ for ( let qi = 0 , qLen = queries . length ; qi < qLen ; qi ++ ) {
472+ const query = queries [ qi ] ;
473+ query . toRemove . remove ( entity ) ;
474+ // Use checkQueryWithRelations if query has relation filters, otherwise use checkQuery
475+ const match =
476+ query . relationFilters && query . relationFilters . length > 0
477+ ? checkQueryWithRelations ( world , query , entity )
478+ : query . check ( world , entity ) ;
479+ if ( match ) query . add ( entity ) ;
480+ else query . remove ( world , entity ) ;
481+ }
482+
483+ // Update tracking queries (with event data)
484+ // PERF: Use indexed loop instead of for...of to avoid iterator overhead
485+ for ( let qi = 0 , qLen = trackingQueries . length ; qi < qLen ; qi ++ ) {
486+ const query = trackingQueries [ qi ] ;
487+ query . toRemove . remove ( entity ) ;
488+ // Use checkQueryTrackingWithRelations if query has relation filters, otherwise use checkQueryTracking
489+ const match =
490+ query . relationFilters && query . relationFilters . length > 0
491+ ? checkQueryTrackingWithRelations ( world , query , entity , 'add' , generationId , bitflag )
492+ : query . checkTracking ( world , entity , 'add' , generationId , bitflag ) ;
493+ if ( match ) query . add ( entity ) ;
494+ else query . remove ( world , entity ) ;
495+ }
487496
488497 // Add trait to entity internally
489498 ctx . entityTraits . get ( entity ) ! . add ( trait ) ;
@@ -510,40 +519,41 @@ function removeTraitFromEntity(world: World, entity: Entity, trait: Trait): void
510519 // Remove bitflag from entity bitmask
511520 const eid = getEntityId ( entity ) ;
512521 ctx . entityMasks [ generationId ] [ eid ] &= ~ bitflag ;
522+ instance . entityBitSet . remove ( eid ) ;
513523
514524 // Set the entity as dirty
515525 for ( const dirtyMask of ctx . dirtyMasks . values ( ) ) {
516526 dirtyMask [ generationId ] [ eid ] |= bitflag ;
517527 }
518528
519- // Update non-tracking queries
520- for ( const query of queries ) {
521- // Use checkQueryWithRelations if query has relation filters, otherwise use checkQuery
522- const match =
523- query . relationFilters && query . relationFilters . length > 0
524- ? checkQueryWithRelations ( world , query , entity )
525- : query . check ( world , entity ) ;
526- if ( match ) query . add ( entity ) ;
527- else query . remove ( world , entity ) ;
528- }
529-
530- // Update tracking queries (with event data)
531- for ( const query of trackingQueries ) {
532- // Use checkQueryTrackingWithRelations if query has relation filters, otherwise use checkQueryTracking
533- const match =
534- query . relationFilters && query . relationFilters . length > 0
535- ? checkQueryTrackingWithRelations (
536- world ,
537- query ,
538- entity ,
539- 'remove' ,
540- generationId ,
541- bitflag
542- )
543- : query . checkTracking ( world , entity , 'remove' , generationId , bitflag ) ;
544- if ( match ) query . add ( entity ) ;
545- else query . remove ( world , entity ) ;
546- }
529+ // Update non-tracking queries
530+ for ( let qi = 0 , qLen = queries . length ; qi < qLen ; qi ++ ) {
531+ const query = queries [ qi ] ;
532+ const match =
533+ query . relationFilters && query . relationFilters . length > 0
534+ ? checkQueryWithRelations ( world , query , entity )
535+ : query . check ( world , entity ) ;
536+ if ( match ) query . add ( entity ) ;
537+ else query . remove ( world , entity ) ;
538+ }
539+
540+ // Update tracking queries (with event data)
541+ for ( let qi = 0 , qLen = trackingQueries . length ; qi < qLen ; qi ++ ) {
542+ const query = trackingQueries [ qi ] ;
543+ const match =
544+ query . relationFilters && query . relationFilters . length > 0
545+ ? checkQueryTrackingWithRelations (
546+ world ,
547+ query ,
548+ entity ,
549+ 'remove' ,
550+ generationId ,
551+ bitflag
552+ )
553+ : query . checkTracking ( world , entity , 'remove' , generationId , bitflag ) ;
554+ if ( match ) query . add ( entity ) ;
555+ else query . remove ( world , entity ) ;
556+ }
547557
548558 // Remove trait from entity internally
549559 ctx . entityTraits . get ( entity ) ! . delete ( trait ) ;
0 commit comments