@vworlds/vecs 1.0.9 → 1.0.11

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.
Files changed (46) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/README.md +218 -229
  3. package/dist/command.d.ts +1 -0
  4. package/dist/command.js +2 -0
  5. package/dist/command.js.map +1 -0
  6. package/dist/component.d.ts +51 -59
  7. package/dist/component.js +31 -25
  8. package/dist/component.js.map +1 -1
  9. package/dist/dsl.d.ts +34 -26
  10. package/dist/dsl.js +46 -20
  11. package/dist/dsl.js.map +1 -1
  12. package/dist/entity.d.ts +110 -127
  13. package/dist/entity.js +323 -164
  14. package/dist/entity.js.map +1 -1
  15. package/dist/filter.d.ts +31 -23
  16. package/dist/filter.js +41 -32
  17. package/dist/filter.js.map +1 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/package.json +3 -1
  20. package/dist/phase.d.ts +5 -28
  21. package/dist/phase.js +11 -10
  22. package/dist/phase.js.map +1 -1
  23. package/dist/query.d.ts +128 -94
  24. package/dist/query.js +254 -145
  25. package/dist/query.js.map +1 -1
  26. package/dist/system.d.ts +64 -128
  27. package/dist/system.js +156 -149
  28. package/dist/system.js.map +1 -1
  29. package/dist/util/array_map.d.ts +4 -55
  30. package/dist/util/array_map.js +35 -37
  31. package/dist/util/array_map.js.map +1 -1
  32. package/dist/util/bitset.d.ts +40 -50
  33. package/dist/util/bitset.js +76 -62
  34. package/dist/util/bitset.js.map +1 -1
  35. package/dist/util/events.d.ts +14 -18
  36. package/dist/util/events.js +24 -3
  37. package/dist/util/events.js.map +1 -1
  38. package/dist/util/ordered_set.d.ts +1 -17
  39. package/dist/util/ordered_set.js +74 -25
  40. package/dist/util/ordered_set.js.map +1 -1
  41. package/dist/world.d.ts +222 -201
  42. package/dist/world.js +394 -323
  43. package/dist/world.js.map +1 -1
  44. package/eslint-rules/internal-underscore.js +60 -0
  45. package/eslint.config.js +5 -0
  46. package/package.json +3 -1
package/dist/world.d.ts CHANGED
@@ -4,20 +4,21 @@ import { Query } from "./query.js";
4
4
  import { System } from "./system.js";
5
5
  import { Filter } from "./filter.js";
6
6
  import { type QueryDSL, type ExtractRequired } from "./dsl.js";
7
- import { IPhase, Phase } from "./phase.js";
7
+ import { IPhase } from "./phase.js";
8
8
  /**
9
- * The central ECS container.
9
+ * The central ECS container. One world per game session.
10
10
  *
11
- * A `World` owns all entities, components, systems, queries, and the update
12
- * pipeline. Typical lifecycle:
11
+ * A `World` owns every entity, every registered component class, every
12
+ * registered query / system, and the update pipeline. The typical lifecycle:
13
13
  *
14
- * 1. **Register components** — call {@link registerComponent} (and optionally
15
- * {@link registerComponentType}) for every component class.
16
- * 2. **Register systems and queries** — call {@link system} and {@link query}
17
- * to create and configure them.
14
+ * 1. **Register components** — {@link registerComponent} (and optionally
15
+ * {@link registerComponentType}) for every component class you plan to use.
16
+ * 2. **Build the pipeline** — {@link addPhase} for every named phase, then
17
+ * {@link system} / {@link query} for each processor.
18
18
  * 3. **Start** — call {@link start} to freeze component registration and
19
19
  * distribute systems into their phases.
20
- * 4. **Run loop** — call {@link runPhase} once per frame for each phase.
20
+ * 4. **Run loop** — call {@link runPhase} per phase or {@link progress} for
21
+ * every phase, once per frame.
21
22
  *
22
23
  * ```ts
23
24
  * const world = new World();
@@ -27,126 +28,95 @@ import { IPhase, Phase } from "./phase.js";
27
28
  *
28
29
  * world.system("Move")
29
30
  * .requires(Position, Velocity)
30
- * .update(Position, (pos) => { pos.x += vel.x; });
31
+ * .each([Position, Velocity], (e, [pos, vel]) => {
32
+ * pos.x += vel.vx;
33
+ * });
31
34
  *
32
35
  * world.start();
33
36
  *
34
37
  * // game loop:
35
- * world.runPhase(updatePhase, Date.now(), 16);
38
+ * world.progress(now, delta);
36
39
  * ```
40
+ *
41
+ * ## Deferred mode
42
+ *
43
+ * The world can be in **deferred mode**, in which case entity mutations
44
+ * (`add` / `set` / `remove` / `destroy` / `setParent` / `modified`) are
45
+ * queued instead of applied inline. Systems run inside an automatically
46
+ * deferred scope; user code can wrap arbitrary blocks with
47
+ * {@link beginDefer} / {@link endDefer} or {@link defer}. {@link flush}
48
+ * drains the queue at top level.
37
49
  */
