@@ -103,10 +103,9 @@ export function removeMatchingRelations(
103103 }
104104
105105 // Check dontFragment relations stored on entity
106- const entityData = archetype . getEntity ( entityId ) ;
107- if ( entityData ) {
108- for ( const [ componentType ] of entityData ) {
109- if ( archetype . componentTypeSet . has ( componentType ) ) continue ;
106+ const dontFragmentData = archetype . getEntityDontFragmentRelations ( entityId ) ;
107+ if ( dontFragmentData ) {
108+ for ( const componentType of dontFragmentData . keys ( ) ) {
110109 if ( getComponentIdFromRelationId ( componentType ) === baseComponentId ) {
111110 changeset . delete ( componentType ) ;
112111 }
@@ -140,14 +139,9 @@ export function maybeRemoveWildcardMarker(
140139 }
141140
142141 const wildcardMarker = relation ( componentId , "*" ) ;
143- const entityData = archetype . getEntity ( entityId ) ;
144- if ( ! entityData ) {
145- changeset . delete ( wildcardMarker ) ;
146- return ;
147- }
148142
149143 // Check if there are any other relations with the same component ID
150- for ( const [ otherComponentType ] of entityData ) {
144+ for ( const otherComponentType of archetype . componentTypes ) {
151145 if ( otherComponentType === removedComponentType ) continue ;
152146 if ( otherComponentType === wildcardMarker ) continue ;
153147 if ( changeset . removes . has ( otherComponentType ) ) continue ;
@@ -157,52 +151,99 @@ export function maybeRemoveWildcardMarker(
157151 }
158152 }
159153
154+ const dontFragmentData = archetype . getEntityDontFragmentRelations ( entityId ) ;
155+ if ( dontFragmentData ) {
156+ for ( const otherComponentType of dontFragmentData . keys ( ) ) {
157+ if ( otherComponentType === removedComponentType ) continue ;
158+ if ( changeset . removes . has ( otherComponentType ) ) continue ;
159+
160+ if ( getComponentIdFromRelationId ( otherComponentType ) === componentId ) {
161+ return ; // Found another relation, keep the marker
162+ }
163+ }
164+ }
165+
160166 changeset . delete ( wildcardMarker ) ;
161167}
162168
169+ function hasEntityComponent ( archetype : Archetype , entityId : EntityId , componentType : EntityId < any > ) : boolean {
170+ if ( archetype . componentTypeSet . has ( componentType ) ) {
171+ return true ;
172+ }
173+
174+ return archetype . getEntityDontFragmentRelations ( entityId ) ?. has ( componentType ) ?? false ;
175+ }
176+
177+ function pruneMissingRemovals ( changeset : ComponentChangeset , archetype : Archetype , entityId : EntityId ) : void {
178+ for ( const componentType of Array . from ( changeset . removes ) ) {
179+ if ( ! hasEntityComponent ( archetype , entityId , componentType ) ) {
180+ changeset . removes . delete ( componentType ) ;
181+ }
182+ }
183+ }
184+
185+ function hasArchetypeStructuralChange ( changeset : ComponentChangeset , currentArchetype : Archetype ) : boolean {
186+ for ( const componentType of changeset . removes ) {
187+ if ( ! isDontFragmentRelation ( componentType ) && currentArchetype . componentTypeSet . has ( componentType ) ) {
188+ return true ;
189+ }
190+ }
191+
192+ for ( const componentType of changeset . adds . keys ( ) ) {
193+ if ( ! isDontFragmentRelation ( componentType ) && ! currentArchetype . componentTypeSet . has ( componentType ) ) {
194+ return true ;
195+ }
196+ }
197+
198+ return false ;
199+ }
200+
201+ function buildFinalRegularComponentTypes ( currentArchetype : Archetype , changeset : ComponentChangeset ) : EntityId < any > [ ] {
202+ const finalRegularTypes = new Set ( currentArchetype . componentTypes ) ;
203+
204+ for ( const componentType of changeset . removes ) {
205+ if ( ! isDontFragmentRelation ( componentType ) ) {
206+ finalRegularTypes . delete ( componentType ) ;
207+ }
208+ }
209+
210+ for ( const componentType of changeset . adds . keys ( ) ) {
211+ if ( ! isDontFragmentRelation ( componentType ) ) {
212+ finalRegularTypes . add ( componentType ) ;
213+ }
214+ }
215+
216+ return Array . from ( finalRegularTypes ) ;
217+ }
218+
163219export function applyChangeset (
164220 ctx : CommandProcessorContext ,
165221 entityId : EntityId ,
166222 currentArchetype : Archetype ,
167223 changeset : ComponentChangeset ,
168224 entityToArchetype : Map < EntityId , Archetype > ,
169225) : { removedComponents : Map < EntityId < any > , any > ; newArchetype : Archetype } {
170- const currentEntityData = currentArchetype . getEntity ( entityId ) ;
171- const allCurrentComponentTypes = currentEntityData
172- ? Array . from ( currentEntityData . keys ( ) )
173- : currentArchetype . componentTypes ;
174-
175- const finalComponentTypes = changeset . getFinalComponentTypes ( allCurrentComponentTypes ) ;
176226 const removedComponents = new Map < EntityId < any > , any > ( ) ;
177-
178- if ( finalComponentTypes ) {
179- // Check if archetype-affecting components actually changed
180- // (dontFragment components don't affect archetype signature)
181- const currentRegularTypes = filterRegularComponentTypes ( allCurrentComponentTypes ) ;
182- const finalRegularTypes = filterRegularComponentTypes ( finalComponentTypes ) ;
183- const archetypeChanged = ! areComponentTypesEqual ( currentRegularTypes , finalRegularTypes ) ;
184-
185- if ( archetypeChanged ) {
186- // Move to new archetype (regular components changed)
187- const newArchetype = moveEntityToNewArchetype (
188- ctx ,
189- entityId ,
190- currentArchetype ,
191- finalComponentTypes ,
192- changeset ,
193- removedComponents ,
194- entityToArchetype ,
195- ) ;
196- return { removedComponents, newArchetype } ;
197- } else {
198- // Only dontFragment components changed, stay in same archetype
199- updateEntityInSameArchetype ( ctx , entityId , currentArchetype , changeset , removedComponents ) ;
200- }
201- } else {
202- // Update in same archetype
203- updateEntityInSameArchetype ( ctx , entityId , currentArchetype , changeset , removedComponents ) ;
227+ pruneMissingRemovals ( changeset , currentArchetype , entityId ) ;
228+ const archetypeChanged = hasArchetypeStructuralChange ( changeset , currentArchetype ) ;
229+
230+ if ( archetypeChanged ) {
231+ const finalRegularTypes = buildFinalRegularComponentTypes ( currentArchetype , changeset ) ;
232+ const newArchetype = moveEntityToNewArchetype (
233+ ctx ,
234+ entityId ,
235+ currentArchetype ,
236+ finalRegularTypes ,
237+ changeset ,
238+ removedComponents ,
239+ entityToArchetype ,
240+ ) ;
241+ return { removedComponents, newArchetype } ;
204242 }
205243
244+ // No archetype move needed: only component payload updates and/or dontFragment relation updates.
245+ updateEntityInSameArchetype ( ctx , entityId , currentArchetype , changeset , removedComponents ) ;
246+
206247 return { removedComponents, newArchetype : currentArchetype } ;
207248}
208249
0 commit comments