11import { ComponentChangeset } from "../commands/changeset" ;
22import { CommandBuffer , type Command } from "../commands/command-buffer" ;
3- import { serializeQueryFilter , type QueryFilter } from "../query/filter" ;
3+ import { matchesFilter , serializeQueryFilter , type QueryFilter } from "../query/filter" ;
44import { Query } from "../query/query" ;
55import { getOrCompute } from "../utils/utils" ;
66import { Archetype , MISSING_COMPONENT } from "./archetype" ;
@@ -663,12 +663,14 @@ export class World {
663663 * @overload hook<const T extends readonly ComponentType<any>[]>(
664664 * componentTypes: T,
665665 * hook: LifecycleHook<T> | LifecycleCallback<T>,
666+ * filter?: QueryFilter,
666667 * ): () => void
667668 * Registers a hook for multiple component types.
668- * The hook is triggered when all required components change together .
669+ * The hook is triggered when entities enter/exit the matching set .
669670 *
670671 * @param componentTypesOrSingle - A single component type or an array of component types
671672 * @param hook - Either a hook object with on_init/on_set/on_remove handlers, or a callback function
673+ * @param filter - Optional filter, only applied to array overload
672674 * @returns A function that unsubscribes the hook when called
673675 *
674676 * @throws {Error } If no required components are specified in array overload
@@ -686,15 +688,26 @@ export class World {
686688 * const unsubscribe = world.hook([Position], (event, entityId, position) => {
687689 * if (event === "init") console.log("Initialized");
688690 * });
691+ *
692+ * // With filter
693+ * const unsubscribe2 = world.hook(
694+ * [Position, Velocity],
695+ * {
696+ * on_set: (entityId, position, velocity) => console.log(entityId, position, velocity),
697+ * },
698+ * { negativeComponentTypes: [Disabled] },
699+ * );
689700 */
690701 hook < T > ( componentType : EntityId < T > , hook : LegacyLifecycleHook < T > | LegacyLifecycleCallback < T > ) : ( ) => void ;
691702 hook < const T extends readonly ComponentType < any > [ ] > (
692703 componentTypes : T ,
693704 hook : LifecycleHook < T > | LifecycleCallback < T > ,
705+ filter ?: QueryFilter ,
694706 ) : ( ) => void ;
695707 hook (
696708 componentTypesOrSingle : EntityId < any > | readonly ComponentType < any > [ ] ,
697709 hook : LegacyLifecycleHook < any > | LifecycleHook < any > | LegacyLifecycleCallback < any > | LifecycleCallback < any > ,
710+ filter ?: QueryFilter ,
698711 ) : ( ) => void {
699712 // Normalize callback functions to hook objects
700713 if ( typeof hook === "function" ) {
@@ -735,6 +748,7 @@ export class World {
735748 componentTypes,
736749 requiredComponents,
737750 optionalComponents,
751+ filter : filter || { } ,
738752 hook : hook as LifecycleHook < any > ,
739753 } ;
740754 this . hooks . add ( entry ) ;
@@ -748,8 +762,8 @@ export class World {
748762
749763 const multiHook = hook as LifecycleHook < any > ;
750764 if ( multiHook . on_init !== undefined ) {
751- const matchingArchetypes = this . getMatchingArchetypes ( requiredComponents ) ;
752- for ( const archetype of matchingArchetypes ) {
765+ for ( const archetype of this . archetypes ) {
766+ if ( ! this . archetypeMatchesHook ( archetype , entry ) ) continue ;
753767 for ( const entityId of archetype . getEntities ( ) ) {
754768 const components = collectMultiHookComponents ( this . createHooksContext ( ) , entityId , componentTypes ) ;
755769 multiHook . on_init ( entityId , ...components ) ;
@@ -1296,14 +1310,16 @@ export class World {
12961310 }
12971311
12981312 private archetypeMatchesHook ( archetype : Archetype , entry : LifecycleHookEntry ) : boolean {
1299- return entry . requiredComponents . every ( ( c : EntityId < any > ) => {
1300- if ( isWildcardRelationId ( c ) ) {
1301- if ( isDontFragmentWildcard ( c ) ) return true ;
1302- const componentId = getComponentIdFromRelationId ( c ) ;
1303- return componentId !== undefined && archetype . hasRelationWithComponentId ( componentId ) ;
1304- }
1305- return archetype . componentTypeSet . has ( c ) || isDontFragmentRelation ( c ) ;
1306- } ) ;
1313+ return (
1314+ entry . requiredComponents . every ( ( c : EntityId < any > ) => {
1315+ if ( isWildcardRelationId ( c ) ) {
1316+ if ( isDontFragmentWildcard ( c ) ) return true ;
1317+ const componentId = getComponentIdFromRelationId ( c ) ;
1318+ return componentId !== undefined && archetype . hasRelationWithComponentId ( componentId ) ;
1319+ }
1320+ return archetype . componentTypeSet . has ( c ) || isDontFragmentRelation ( c ) ;
1321+ } ) && matchesFilter ( archetype , entry . filter )
1322+ ) ;
13071323 }
13081324
13091325 private archetypeReferencesEntity ( archetype : Archetype , entityId : EntityId ) : boolean {
0 commit comments