iris-ecs 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +721 -0
- package/dist/actions.d.ts +43 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +35 -0
- package/dist/actions.js.map +1 -0
- package/dist/archetype.d.ts +194 -0
- package/dist/archetype.d.ts.map +1 -0
- package/dist/archetype.js +412 -0
- package/dist/archetype.js.map +1 -0
- package/dist/component.d.ts +89 -0
- package/dist/component.d.ts.map +1 -0
- package/dist/component.js +237 -0
- package/dist/component.js.map +1 -0
- package/dist/encoding.d.ts +204 -0
- package/dist/encoding.d.ts.map +1 -0
- package/dist/encoding.js +215 -0
- package/dist/encoding.js.map +1 -0
- package/dist/entity.d.ts +129 -0
- package/dist/entity.d.ts.map +1 -0
- package/dist/entity.js +243 -0
- package/dist/entity.js.map +1 -0
- package/dist/event.d.ts +237 -0
- package/dist/event.d.ts.map +1 -0
- package/dist/event.js +293 -0
- package/dist/event.js.map +1 -0
- package/dist/filters.d.ts +121 -0
- package/dist/filters.d.ts.map +1 -0
- package/dist/filters.js +202 -0
- package/dist/filters.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/name.d.ts +70 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +172 -0
- package/dist/name.js.map +1 -0
- package/dist/observer.d.ts +83 -0
- package/dist/observer.d.ts.map +1 -0
- package/dist/observer.js +62 -0
- package/dist/observer.js.map +1 -0
- package/dist/query.d.ts +198 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +299 -0
- package/dist/query.js.map +1 -0
- package/dist/registry.d.ts +118 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +112 -0
- package/dist/registry.js.map +1 -0
- package/dist/relation.d.ts +60 -0
- package/dist/relation.d.ts.map +1 -0
- package/dist/relation.js +171 -0
- package/dist/relation.js.map +1 -0
- package/dist/removal.d.ts +27 -0
- package/dist/removal.d.ts.map +1 -0
- package/dist/removal.js +66 -0
- package/dist/removal.js.map +1 -0
- package/dist/resource.d.ts +78 -0
- package/dist/resource.d.ts.map +1 -0
- package/dist/resource.js +86 -0
- package/dist/resource.js.map +1 -0
- package/dist/scheduler.d.ts +106 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +204 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/schema.d.ts +117 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +113 -0
- package/dist/schema.js.map +1 -0
- package/dist/world.d.ts +172 -0
- package/dist/world.d.ts.map +1 -0
- package/dist/world.js +127 -0
- package/dist/world.js.map +1 -0
- package/package.json +52 -0
package/dist/observer.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// Observer API
|
|
3
|
+
// ============================================================================
|
|
4
|
+
/**
|
|
5
|
+
* Registers a callback to be invoked when an event of the specified type is fired.
|
|
6
|
+
*
|
|
7
|
+
* @param world - The world instance containing observer state
|
|
8
|
+
* @param eventType - The event type to listen for
|
|
9
|
+
* @param callback - Function to invoke when the event fires
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* registerObserverCallback(world, "onAdd", (entity, componentId, value) => {
|
|
14
|
+
* console.log(`Component ${componentId} added to entity ${entity}`);
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function registerObserverCallback(world, eventType, callback) {
|
|
19
|
+
world.observers[eventType].callbacks.push(callback);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Removes a previously registered callback for the specified event type.
|
|
23
|
+
*
|
|
24
|
+
* @param world - The world instance containing observer state
|
|
25
|
+
* @param eventType - The event type to stop listening for
|
|
26
|
+
* @param callback - The exact callback reference to remove
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const handler = (entity, componentId, value) => { ... };
|
|
31
|
+
* registerObserverCallback(world, "onAdd", handler);
|
|
32
|
+
* // Later:
|
|
33
|
+
* unregisterObserverCallback(world, "onAdd", handler);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export function unregisterObserverCallback(world, eventType, callback) {
|
|
37
|
+
const meta = world.observers[eventType];
|
|
38
|
+
const idx = meta.callbacks.indexOf(callback);
|
|
39
|
+
if (idx !== -1) {
|
|
40
|
+
meta.callbacks.splice(idx, 1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Dispatches an event to all registered callbacks for the specified event type.
|
|
45
|
+
*
|
|
46
|
+
* @param world - The world instance containing observer state
|
|
47
|
+
* @param eventType - The event type to dispatch
|
|
48
|
+
* @param args - Arguments to pass to each callback (varies by event type)
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* fireObserverEvent(world, "onAdd", entity, componentId, componentValue);
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function fireObserverEvent(world, eventType, ...args) {
|
|
56
|
+
const meta = world.observers[eventType];
|
|
57
|
+
// Iterate in reverse so callbacks can safely unregister themselves during dispatch
|
|
58
|
+
for (let i = meta.callbacks.length - 1; i >= 0; i--) {
|
|
59
|
+
meta.callbacks[i](...args);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=observer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observer.js","sourceRoot":"","sources":["../src/observer.ts"],"names":[],"mappings":"AA+CA,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,wBAAwB,CAAsB,KAAY,EAAE,SAAY,EAAE,QAAqB;IAC7G,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,0BAA0B,CACxC,KAAY,EACZ,SAAY,EACZ,QAAqB;IAErB,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE7C,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QACf,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,iBAAiB,CAAsB,KAAY,EAAE,SAAY,EAAE,GAAG,IAAsB;IAC1G,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IAExC,mFAAmF;IACnF,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,IAAI,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC"}
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import type { EntityId } from "./encoding.js";
|
|
2
|
+
import type { FilterMeta } from "./filters.js";
|
|
3
|
+
import type { Observer } from "./observer.js";
|
|
4
|
+
import type { World } from "./world.js";
|
|
5
|
+
/**
|
|
6
|
+
* Query metadata for registry caching.
|
|
7
|
+
*
|
|
8
|
+
* Stores required and excluded components with reference to underlying filter.
|
|
9
|
+
*/
|
|
10
|
+
export type QueryMeta = {
|
|
11
|
+
/**
|
|
12
|
+
* Required components.
|
|
13
|
+
*/
|
|
14
|
+
include: EntityId[];
|
|
15
|
+
/**
|
|
16
|
+
* Excluded components.
|
|
17
|
+
*/
|
|
18
|
+
exclude: EntityId[];
|
|
19
|
+
/**
|
|
20
|
+
* Direct reference to underlying filter.
|
|
21
|
+
*/
|
|
22
|
+
filter: FilterMeta;
|
|
23
|
+
/**
|
|
24
|
+
* Observer callback for filter destruction.
|
|
25
|
+
*/
|
|
26
|
+
onFilterDestroy: Observer<"filterDestroyed">;
|
|
27
|
+
/**
|
|
28
|
+
* Components with added() modifier.
|
|
29
|
+
*/
|
|
30
|
+
added: EntityId[];
|
|
31
|
+
/**
|
|
32
|
+
* Components with changed() modifier.
|
|
33
|
+
*/
|
|
34
|
+
changed: EntityId[];
|
|
35
|
+
/**
|
|
36
|
+
* Execution tick tracking for change detection.
|
|
37
|
+
*/
|
|
38
|
+
lastTick: {
|
|
39
|
+
/**
|
|
40
|
+
* Tick when query last executed outside any system.
|
|
41
|
+
*/
|
|
42
|
+
self: number;
|
|
43
|
+
/**
|
|
44
|
+
* Per-system execution ticks: systemId -> tick
|
|
45
|
+
*/
|
|
46
|
+
bySystemId: Map<string, number>;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
export type ModifierType = "not" | "added" | "changed";
|
|
50
|
+
export type NotModifier = {
|
|
51
|
+
type: "not";
|
|
52
|
+
componentId: EntityId;
|
|
53
|
+
};
|
|
54
|
+
export type AddedModifier = {
|
|
55
|
+
type: "added";
|
|
56
|
+
componentId: EntityId;
|
|
57
|
+
};
|
|
58
|
+
export type ChangedModifier = {
|
|
59
|
+
type: "changed";
|
|
60
|
+
componentId: EntityId;
|
|
61
|
+
};
|
|
62
|
+
export type QueryModifier = NotModifier | AddedModifier | ChangedModifier;
|
|
63
|
+
/**
|
|
64
|
+
* Create exclusion modifier for query.
|
|
65
|
+
*
|
|
66
|
+
* @param componentId - Component to exclude from query results
|
|
67
|
+
* @returns Not modifier
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* fetchEntities(world, Position, not(Dead))
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function not(componentId: EntityId): NotModifier;
|
|
75
|
+
/**
|
|
76
|
+
* Create added modifier for change detection.
|
|
77
|
+
*
|
|
78
|
+
* Matches entities where component was added since last query execution.
|
|
79
|
+
*
|
|
80
|
+
* @param componentId - Component to check for addition
|
|
81
|
+
* @returns Added modifier
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* for (const entity of fetchEntities(world, added(Enemy))) { ... }
|
|
85
|
+
*/
|
|
86
|
+
export declare function added(componentId: EntityId): AddedModifier;
|
|
87
|
+
/**
|
|
88
|
+
* Create changed modifier for change detection.
|
|
89
|
+
*
|
|
90
|
+
* Matches entities where component was modified or added since last query execution.
|
|
91
|
+
*
|
|
92
|
+
* @param componentId - Component to check for changes
|
|
93
|
+
* @returns Changed modifier
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* for (const entity of fetchEntities(world, changed(Health))) { ... }
|
|
97
|
+
*/
|
|
98
|
+
export declare function changed(componentId: EntityId): ChangedModifier;
|
|
99
|
+
/**
|
|
100
|
+
* Hash query terms to unique string ID for cache lookup.
|
|
101
|
+
*
|
|
102
|
+
* @param include - Component IDs that must be present
|
|
103
|
+
* @param exclude - Component IDs that must not be present
|
|
104
|
+
* @param added - Component IDs to check for recent addition
|
|
105
|
+
* @param changed - Component IDs to check for recent modification
|
|
106
|
+
* @returns Query ID in format "+include|-exclude|~+added|~>changed"
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```typescript
|
|
110
|
+
* const id = hashQuery([Position, Velocity], [Dead], [], []);
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function hashQuery(include: EntityId[], exclude: EntityId[], added: EntityId[], changed: EntityId[]): string;
|
|
114
|
+
/**
|
|
115
|
+
* Ensure query exists in registry, creating if necessary.
|
|
116
|
+
*
|
|
117
|
+
* @param world - World instance
|
|
118
|
+
* @param terms - Components and modifiers
|
|
119
|
+
* @returns Query metadata
|
|
120
|
+
* @throws {Error} If no included components (query must match something)
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* const query = ensureQuery(world, Position, Velocity, not(Dead));
|
|
124
|
+
*/
|
|
125
|
+
export declare function ensureQuery(world: World, ...terms: (EntityId | QueryModifier)[]): QueryMeta;
|
|
126
|
+
/**
|
|
127
|
+
* Fetch entities using pre-registered query metadata.
|
|
128
|
+
*
|
|
129
|
+
* Filters by change modifiers (added/changed) when present and updates
|
|
130
|
+
* lastTick after iteration for per-query/per-system change tracking.
|
|
131
|
+
*
|
|
132
|
+
* @param world - World instance
|
|
133
|
+
* @param queryMeta - Query metadata from ensureQuery()
|
|
134
|
+
* @returns Entity IDs in backward order (safe for deletion during iteration)
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const query = ensureQuery(world, Position, Velocity);
|
|
139
|
+
* for (const entity of fetchEntitiesWithQuery(world, query)) {
|
|
140
|
+
* // Process entity
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export declare function fetchEntitiesWithQuery(world: World, queryMeta: QueryMeta): IterableIterator<EntityId>;
|
|
145
|
+
/**
|
|
146
|
+
* Destroy query and clean up associated resources.
|
|
147
|
+
*
|
|
148
|
+
* Unregisters observer callbacks and removes from query registry.
|
|
149
|
+
*
|
|
150
|
+
* @param world - World instance
|
|
151
|
+
* @param queryMeta - Query metadata to destroy
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* const query = ensureQuery(world, Position);
|
|
156
|
+
* // ... use query ...
|
|
157
|
+
* destroyQuery(world, query);
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
export declare function destroyQuery(world: World, queryMeta: QueryMeta): void;
|
|
161
|
+
/**
|
|
162
|
+
* Fetch entities matching components and modifiers.
|
|
163
|
+
*
|
|
164
|
+
* Iterates backward for safe entity destruction during iteration.
|
|
165
|
+
* Creates/reuses cached query internally.
|
|
166
|
+
*
|
|
167
|
+
* @param world - World instance
|
|
168
|
+
* @param terms - Component IDs and query modifiers (not, added, changed)
|
|
169
|
+
* @returns Entity IDs in deletion-safe order
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* for (const entity of fetchEntities(world, Position, Velocity, not(Dead))) {
|
|
174
|
+
* const pos = get(world, entity, Position);
|
|
175
|
+
* // Entity can be safely destroyed here
|
|
176
|
+
* }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export declare function fetchEntities(world: World, ...terms: (EntityId | QueryModifier)[]): IterableIterator<EntityId>;
|
|
180
|
+
/**
|
|
181
|
+
* Fetch first entity matching components and modifiers.
|
|
182
|
+
*
|
|
183
|
+
* Useful for singleton patterns or when only one match is expected.
|
|
184
|
+
*
|
|
185
|
+
* @param world - World instance
|
|
186
|
+
* @param terms - Component IDs and query modifiers (not, added, changed)
|
|
187
|
+
* @returns First matching entity ID, or undefined if no matches
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* const player = fetchFirstEntity(world, Player, not(Dead));
|
|
192
|
+
* if (player !== undefined) {
|
|
193
|
+
* const health = get(world, player, Health);
|
|
194
|
+
* }
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
export declare function fetchFirstEntity(world: World, ...terms: (EntityId | QueryModifier)[]): EntityId | undefined;
|
|
198
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAMxC;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB;;OAEG;IACH,OAAO,EAAE,QAAQ,EAAE,CAAC;IAEpB;;OAEG;IACH,OAAO,EAAE,QAAQ,EAAE,CAAC;IAEpB;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC;IAEnB;;OAEG;IACH,eAAe,EAAE,QAAQ,CAAC,iBAAiB,CAAC,CAAC;IAE7C;;OAEG;IACH,KAAK,EAAE,QAAQ,EAAE,CAAC;IAElB;;OAEG;IACH,OAAO,EAAE,QAAQ,EAAE,CAAC;IAEpB;;OAEG;IACH,QAAQ,EAAE;QACR;;WAEG;QACH,IAAI,EAAE,MAAM,CAAC;QACb;;WAEG;QACH,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACjC,CAAC;CACH,CAAC;AAMF,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,OAAO,GAAG,SAAS,CAAC;AACvD,MAAM,MAAM,WAAW,GAAG;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,WAAW,EAAE,QAAQ,CAAA;CAAE,CAAC;AACjE,MAAM,MAAM,aAAa,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,QAAQ,CAAA;CAAE,CAAC;AACrE,MAAM,MAAM,eAAe,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,WAAW,EAAE,QAAQ,CAAA;CAAE,CAAC;AACzE,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,aAAa,GAAG,eAAe,CAAC;AAE1E;;;;;;;;;;GAUG;AACH,wBAAgB,GAAG,CAAC,WAAW,EAAE,QAAQ,GAAG,WAAW,CAEtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,WAAW,EAAE,QAAQ,GAAG,aAAa,CAE1D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,WAAW,EAAE,QAAQ,GAAG,eAAe,CAE9D;AAaD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,MAAM,CAKlH;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,QAAQ,GAAG,aAAa,CAAC,EAAE,GAAG,SAAS,CAuE3F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAiB,sBAAsB,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAyEtG;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAMrE;AAMD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAiB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,QAAQ,GAAG,aAAa,CAAC,EAAE,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAI/G;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,CAAC,QAAQ,GAAG,aAAa,CAAC,EAAE,GAAG,QAAQ,GAAG,SAAS,CAQ3G"}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { ensureFilter, iterateFilterEntities } from "./filters.js";
|
|
2
|
+
import { registerObserverCallback, unregisterObserverCallback } from "./observer.js";
|
|
3
|
+
/**
|
|
4
|
+
* Create exclusion modifier for query.
|
|
5
|
+
*
|
|
6
|
+
* @param componentId - Component to exclude from query results
|
|
7
|
+
* @returns Not modifier
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* fetchEntities(world, Position, not(Dead))
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export function not(componentId) {
|
|
15
|
+
return { type: "not", componentId };
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Create added modifier for change detection.
|
|
19
|
+
*
|
|
20
|
+
* Matches entities where component was added since last query execution.
|
|
21
|
+
*
|
|
22
|
+
* @param componentId - Component to check for addition
|
|
23
|
+
* @returns Added modifier
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* for (const entity of fetchEntities(world, added(Enemy))) { ... }
|
|
27
|
+
*/
|
|
28
|
+
export function added(componentId) {
|
|
29
|
+
return { type: "added", componentId };
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Create changed modifier for change detection.
|
|
33
|
+
*
|
|
34
|
+
* Matches entities where component was modified or added since last query execution.
|
|
35
|
+
*
|
|
36
|
+
* @param componentId - Component to check for changes
|
|
37
|
+
* @returns Changed modifier
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* for (const entity of fetchEntities(world, changed(Health))) { ... }
|
|
41
|
+
*/
|
|
42
|
+
export function changed(componentId) {
|
|
43
|
+
return { type: "changed", componentId };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if argument is a query modifier (not, added, changed) vs plain component ID.
|
|
47
|
+
*/
|
|
48
|
+
function isModifier(arg) {
|
|
49
|
+
return typeof arg === "object" && arg !== null && "type" in arg && "componentId" in arg;
|
|
50
|
+
}
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Query Hashing
|
|
53
|
+
// ============================================================================
|
|
54
|
+
/**
|
|
55
|
+
* Hash query terms to unique string ID for cache lookup.
|
|
56
|
+
*
|
|
57
|
+
* @param include - Component IDs that must be present
|
|
58
|
+
* @param exclude - Component IDs that must not be present
|
|
59
|
+
* @param added - Component IDs to check for recent addition
|
|
60
|
+
* @param changed - Component IDs to check for recent modification
|
|
61
|
+
* @returns Query ID in format "+include|-exclude|~+added|~>changed"
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const id = hashQuery([Position, Velocity], [Dead], [], []);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export function hashQuery(include, exclude, added, changed) {
|
|
69
|
+
// Sort to ensure consistent hashing regardless of term order
|
|
70
|
+
const join = (arr) => arr.toSorted((a, b) => a - b).join(":");
|
|
71
|
+
return `+${join(include)}|-${join(exclude)}|~+${join(added)}|~>${join(changed)}`;
|
|
72
|
+
}
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Query Registry Operations
|
|
75
|
+
// ============================================================================
|
|
76
|
+
/**
|
|
77
|
+
* Ensure query exists in registry, creating if necessary.
|
|
78
|
+
*
|
|
79
|
+
* @param world - World instance
|
|
80
|
+
* @param terms - Components and modifiers
|
|
81
|
+
* @returns Query metadata
|
|
82
|
+
* @throws {Error} If no included components (query must match something)
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const query = ensureQuery(world, Position, Velocity, not(Dead));
|
|
86
|
+
*/
|
|
87
|
+
export function ensureQuery(world, ...terms) {
|
|
88
|
+
const include = [];
|
|
89
|
+
const exclude = [];
|
|
90
|
+
const added = [];
|
|
91
|
+
const changed = [];
|
|
92
|
+
// Separate terms into categories based on modifier type
|
|
93
|
+
for (const term of terms) {
|
|
94
|
+
if (isModifier(term)) {
|
|
95
|
+
switch (term.type) {
|
|
96
|
+
case "not":
|
|
97
|
+
exclude.push(term.componentId);
|
|
98
|
+
break;
|
|
99
|
+
case "added":
|
|
100
|
+
added.push(term.componentId);
|
|
101
|
+
break;
|
|
102
|
+
case "changed":
|
|
103
|
+
changed.push(term.componentId);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
include.push(term);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Filter must include added/changed components since they must be present on entity
|
|
112
|
+
const filterInclude = include.concat(added, changed);
|
|
113
|
+
if (filterInclude.length === 0) {
|
|
114
|
+
throw new Error("Query must include at least one component");
|
|
115
|
+
}
|
|
116
|
+
const queryId = hashQuery(include, exclude, added, changed);
|
|
117
|
+
let queryMeta = world.queries.byId.get(queryId);
|
|
118
|
+
if (!queryMeta) {
|
|
119
|
+
const filterMeta = ensureFilter(world, { include: filterInclude, exclude });
|
|
120
|
+
queryMeta = {
|
|
121
|
+
include,
|
|
122
|
+
exclude,
|
|
123
|
+
added,
|
|
124
|
+
changed,
|
|
125
|
+
filter: filterMeta,
|
|
126
|
+
lastTick: {
|
|
127
|
+
self: 0,
|
|
128
|
+
bySystemId: new Map(),
|
|
129
|
+
},
|
|
130
|
+
// Callback to clean up query when its underlying filter is destroyed
|
|
131
|
+
onFilterDestroy: (destroyedFilter) => {
|
|
132
|
+
if (destroyedFilter !== filterMeta) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
// Self-cleanup: unregister callback and remove from registry
|
|
136
|
+
unregisterObserverCallback(world, "filterDestroyed", queryMeta.onFilterDestroy);
|
|
137
|
+
world.queries.byId.delete(queryId);
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
world.queries.byId.set(queryId, queryMeta);
|
|
141
|
+
// Register for filter destruction events to enable automatic cleanup
|
|
142
|
+
registerObserverCallback(world, "filterDestroyed", queryMeta.onFilterDestroy);
|
|
143
|
+
}
|
|
144
|
+
return queryMeta;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Fetch entities using pre-registered query metadata.
|
|
148
|
+
*
|
|
149
|
+
* Filters by change modifiers (added/changed) when present and updates
|
|
150
|
+
* lastTick after iteration for per-query/per-system change tracking.
|
|
151
|
+
*
|
|
152
|
+
* @param world - World instance
|
|
153
|
+
* @param queryMeta - Query metadata from ensureQuery()
|
|
154
|
+
* @returns Entity IDs in backward order (safe for deletion during iteration)
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* const query = ensureQuery(world, Position, Velocity);
|
|
159
|
+
* for (const entity of fetchEntitiesWithQuery(world, query)) {
|
|
160
|
+
* // Process entity
|
|
161
|
+
* }
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export function* fetchEntitiesWithQuery(world, queryMeta) {
|
|
165
|
+
const hasChangeModifiers = queryMeta.added.length > 0 || queryMeta.changed.length > 0;
|
|
166
|
+
// Fast path: no change modifiers
|
|
167
|
+
if (!hasChangeModifiers) {
|
|
168
|
+
yield* iterateFilterEntities(queryMeta.filter);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
// Slow path: filter by change detection using archetype-local tick arrays.
|
|
172
|
+
// Each component tracks when it was added/changed per-entity via tick timestamps.
|
|
173
|
+
const { systemId, tick } = world.execution;
|
|
174
|
+
// Get lastTick for this execution context (global or per-system)
|
|
175
|
+
const lastTick = systemId === null ? queryMeta.lastTick.self : (queryMeta.lastTick.bySystemId.get(systemId) ?? 0);
|
|
176
|
+
const archetypes = queryMeta.filter.archetypes;
|
|
177
|
+
// Pre-allocated arrays reused across archetypes to avoid allocation in hot loop
|
|
178
|
+
const addedTickArrays = [];
|
|
179
|
+
const changedTickArrays = [];
|
|
180
|
+
// Use try/finally to ensure lastTick updates even on early exit (break/return/throw).
|
|
181
|
+
try {
|
|
182
|
+
for (let a = 0; a < archetypes.length; a++) {
|
|
183
|
+
const archetype = archetypes[a];
|
|
184
|
+
const entities = archetype.entities;
|
|
185
|
+
// Pre-fetch tick arrays for this archetype (one Map lookup per component per archetype)
|
|
186
|
+
addedTickArrays.length = 0;
|
|
187
|
+
for (let j = 0; j < queryMeta.added.length; j++) {
|
|
188
|
+
const ticks = archetype.ticks.get(queryMeta.added[j]);
|
|
189
|
+
if (ticks)
|
|
190
|
+
addedTickArrays.push(ticks.added);
|
|
191
|
+
}
|
|
192
|
+
changedTickArrays.length = 0;
|
|
193
|
+
for (let j = 0; j < queryMeta.changed.length; j++) {
|
|
194
|
+
const ticks = archetype.ticks.get(queryMeta.changed[j]);
|
|
195
|
+
if (ticks)
|
|
196
|
+
changedTickArrays.push(ticks.changed);
|
|
197
|
+
}
|
|
198
|
+
// Iterate entities backward (deletion-safe)
|
|
199
|
+
entityLoop: for (let i = entities.length - 1; i >= 0; i--) {
|
|
200
|
+
const entityId = entities[i];
|
|
201
|
+
// Check added modifiers: skip if component wasn't added in (lastTick, tick] range
|
|
202
|
+
for (let j = 0; j < addedTickArrays.length; j++) {
|
|
203
|
+
const addedTick = addedTickArrays[j][i];
|
|
204
|
+
if (addedTick <= lastTick || addedTick > tick) {
|
|
205
|
+
continue entityLoop;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Check changed modifiers: skip if component wasn't modified in (lastTick, tick] range
|
|
209
|
+
for (let j = 0; j < changedTickArrays.length; j++) {
|
|
210
|
+
const changedTick = changedTickArrays[j][i];
|
|
211
|
+
if (changedTick <= lastTick || changedTick > tick) {
|
|
212
|
+
continue entityLoop;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
yield entityId;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
// Update lastTick after iteration completes (or on break/return/throw).
|
|
221
|
+
// This ensures subsequent iterations only see changes since this execution.
|
|
222
|
+
if (systemId === null) {
|
|
223
|
+
queryMeta.lastTick.self = tick;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
queryMeta.lastTick.bySystemId.set(systemId, tick);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Destroy query and clean up associated resources.
|
|
232
|
+
*
|
|
233
|
+
* Unregisters observer callbacks and removes from query registry.
|
|
234
|
+
*
|
|
235
|
+
* @param world - World instance
|
|
236
|
+
* @param queryMeta - Query metadata to destroy
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* ```typescript
|
|
240
|
+
* const query = ensureQuery(world, Position);
|
|
241
|
+
* // ... use query ...
|
|
242
|
+
* destroyQuery(world, query);
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
export function destroyQuery(world, queryMeta) {
|
|
246
|
+
const queryId = hashQuery(queryMeta.include, queryMeta.exclude, queryMeta.added, queryMeta.changed);
|
|
247
|
+
unregisterObserverCallback(world, "filterDestroyed", queryMeta.onFilterDestroy);
|
|
248
|
+
world.queries.byId.delete(queryId);
|
|
249
|
+
}
|
|
250
|
+
// ============================================================================
|
|
251
|
+
// Query Iteration
|
|
252
|
+
// ============================================================================
|
|
253
|
+
/**
|
|
254
|
+
* Fetch entities matching components and modifiers.
|
|
255
|
+
*
|
|
256
|
+
* Iterates backward for safe entity destruction during iteration.
|
|
257
|
+
* Creates/reuses cached query internally.
|
|
258
|
+
*
|
|
259
|
+
* @param world - World instance
|
|
260
|
+
* @param terms - Component IDs and query modifiers (not, added, changed)
|
|
261
|
+
* @returns Entity IDs in deletion-safe order
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* ```typescript
|
|
265
|
+
* for (const entity of fetchEntities(world, Position, Velocity, not(Dead))) {
|
|
266
|
+
* const pos = get(world, entity, Position);
|
|
267
|
+
* // Entity can be safely destroyed here
|
|
268
|
+
* }
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
export function* fetchEntities(world, ...terms) {
|
|
272
|
+
const queryMeta = ensureQuery(world, ...terms);
|
|
273
|
+
yield* fetchEntitiesWithQuery(world, queryMeta);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Fetch first entity matching components and modifiers.
|
|
277
|
+
*
|
|
278
|
+
* Useful for singleton patterns or when only one match is expected.
|
|
279
|
+
*
|
|
280
|
+
* @param world - World instance
|
|
281
|
+
* @param terms - Component IDs and query modifiers (not, added, changed)
|
|
282
|
+
* @returns First matching entity ID, or undefined if no matches
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```typescript
|
|
286
|
+
* const player = fetchFirstEntity(world, Player, not(Dead));
|
|
287
|
+
* if (player !== undefined) {
|
|
288
|
+
* const health = get(world, player, Health);
|
|
289
|
+
* }
|
|
290
|
+
* ```
|
|
291
|
+
*/
|
|
292
|
+
export function fetchFirstEntity(world, ...terms) {
|
|
293
|
+
const queryMeta = ensureQuery(world, ...terms);
|
|
294
|
+
for (const entityId of fetchEntitiesWithQuery(world, queryMeta)) {
|
|
295
|
+
return entityId;
|
|
296
|
+
}
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"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"}
|