@@ -79,6 +79,44 @@ export function triggerLifecycleHooks(
7979 triggerMultiComponentHooks ( ctx , entityId , addedComponents , removedComponents , oldArchetype , newArchetype ) ;
8080}
8181
82+ /**
83+ * Fast path for triggering lifecycle hooks when an entity is being deleted.
84+ * This avoids unnecessary archetype lookups and on_set checks since the entity
85+ * is being completely removed.
86+ */
87+ export function triggerRemoveHooksForEntityDeletion (
88+ ctx : HooksContext ,
89+ entityId : EntityId ,
90+ removedComponents : Map < EntityId < any > , any > ,
91+ oldArchetype : Archetype ,
92+ ) : void {
93+ if ( removedComponents . size === 0 ) return ;
94+
95+ // Trigger legacy hooks for removed components
96+ invokeHooksForComponents ( ctx . hooks , entityId , removedComponents , "on_remove" ) ;
97+
98+ // Trigger multi-component hooks - only on_remove since entity is being deleted
99+ for ( const entry of oldArchetype . matchingMultiHooks ) {
100+ const { hook, requiredComponents, componentTypes } = entry ;
101+ if ( ! hook . on_remove ) continue ;
102+
103+ // Check if any required component was removed
104+ const anyRequiredRemoved = requiredComponents . some ( ( c ) => anyComponentMatches ( removedComponents , c ) ) ;
105+ if ( ! anyRequiredRemoved ) continue ;
106+
107+ // For entity deletion, we know:
108+ // 1. All components are being removed, so entity "had" all required components
109+ // 2. Entity will no longer match after deletion
110+ // Just need to verify the entity actually had all required components before
111+ const hadAllRequired = requiredComponents . every ( ( c ) => anyComponentMatches ( removedComponents , c ) ) ;
112+ if ( ! hadAllRequired ) continue ;
113+
114+ // Collect component values from removedComponents directly (no entity lookup needed)
115+ const components = collectComponentsFromRemoved ( componentTypes , removedComponents ) ;
116+ hook . on_remove ( entityId , ...components ) ;
117+ }
118+ }
119+
82120function invokeHooksForComponents (
83121 hooks : HooksMap ,
84122 entityId : EntityId ,
@@ -262,3 +300,56 @@ function collectMultiHookComponentsWithRemoved(
262300 return match ? match [ 1 ] : ctx . get ( entityId , compId ) ;
263301 } ) ;
264302}
303+
304+ /**
305+ * Collect component values directly from removedComponents map.
306+ * Used for entity deletion fast path where the entity no longer exists.
307+ */
308+ function collectComponentsFromRemoved (
309+ componentTypes : readonly ComponentType < any > [ ] ,
310+ removedComponents : Map < EntityId < any > , any > ,
311+ ) : any [ ] {
312+ return componentTypes . map ( ( ct ) => {
313+ if ( isOptionalEntityId ( ct ) ) {
314+ const optionalId = ct . optional ;
315+
316+ if ( isWildcardRelationId ( optionalId ) ) {
317+ const result = collectWildcardFromRemoved ( optionalId , removedComponents ) ;
318+ return result . length > 0 ? { value : result } : undefined ;
319+ }
320+
321+ const match = findMatchingComponent ( removedComponents , optionalId ) ;
322+ return match ? { value : match [ 1 ] } : undefined ;
323+ }
324+
325+ const compId = ct as EntityId < any > ;
326+
327+ if ( isWildcardRelationId ( compId ) ) {
328+ return collectWildcardFromRemoved ( compId , removedComponents ) ;
329+ }
330+
331+ const match = findMatchingComponent ( removedComponents , compId ) ;
332+ return match ? match [ 1 ] : undefined ;
333+ } ) ;
334+ }
335+
336+ /**
337+ * Collect all matching wildcard relation data from removed components.
338+ */
339+ function collectWildcardFromRemoved (
340+ wildcardId : EntityId < any > ,
341+ removedComponents : Map < EntityId < any > , any > ,
342+ ) : [ EntityId , any ] [ ] {
343+ const result : [ EntityId , any ] [ ] = [ ] ;
344+
345+ for ( const [ removedCompId , removedValue ] of removedComponents . entries ( ) ) {
346+ if ( componentMatchesHookType ( removedCompId , wildcardId ) ) {
347+ const targetId = getTargetIdFromRelationId ( removedCompId ) ;
348+ if ( targetId !== undefined ) {
349+ result . push ( [ targetId , removedValue ] ) ;
350+ }
351+ }
352+ }
353+
354+ return result ;
355+ }
0 commit comments