38
50
  export declare class World {
39
- private entities;
40
- private componentNameTypeMap;
41
- private archChangeQueue;
42
- private destroyedEntities;
43
- private allQueries;
44
- private Class2Meta;
45
- private Type2Meta;
46
- private updatedComponents;
47
- private localComponentCounter;
48
- private componentRegistrationDisabled;
49
- /** @internal */
50
- _pipeline: Map<string, Phase>;
51
- private eidCounter;
52
51
  constructor();
52
+ /** Read-only view of the live entities, keyed by entity id. */
53
+ get entities(): Omit<Map<number, Entity>, "set" | "delete" | "clear">;
54
+ /** Read-only view of every registered query (includes systems). */
55
+ get queries(): ReadonlyArray<Query>;
53
56
  /**
54
- * Return the entity with id `eid`, creating it if it does not yet exist.
55
- *
56
- * Used by networking code to materialise server-assigned entities:
57
- *
58
- * ```ts
59
- * const e = world.getOrCreateEntity(snapshot.eid, (e) => {
60
- * networkEntities.add(e);
61
- * });
62
- * e.add(snapshot.type, false);
63
- * ```
64
- *
65
- * @param eid - The entity id to look up or create.
66
- * @param onCreateCallback - Optional callback invoked only when a **new**
67
- * entity is created, before it is returned. Use this to initialise
68
- * bookkeeping (e.g. tracking it in a local set).
69
- * @returns The existing or newly created entity.
70
- */
71
- getOrCreateEntity(eid: number, onCreateCallback?: (e: Entity) => void): Entity;
72
- /**
73
- * Create a new entity with an auto-assigned id and register it in the world.
74
- *
75
- * The id counter starts at 0 (or at the value set by
76
- * {@link setEntityIdRange}) and increments by one for each call.
57
+ * `true` while the world is in deferred mode entity mutations are queued
58
+ * rather than applied inline. Equivalent to "the queue depth is non-zero or
59
+ * the world is currently draining".
77
60
  */
78
- entity(): Entity;
61
+ get deferred(): boolean;
79
62
  /**
80
- * Look up an entity by id.
63
+ * Enter deferred mode. Mutations made until the matching {@link endDefer}
64
+ * are queued instead of executing inline.
81
65
  *
82
- * @param id - Numeric entity id.
83
- * @returns The entity, or `undefined` if no entity with that id exists.
66
+ * Nested `beginDefer` / `endDefer` pairs are allowed; only the outermost
67
+ * `endDefer` triggers a queue drain.
84
68
  */
85
- entity(id: number): Entity | undefined;
69
+ beginDefer(): void;
86
70
  /**
87
- * Set the starting value for the auto-incrementing entity id counter.
88
- *
89
- * Must be called **before** {@link start} (or
90
- * {@link disableComponentRegistration}). Useful when the world runs alongside
91
- * a server that owns a different id range — for example, locally-created
92
- * client entities can start at a high offset to avoid collisions with
93
- * server-assigned ids.
94
- *
95
- * @param min - The first id that will be assigned by {@link entity}.
96
- * @throws If called after registration has been disabled.
71
+ * Leave deferred mode. When the depth returns to zero the world drains the
72
+ * command queue (firing hooks and routing enter / exit / update events).
97
73
  */
98
- setEntityIdRange(min: number): void;
74
+ endDefer(): void;
99
75
  /**
100
- * Retrieve the {@link ComponentMeta} record for a registered component.
76
+ * Run `fn` inside a deferred scope. Equivalent to
77
+ * `beginDefer(); try { fn(); } finally { endDefer(); }`.
101
78
  *
102
- * @param typeOrClass - A component class constructor or a numeric type id.
103
- * @returns The corresponding `ComponentMeta`.
104
- * @throws If no component with that class or type id has been registered.
79
+ * @param fn - Callback executed in deferred mode.
105
80
  */
106
- getComponentMeta(typeOrClass: ComponentClassOrType): ComponentMeta;
81
+ defer(fn: () => void): void;
107
82
  /**
108
- * Resolve a component class or type id to its numeric type id.
83
+ * Drain any commands queued at the top level (depth 0).
109
84
  *
110
- * @param typeOrClass - A component class constructor or a numeric type id.
111
- * @returns The numeric type id.
85
+ * Call between phases or after batch-loading network snapshots to surface
86
+ * accumulated mutations (firing hooks and routing enter / exit / update)
87
+ * before the next read or system run.
112
88
  */
113
- getComponentType(typeOrClass: ComponentClassOrType): number;
89
+ flush(): void;
114
90
  /**
115
- * Mark an entity's archetype as changed, queuing it for re-evaluation
116
- * against all system queries at the end of the current system run.
91
+ * Pre-register a `componentName typeId` mapping without binding a class.
117
92
  *
118
- * Also recursively marks all children as changed so that `{ PARENT: ... }`
119
- * queries are re-evaluated.
93
+ * Useful when network messages refer to components by type id and the
94
+ * corresponding class may be registered later. Call this **before**
95
+ * {@link registerComponent} so the class picks up the server-assigned id
96
+ * rather than a locally generated one.
120
97
  *
121
- * @internal Called automatically by {@link Entity.add} and
122
- * {@link Entity.remove}.
98
+ * @param componentName - String name used in network payloads.
99
+ * @param type - Numeric type id assigned by the server.
123
100
  */
124
- archetypeChanged(e: Entity): void;
125
- /** @internal */
126
- _notifyComponentAdded(e: Entity, c: Component): void;
127
- /** @internal */
128
- _notifyComponentRemoved(e: Entity, c: Component): void;
129
- /** @internal */
130
- _notifyEntityDestroyed(e: Entity): void;
131
- private updateArchetypes;
132
- /** @internal Queues a component for onSet / update delivery. */
133
- _queueUpdatedComponent(c: Component): void;
101
+ registerComponentType(componentName: string, type: number): void;
134
102
  /**
135
103
  * Register a component class with the world.
136
104
  *
137
- * Must be called before any entity can use the component. Registration is
138
- * disabled once {@link start} is called.
105
+ * Must be called before any entity uses the component. Registration is
106
+ * disabled once {@link start} (or {@link disableComponentRegistration}) is
107
+ * called.
139
108
  *
140
109
  * **Overloads:**
141
- * - `registerComponent(Class)` — type id auto-assigned from the name map, or
142
- * from a local counter (≥ 256) if the name is not yet mapped.
110
+ * - `registerComponent(Class)` — type id auto-assigned from the
111
+ * {@link registerComponentType} map, falling back to a local counter
112
+ * (≥ 256) if the name is not yet mapped.
143
113
  * - `registerComponent(Class, type)` — explicit numeric type id.
144
114
  * - `registerComponent(Class, componentName)` — auto-assigned id, custom
145
115
  * display name (useful when the class name differs from the network name).
146
116
  * - `registerComponent(Class, type, componentName)` — explicit id + name.
147
117
  *
148
- * @param ComponentClass - The component class to register.
149
- * @throws If the class has already been registered, or if registration is
118
+ * @param ComponentClass - Component class to register.
119
+ * @throws When the class has already been registered or registration is
150
120
  * disabled.
151
121
  */
152
122
  registerComponent(ComponentClass: typeof Component): void;
@@ -154,47 +124,138 @@ export declare class World {
154
124
  registerComponent(ComponentClass: typeof Component, componentName?: string): void;
155
125
  registerComponent(ComponentClass: typeof Component, type: number, componentName: string): void;
156
126
  /**
157
- * Pre-register a component name type id mapping without associating a
158
- * class.
127
+ * Look up the {@link ComponentMeta} for a registered component.
159
128
  *
160
- * Useful when network messages refer to components by type id and the
161
- * corresponding class may be registered later. Call this before
162
- * {@link registerComponent} to ensure the class picks up the server-assigned
163
- * id rather than a locally generated one.
129
+ * @param typeOrClass - Component class or numeric type id.
130
+ * @returns The corresponding meta record.
131
+ * @throws When no component with that class or type id has been registered.
132
+ */
133
+ getComponentMeta(typeOrClass: ComponentClassOrType): ComponentMeta;
134
+ /**
135
+ * Resolve a component class or type id to its numeric type id.
164
136
  *
165
- * @param componentName - The string name used in network payloads.
166
- * @param type - The numeric type id assigned by the server.
137
+ * @param typeOrClass - Component class or numeric type id.
138
+ * @returns The numeric type id.
167
139
  */
168
- registerComponentType(componentName: string, type: number): void;
169
- /** @internal Called by the {@link Query} constructor to register itself. */
170
- _addQuery(q: Query): void;
171
- /** @internal Called by {@link Query.destroy} to unregister a query and remove it from all entities. */
172
- _removeQuery(q: Query): void;
173
- /** @internal Iterate over all entities currently in the world. */
174
- _forEachEntity(callback: (e: Entity) => void): void;
140
+ getComponentType(typeOrClass: ComponentClassOrType): number;
141
+ /**
142
+ * Return the {@link Hook} for a component class.
143
+ *
144
+ * Hooks let you react to component lifecycle events (add / remove / set)
145
+ * without building a full {@link System}. The same hook is returned on every
146
+ * call handlers stack on the underlying meta record.
147
+ *
148
+ * ```ts
149
+ * world.hook(Sprite)
150
+ * .onAdd(c => c.initialize(scene))
151
+ * .onRemove(c => c.destroy());
152
+ * ```
153
+ *
154
+ * @param C - Component class.
155
+ * @returns The hook bound to that component type.
156
+ */
157
+ hook<T extends typeof Component>(C: T): Hook<InstanceType<T>>;
158
+ /**
159
+ * Declare a group of mutually exclusive components.
160
+ *
161
+ * Adding any component in the group to an entity that already has another
162
+ * member of the group automatically removes the previous member. Members
163
+ * not in the group are unaffected.
164
+ *
165
+ * ```ts
166
+ * world.setExclusiveComponents(Walking, Running, Idle);
167
+ * entity.add(Walking);
168
+ * entity.add(Running); // Walking is removed automatically
169
+ * ```
170
+ *
171
+ * Each call defines one independent group. A component may belong to at
172
+ * most one group at a time; calling {@link setExclusiveComponents} with the
173
+ * same class again overwrites its group. Safe to call before or after
174
+ * {@link start}.
175
+ *
176
+ * @param components - Two or more component classes that cannot coexist.
177
+ * @throws When any class has not been registered.
178
+ */
179
+ setExclusiveComponents(...components: (typeof Component)[]): void;
180
+ /**
181
+ * Set the starting value of the auto-incrementing entity id counter.
182
+ *
183
+ * Must be called **before** {@link start} (or
184
+ * {@link disableComponentRegistration}). Useful when the world runs
185
+ * alongside a server that owns a different id range — locally created
186
+ * client entities can start at a high offset to avoid collisions with
187
+ * server-assigned ids.
188
+ *
189
+ * @param min - First id assigned by {@link entity}.
190
+ * @throws When called after registration has been disabled.
191
+ */
192
+ setEntityIdRange(min: number): void;
193
+ /**
194
+ * Return the entity with id `eid`, creating it if it does not yet exist.
195
+ *
196
+ * Used by networking code to materialise server-assigned entities:
197
+ *
198
+ * ```ts
199
+ * const e = world.getOrCreateEntity(snapshot.eid, (e) => {
200
+ * networkEntities.add(e);
201
+ * });
202
+ * e.add(snapshot.type);
203
+ * ```
204
+ *
205
+ * @param eid - Entity id to look up or create.
206
+ * @param onCreateCallback - Optional callback invoked only when a new
207
+ * entity is created, before it is returned. Use it to initialise
208
+ * bookkeeping (e.g. tracking it in a local set).
209
+ * @returns The existing or newly created entity.
210
+ */
211
+ getOrCreateEntity(eid: number, onCreateCallback?: (e: Entity) => void): Entity;
212
+ /**
213
+ * Create a new entity with an auto-assigned id.
214
+ *
215
+ * The id counter starts at `0` (or at the value set by
216
+ * {@link setEntityIdRange}) and increments by one for each call. In
217
+ * deferred mode the new entity is queued onto the command queue and is not
218
+ * visible in {@link entities} until the queue drains.
219
+ */
220
+ entity(): Entity;
221
+ /**
222
+ * Look up an existing entity by id.
223
+ *
224
+ * @param id - Numeric entity id.
225
+ * @returns The entity, or `undefined` when no entity with that id exists.
226
+ */
227
+ entity(id: number): Entity | undefined;
228
+ /**
229
+ * Destroy every entity currently tracked by the world.
230
+ *
231
+ * Triggers all `onRemove` hooks and `exit` callbacks. Useful when
232
+ * transitioning between game sessions or resetting to a clean state.
233
+ */
234
+ clearAllEntities(): void;
175
235
  /**
176
- * Create a new {@link System}, register it, and return it for configuration.
236
+ * Create, register, and return a new {@link System}, ready for fluent
237
+ * configuration.
177
238
  *
178
239
  * ```ts
179
240
  * world.system("Render")
180
241
  * .phase("update")
181
242
  * .requires(Position, Sprite)
182
243
  * .enter([Sprite], (e, [sprite]) => sprite.initialize(scene))
183
- * .update(Position, (pos) => { ... });
244
+ * .each([Position, Sprite], (e, [pos, sprite]) => sprite.draw(pos.x, pos.y));
184
245
  * ```
185
246
  *
186
- * @param name - A unique display name for the system.
187
- * @returns The new `System` instance.
247
+ * @param name - Unique display name for the system.
248
+ * @returns The new system.
188
249
  */
189
- system(name: string): System<[]>;
250
+ system(name: string): System;
190
251
  /**
191
- * Create a standalone {@link Query}, register it, and return it for
252
+ * Create, register, and return a standalone {@link Query}, ready for fluent
192
253
  * configuration.
193
254
  *
194
255
  * Unlike a {@link System}, a standalone query has no phase and no per-tick
195
- * callbacks — it is a reactive, always-updated entity set that can be read
196
- * at any time after {@link start}. Standalone queries can also be created
197
- * after {@link start}; existing matched entities are backfilled immediately.
256
+ * callbacks — it is a reactive entity set that can be read at any time. It
257
+ * can also be created **after** {@link start}; existing matched entities
258
+ * are backfilled immediately.
198
259
  *
199
260
  * ```ts
200
261
  * const enemies = world.query("Enemies")
@@ -205,81 +266,44 @@ export declare class World {
205
266
  * // enemies.entities is kept up-to-date automatically
206
267
  * ```
207
268
  *
208
- * @param name - A unique display name for the query.
209
- * @returns The new `Query` instance.
269
+ * @param name - Unique display name for the query.
270
+ * @returns The new query.
210
271
  */
211
- query(name: string): Query<[]>;
272
+ query(name: string): Query;
212
273
  /**
213
274
  * Create a non-reactive {@link Filter} that matches entities satisfying `q`.
214
275
  *
215
276
  * Unlike {@link query}, the returned filter holds no tracked entity set and
216
- * registers nothing with the world. Each call to {@link Filter.forEach} walks
217
- * all current world entities and invokes the callback for matching ones.
277
+ * registers nothing on the world. Each call to {@link Filter.forEach} walks
278
+ * all current world entities and invokes the callback on the matches.
218
279
  *
219
- * Component classes guaranteed present on every matched entity are inferred
220
- * automatically from the DSL where possible (plain arrays, `HAS`, `HAS_ONLY`,
221
- * and `AND` of those forms). For cases the type extractor cannot see through
222
- * (`OR`, `NOT`, `PARENT`, custom `EntityTestFunc`), pass a `_guaranteed`
280
+ * The component classes guaranteed present on every matched entity are
281
+ * inferred from the DSL where possible (plain arrays, `HAS`, `HAS_ONLY`,
282
+ * and `AND` of those forms). For shapes the inferer cannot see through
283
+ * (`OR`, `NOT`, `PARENT`, custom `EntityTestFunc`) supply a `_guaranteed`
223
284
  * tuple as a type-level override:
224
285
  *
225
286
  * ```ts
226
- * // Auto-deduced pos and vel are non-nullable
287
+ * // Auto-deduced: pos and vel are non-nullable.
227
288
  * world.filter([Position, Velocity])
228
289
  * .forEach([Position, Velocity], (e, [pos, vel]) => { ... });
229
290
  *
230
- * // Manual override for an opaque query
291
+ * // Manual override for an opaque query.
231
292
  * world.filter(myTestFunc, [Position])
232
293
  * .forEach([Position], (e, [pos]) => pos.x);
233
294
  * ```
234
295
  *
235
- * @param q - A {@link QueryDSL} expression.
296
+ * @param q - Query expression.
236
297
  * @param _guaranteed - Optional type hint declaring which components are
237
298
  * guaranteed present (not validated at runtime).
238
299
  */
239
300
  filter<Q extends QueryDSL>(q: Q): Filter<ExtractRequired<Q>>;
240
301
  filter<T extends (typeof Component)[]>(q: QueryDSL, _guaranteed: readonly [...T]): Filter<T>;
241
302
  /**
242
- * Prevent any further calls to {@link registerComponent}.
243
- *
244
- * Called automatically by {@link start}. Can be called early if you want to
245
- * lock component registration before systems are fully configured.
246
- */
247
- disableComponentRegistration(): void;
248
- /**
249
- * Freeze component registration and prepare the world for running.
250
- *
251
- * Distributes all systems registered so far into their pipeline phases
252
- * (defaulting to `"update"`) and logs the phase → system order to the
253
- * console. Systems and queries can still be created after this call —
254
- * standalone queries will immediately backfill existing matched entities.
255
- *
256
- * Call this once before the first {@link runPhase} call.
257
- */
258
- start(): void;
259
- private reindexSystems;
260
- /**
261
- * Return the {@link Hook} for a component class.
303
+ * Add a named phase to the update pipeline.
262
304
  *
263
- * Hooks let you react to component lifecycle events (add / remove / set)
264
- * without building a full {@link System}. The hook is backed by the
265
- * component's {@link ComponentMeta} and the same object is returned on every
266
- * call.
267
- *
268
- * ```ts
269
- * world.hook(Sprite)
270
- * .onAdd(c => c.initialize(scene))
271
- * .onRemove(c => c.destroy());
272
- * ```
273
- *
274
- * @param C - The component class.
275
- * @returns The `Hook` for that component type.
276
- */
277
- hook<T extends typeof Component>(C: T): Hook<InstanceType<T>>;
278
- /**
279
- * Add a named phase to the update pipeline and return it.
280
- *
281
- * Phases are executed in insertion order when you call {@link runPhase} for
282
- * each one. Systems are assigned to a phase via {@link System.phase}.
305
+ * Phases are executed in insertion order when {@link runPhase} or
306
+ * {@link progress} is called. Systems join a phase via {@link System.phase}.
283
307
  *
284
308
  * ```ts
285
309
  * const preUpdate = world.addPhase("preupdate");
@@ -288,50 +312,47 @@ export declare class World {
288
312
  * ```
289
313
  *
290
314
  * @param name - Unique phase name. Systems can reference it by this string.
291
- * @returns The new {@link IPhase}.
315
+ * @returns The new phase.
292
316
  */
293
317
  addPhase(name: string): IPhase;
294
318
  /**
295
- * Execute all systems in the given phase for one tick.
296
- *
297
- * After each system runs, pending archetype changes (entity add/remove
298
- * component events) are flushed so that `enter` / `exit` callbacks are
299
- * delivered before the next system in the same phase executes.
319
+ * Prevent any further calls to {@link registerComponent}.
300
320
  *
301
- * @param phase - The {@link IPhase} to run (returned by {@link addPhase}).
302
- * @param now - Absolute timestamp in milliseconds (e.g. `Date.now()`).
303
- * @param delta - Milliseconds elapsed since the previous tick.
321
+ * Called automatically by {@link start}. Call directly if you want to lock
322
+ * registration before the rest of the systems are wired up.
304
323
  */
305
- runPhase(phase: IPhase, now: number, delta: number): void;
324
+ disableComponentRegistration(): void;
306
325
  /**
307
- * Run every phase in the pipeline in insertion order (the order phases were
308
- * registered via {@link addPhase}). Equivalent to calling
309
- * {@link runPhase} for each phase manually.
326
+ * Freeze component registration and prepare the world for running.
310
327
  *
311
- * @param now - Absolute timestamp in milliseconds (e.g. `Date.now()`).
312
- * @param delta - Milliseconds elapsed since the previous tick.
328
+ * Distributes every system registered so far into its phase (defaulting to
329
+ * `"update"`) and logs the phase system order to the console. Systems
330
+ * and queries can still be created after this call — standalone queries
331
+ * backfill existing matched entities immediately.
332
+ *
333
+ * Call once before the first {@link runPhase} / {@link progress}.
313
334
  */
314
- progress(now: number, delta: number): void;
335
+ start(): void;
315
336
  /**
316
- * Declare a group of mutually exclusive components.
317
- *
318
- * After this call, adding any component in the group to an entity that
319
- * already has another component from the same group will remove the other component
337
+ * Execute every system in `phase` for one tick.
320
338
  *
321
- * ```ts
322
- * world.setExclusiveComponents(Walking, Running, Idle);
323
- * // entity.add(Running) throws if entity already has Walking or Idle
324
- * ```
339
+ * Pending top-level mutations are drained before the first system runs so
340
+ * each system observes a consistent world. Each system body executes in a
341
+ * deferred scope; mutations made by callbacks land in the world queue and
342
+ * are processed before the next system runs.
325
343
  *
326
- * @param components - Two or more component classes that cannot coexist.
327
- * @throws If any class has not been registered.
344
+ * @param phase - Phase reference returned from {@link addPhase}.
345
+ * @param now - Absolute timestamp in milliseconds (e.g. `Date.now()`).
346
+ * @param delta - Milliseconds elapsed since the previous tick.
328
347
  */
329
- setExclusiveComponents(...components: (typeof Component)[]): void;
348
+ runPhase(phase: IPhase, now: number, delta: number): void;
330
349
  /**
331
- * Destroy every entity currently tracked by the world.
350
+ * Run every phase in the pipeline in registration order.
332
351
  *
333
- * Triggers all `onRemove` hooks and `exit` callbacks. Useful when
334
- * transitioning between game sessions or resetting to a clean state.
352
+ * Equivalent to calling {@link runPhase} for each phase manually.
353
+ *
354
+ * @param now - Absolute timestamp in milliseconds (e.g. `Date.now()`).
355
+ * @param delta - Milliseconds elapsed since the previous tick.
335
356
  */
336
- clearAllEntities(): void;
357
+ progress(now: number, delta: number): void;
337
358
  }