@vworlds/vecs 1.0.10 → 1.0.12

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 (49) hide show
  1. package/.husky/pre-commit +1 -0
  2. package/README.md +299 -228
  3. package/dist/command.d.ts +1 -46
  4. package/dist/component.d.ts +51 -59
  5. package/dist/component.js +31 -25
  6. package/dist/component.js.map +1 -1
  7. package/dist/dsl.d.ts +34 -26
  8. package/dist/dsl.js +46 -20
  9. package/dist/dsl.js.map +1 -1
  10. package/dist/entity.d.ts +96 -106
  11. package/dist/entity.js +261 -190
  12. package/dist/entity.js.map +1 -1
  13. package/dist/filter.d.ts +31 -23
  14. package/dist/filter.js +24 -17
  15. package/dist/filter.js.map +1 -1
  16. package/dist/index.d.ts +2 -1
  17. package/dist/index.js +1 -0
  18. package/dist/index.js.map +1 -1
  19. package/dist/package.json +3 -1
  20. package/dist/phase.d.ts +12 -30
  21. package/dist/phase.js +11 -10
  22. package/dist/phase.js.map +1 -1
  23. package/dist/query.d.ts +107 -144
  24. package/dist/query.js +200 -169
  25. package/dist/query.js.map +1 -1
  26. package/dist/system.d.ts +170 -86
  27. package/dist/system.js +253 -114
  28. package/dist/system.js.map +1 -1
  29. package/dist/timer.d.ts +50 -0
  30. package/dist/timer.js +154 -0
  31. package/dist/timer.js.map +1 -0
  32. package/dist/util/array_map.d.ts +4 -55
  33. package/dist/util/array_map.js +35 -37
  34. package/dist/util/array_map.js.map +1 -1
  35. package/dist/util/bitset.d.ts +40 -50
  36. package/dist/util/bitset.js +76 -62
  37. package/dist/util/bitset.js.map +1 -1
  38. package/dist/util/events.d.ts +14 -18
  39. package/dist/util/events.js +24 -3
  40. package/dist/util/events.js.map +1 -1
  41. package/dist/util/ordered_set.d.ts +1 -17
  42. package/dist/util/ordered_set.js +74 -25
  43. package/dist/util/ordered_set.js.map +1 -1
  44. package/dist/world.d.ts +230 -218
  45. package/dist/world.js +422 -327
  46. package/dist/world.js.map +1 -1
  47. package/eslint-rules/internal-underscore.js +60 -0
  48. package/eslint.config.js +5 -0
  49. package/package.json +3 -1
package/dist/system.js CHANGED
@@ -1,9 +1,10 @@
1
1
  import { Query } from "./query.js";
2
2
  import { Phase } from "./phase.js";
3
+ import { ALWAYS_TICK_SOURCE, IntervalTickSource, RateTickSource, } from "./timer.js";
3
4
  /**
4
- * A reactive processor that operates on a filtered subset of world entities.
5
+ * A reactive processor running over a filtered subset of world entities.
5
6
  *
6
- * Systems are created and registered through {@link World.system}:
7
+ * Systems are created and configured through {@link World.system}:
7
8
  *
8
9
  * ```ts
9
10
  * world.system("Move")
@@ -14,9 +15,9 @@ import { Phase } from "./phase.js";
14
15
  * .exit((e) => { console.log("entity left", e.eid); });
15
16
  * ```
16
17
  *
17
- * All builder methods return `this` for chaining. Call {@link World.start}
18
- * once all systems are registered; after that, drive the loop with
19
- * {@link World.runPhase}.
18
+ * Every builder method returns `this` for chaining. After registering systems
19
+ * call {@link World.start} once, then drive the loop with
20
+ * {@link World.runPhase} or {@link World.progress}.
20
21
  *
21
22
  * Internally each system holds a single ordered **inbox** of routed events
22
23
  * (`enter`, `exit`, `update`). The world appends to it during command-queue
@@ -25,67 +26,62 @@ import { Phase } from "./phase.js";
25
26
  *
26
27
  * ### Component injection and type inference
27
28
  *
28
- * `enter`, `exit`, `update`, `each`, and `sort` all accept an array of
29
- * component classes that are resolved from the entity and passed as a typed
30
- * tuple to the callback. Use `{ parent: SomeComponent }` to resolve from the
31
- * entity's parent instead of the entity itself.
29
+ * `enter`, `exit`, `update`, `each`, and `sort` accept an array of component
30
+ * classes resolved from the entity and passed as a typed tuple to the
31
+ * callback. Use `{ parent: SomeComponent }` to resolve from the entity's
32
+ * parent instead of the entity itself.
32
33
  *
33
- * Components declared via {@link requires} (or the second argument of
34
- * {@link query}) are tracked as a type parameter `R` on the system. In
35
- * `sort`, `each`, and `update` inject callbacks, those components appear as
36
- * non-nullable; any component not in `R` remains `Type | undefined`.
34
+ * Components declared via {@link requires} (or the `_guaranteed` argument of
35
+ * {@link query}) are tracked as the type parameter `R`. Inside `sort`,
36
+ * `each`, and `update` injection callbacks they are non-nullable; any other
37
+ * component remains `Type | undefined`.
38
+ *
39
+ * @typeParam R - Component classes guaranteed present on every matched entity.
37
40
  */
38
41
  export class System extends Query {
39
42
  constructor(name, world) {
40
43
  super(name, world, false);
41
- this.inbox = [];
44
+ /** @internal Single inbox replayed in arrival order on every `_run`. */
45
+ this._inbox = [];
46
+ /** @internal Whether this system processes events and runs callbacks. */
47
+ this._enabled = true;
48
+ /** @internal Cadence source for this system. Defaults to every frame. */
49
+ this._tickSource = ALWAYS_TICK_SOURCE;
42
50
  }
43
51
  /**
44
- * Assign this system to a pipeline phase.
45
- *
46
- * The phase can be specified by name (the world will resolve it at
47
- * {@link World.start | start} time) or by an {@link IPhase} reference
48
- * returned from {@link World.addPhase}. Systems without an explicit phase
49
- * are placed in the built-in `"update"` phase.
50
- *
51
- * @param p - Phase name or `IPhase` reference.
52
- * @returns `this` for chaining.
52
+ * @internal Routing entry: register query membership for `e`, push an
53
+ * inbox `enter` event when an `enter` callback is registered, and bridge
54
+ * watched components through {@link _notifyModified} to surface them as
55
+ * inbox `update` events on entry.
53
56
  */
54
- phase(p) {
55
- if (typeof p !== "string") {
56
- if (!(p instanceof Phase)) {
57
- throw "Invalid Phase object";
58
- }
59
- if (p.world !== this.world) {
60
- throw "Phase does not belong to this system's world";
61
- }
62
- }
63
- this._phase = p;
64
- return this;
65
- }
66
- /** @internal Routing entry: register membership and enqueue an enter event. */
67
57
  _enter(e) {
68
58
  this._entities?.add(e);
69
59
  e._addQueryMembership(this);
60
+ if (!this._enabled) {
61
+ return;
62
+ }
70
63
  if (this._enterCallback !== undefined) {
71
- this.inbox.push({ kind: 0 /* InboxCommand.Enter */, entity: e });
64
+ this._inbox.push({ kind: 0 /* InboxCommand.Enter */, entity: e });
72
65
  }
73
- // Bridge: surface watched components on entry through `notifyModified`,
74
- // which on System pushes inbox `update` events.
75
- e.forEachComponent((c) => {
76
- if (this.watchlistBitmask.hasBit(c.bitPtr)) {
77
- this.notifyModified(c);
66
+ e.components.forEach((c) => {
67
+ if (this._watchlistBitmask.hasBit(c.bitPtr)) {
68
+ this._notifyModified(c);
78
69
  }
79
70
  });
80
71
  }
81
- /** @internal Routing entry: deregister membership and enqueue an exit event. */
72
+ /**
73
+ * @internal Routing entry: deregister query membership for `e` and push an
74
+ * inbox `exit` event when an `exit` callback is registered, capturing a
75
+ * snapshot of the components the callback wants to inject so they remain
76
+ * resolvable after the underlying components are removed.
77
+ */
82
78
  _exit(e) {
83
79
  this._entities?.delete(e);
84
80
  e._removeQueryMembership(this);
81
+ if (!this._enabled) {
82
+ return;
83
+ }
85
84
  if (this._exitCallback !== undefined) {
86
- // Only snapshot the types the callback injects, and only direct (non-parent)
87
- // ones — resolved at registration time. Parent refs are read from e.parent
88
- // at callback time. Undefined when the callback takes no injection.
89
85
  let snapshot;
90
86
  if (this._exitSnapshotTypes && this._exitSnapshotTypes.length > 0) {
91
87
  snapshot = new Map();
@@ -96,26 +92,37 @@ export class System extends Query {
96
92
  }
97
93
  }
98
94
  }
99
- this.inbox.push({ kind: 1 /* InboxCommand.Exit */, entity: e, snapshot });
95
+ this._inbox.push({ kind: 1 /* InboxCommand.Exit */, entity: e, snapshot });
100
96
  }
101
97
  }
102
- /** @internal Routing entry: enqueue an update event if the watchlist matches. */
103
- notifyModified(c) {
104
- if (!this.watchlistBitmask.hasBit(c.bitPtr)) {
98
+ /**
99
+ * @internal Routing entry: push an inbox `update` event when the modified
100
+ * component matches the watchlist.
101
+ */
102
+ _notifyModified(c) {
103
+ if (!this._enabled || !this._watchlistBitmask.hasBit(c.bitPtr)) {
105
104
  return;
106
105
  }
107
- this.inbox.push({ kind: 2 /* InboxCommand.Update */, component: c });
106
+ this._inbox.push({ kind: 2 /* InboxCommand.Update */, component: c });
108
107
  }
109
108
  /**
110
109
  * @internal Execute one tick: drain the inbox in arrival order, then run
111
- * `runCallback` and `eachCallback`. The whole body runs in a deferred
112
- * scope; any mutations made by callbacks land in the world queue and are
113
- * processed by the world after `_run` returns.
110
+ * the `run` callback, then the `each` callback for every tracked entity.
111
+ *
112
+ * The whole body executes inside a `World.defer` scope; mutations made by
113
+ * callbacks land in the world queue and are processed when `_run` returns.
114
114
  */
115
115
  _run(now, delta) {
116
+ if (!this._enabled) {
117
+ return;
118
+ }
119
+ if (!this._tickSource.didTick) {
120
+ return;
121
+ }
122
+ const tickDelta = this._tickSource.lastFireDelta;
116
123
  this.world.defer(() => {
117
- for (let i = 0; i < this.inbox.length; i++) {
118
- const event = this.inbox[i];
124
+ for (let i = 0; i < this._inbox.length; i++) {
125
+ const event = this._inbox[i];
119
126
  switch (event.kind) {
120
127
  case 0 /* InboxCommand.Enter */:
121
128
  this._enterCallback(event.entity);
@@ -124,61 +131,155 @@ export class System extends Query {
124
131
  this._exitCallback(event.entity, event.snapshot);
125
132
  break;
126
133
  case 2 /* InboxCommand.Update */:
127
- const callback = this.componentUpdateCallbacks.get(event.component.type);
134
+ const callback = this._componentUpdateCallbacks.get(event.component.type);
128
135
  if (callback) {
129
136
  callback(event.component);
130
137
  }
131
138
  break;
132
139
  }
133
140
  }
134
- this.inbox.length = 0;
141
+ this._inbox.length = 0;
135
142
  if (this._runCallback) {
136
- this._runCallback(now, delta);
143
+ this._runCallback(now, tickDelta);
137
144
  }
138
- if (this.eachCallback) {
139
- const cb = this.eachCallback;
145
+ if (this._eachCallback) {
146
+ const cb = this._eachCallback;
140
147
  this.forEach((e) => cb(e));
141
148
  }
142
149
  });
143
150
  }
151
+ /** True when this system's cadence source fired during the current frame. */
152
+ get didTick() {
153
+ return this._tickSource.didTick;
154
+ }
155
+ /** Milliseconds accumulated into this system's most recent fire. */
156
+ get lastFireDelta() {
157
+ return this._tickSource.lastFireDelta;
158
+ }
159
+ /** @internal Evaluate this system's cadence source for a frame. */
160
+ _evalTick(delta, frameId) {
161
+ return this._tickSource._evalTick(delta, frameId);
162
+ }
163
+ /** @internal Register this system source and its current cadence source. */
164
+ _register(world) {
165
+ world._registerTickSource(this);
166
+ this._tickSource._register(world);
167
+ }
168
+ /**
169
+ * Run this system at a fixed interval, expressed in seconds.
170
+ *
171
+ * This is seconds, unlike `World.beginFrame`, `World.progress`, and
172
+ * `runPhase`, which receive millisecond deltas. Calling `interval` replaces
173
+ * the current cadence source with an {@link IntervalTickSource}. When this
174
+ * system has a cadence source, the `delta` passed to {@link run} is the
175
+ * accumulated milliseconds since the previous fire, not the per-frame delta.
176
+ *
177
+ * @param seconds - Positive interval duration in seconds.
178
+ * @returns This system, for chaining.
179
+ * @throws When `seconds` is less than or equal to zero.
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * world.system("AI")
184
+ * .interval(0.5)
185
+ * .run((now, delta) => tickAI(delta));
186
+ * ```
187
+ */
188
+ interval(seconds) {
189
+ this._setTickSource(new IntervalTickSource(seconds));
190
+ return this;
191
+ }
192
+ rate(n, source) {
193
+ this._setTickSource(new RateTickSource(n, source ?? this._tickSource));
194
+ return this;
195
+ }
144
196
  /**
145
- * Register a per-tick callback that runs every time this system's phase
146
- * executes, regardless of entity membership.
197
+ * Run this system only when another timer or system ticks.
198
+ *
199
+ * Calling `tickSource` mirrors `source` directly; no wrapper source is
200
+ * created. Use `.tickSource(source).rate(n)` or `.rate(n, source)` when this
201
+ * system should divide an upstream source. The `delta` passed to {@link run}
202
+ * is the accumulated milliseconds since this system last fired.
147
203
  *
148
- * Use this for logic that is not driven by component updates — polling,
204
+ * @param source - Tick source or system source to mirror.
205
+ * @returns This system, for chaining.
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * const second = new IntervalTickSource(1);
210
+ * world.system("Logger").tickSource(second).run(logStats);
211
+ * ```
212
+ */
213
+ tickSource(source) {
214
+ this._setTickSource(source);
215
+ return this;
216
+ }
217
+ _setTickSource(source) {
218
+ this._tickSource = source;
219
+ source._register(this.world);
220
+ }
221
+ /**
222
+ * Assign this system to a pipeline phase.
223
+ *
224
+ * Pass either a phase name (resolved at {@link World.start}) or an
225
+ * {@link IPhase} reference returned from {@link World.addPhase}. Systems
226
+ * with no explicit phase fall into the built-in `"update"` phase.
227
+ *
228
+ * @param p - Phase name or `IPhase` reference.
229
+ * @returns This system, for chaining.
230
+ * @throws When the phase reference is not a `Phase`, or belongs to a
231
+ * different world.
232
+ */
233
+ phase(p) {
234
+ if (typeof p !== "string") {
235
+ if (!(p instanceof Phase)) {
236
+ throw "Invalid Phase object";
237
+ }
238
+ if (p.world !== this.world) {
239
+ throw "Phase does not belong to this system's world";
240
+ }
241
+ }
242
+ this._phase = p;
243
+ return this;
244
+ }
245
+ /**
246
+ * Register a per-tick callback fired every time this system's phase runs,
247
+ * regardless of entity membership.
248
+ *
249
+ * Use it for logic that is not driven by component changes — polling,
149
250
  * network flushing, global timers, etc.
150
251
  *
151
252
  * @param callback - Receives `now` (absolute timestamp in ms) and `delta`
152
- * (ms since the last tick).
153
- * @returns `this` for chaining.
253
+ * (ms since the previous tick). If the system has an active interval,
254
+ * rate, or tick source, `delta` is the accumulated milliseconds since this
255
+ * system last fired.
256
+ * @returns This system, for chaining.
154
257
  */
155
258
  run(callback) {
156
259
  this._runCallback = callback;
157
260
  return this;
158
261
  }
159
262
  /**
160
- * Register a callback that fires **every tick** for every entity currently
161
- * tracked by this system, with the listed components resolved from each
162
- * entity.
263
+ * Register a callback fired **every tick** for **every tracked entity**,
264
+ * unconditionally, with the listed components resolved from each entity.
163
265
  *
164
266
  * Unlike {@link update} (which only fires when `component.modified()` is
165
- * called), `each` fires unconditionally on every tick the system runs,
166
- * once per tracked entity. Components declared via {@link requires} are
167
- * guaranteed non-null in the resolved tuple; any other component class
168
- * may be `undefined` if the entity lacks it.
267
+ * called), `each` fires every tick the system runs, once per tracked entity.
268
+ * Components declared via {@link requires} are non-nullable in the resolved
269
+ * tuple; any other component class may be `undefined` if the entity lacks it.
169
270
  *
170
271
  * `each` does **not** modify the system's query — define membership with
171
272
  * {@link requires} or {@link query} as usual. It does, however, implicitly
172
273
  * enable {@link track}, so matched entities are exposed via {@link entities}.
173
274
  *
174
- * Only a single `each` callback may be registered per system; calling
175
- * `each` a second time throws.
275
+ * Only one `each` callback may be registered per system; calling `each` a
276
+ * second time throws.
176
277
  *
177
278
  * @param components - Component classes to resolve from each entity.
178
279
  * @param callback - Receives the entity and a tuple of resolved component
179
- * instances (`undefined` for components not covered by {@link requires}).
180
- * @returns `this` for chaining.
181
- * @throws If `each` has already been registered on this system.
280
+ * instances (`undefined` for any not covered by {@link requires}).
281
+ * @returns This system, for chaining.
282
+ * @throws When `each` has already been registered on this system.
182
283
  *
183
284
  * @example
184
285
  * ```ts
@@ -191,71 +292,109 @@ export class System extends Query {
191
292
  * ```
192
293
  */
193
294
  each(components, callback) {
194
- if (this.eachCallback) {
295
+ if (this._eachCallback) {
195
296
  throw `each already registered for system '${this.name}'`;
196
297
  }
197
298
  this.track();
198
299
  const types = components.map((C) => this.world.getComponentType(C));
199
- this.eachCallback = (e) => {
300
+ this._eachCallback = (e) => {
200
301
  const resolved = types.map((t) => e.get(t));
201
302
  callback(e, resolved);
202
303
  };
203
304
  return this;
204
305
  }
205
306
  /**
206
- * Not supported on `System`. Throws unconditionally.
307
+ * Set the entity-membership predicate using a {@link QueryDSL} expression.
308
+ *
309
+ * Replaces any implicit query derived from `update` watchlists and any
310
+ * previous `requires` call. After calling `query`, watchlist auto-expansion
311
+ * via `update` is disabled.
207
312
  *
208
- * Systems are owned by the world for the duration of the session. If you
209
- * need a temporary reactive set, use a standalone {@link Query} instead.
313
+ * The optional `_guaranteed` tuple is a pure type-level hint see
314
+ * {@link Query.query} for details.
315
+ *
316
+ * @param q - Query expression.
317
+ * @param _guaranteed - Component classes guaranteed present on every matched
318
+ * entity (type hint only — not validated at runtime).
319
+ * @returns This system, retyped with the guaranteed tuple as its `R`.
210
320
  */
211
- destroy() {
212
- throw `destroy() is not supported on System '${this.name}'`;
321
+ query(q, _guaranteed) {
322
+ super.query(q, _guaranteed);
323
+ return this;
213
324
  }
214
325
  /**
215
- * Set the entity membership predicate using the {@link QueryDSL} DSL.
326
+ * Shorthand for `query([...components])` tracks entities that have **all**
327
+ * of the listed component types.
216
328
  *
217
- * Replaces any implicit query derived from `update` watchlists and any
218
- * previous `requires` call. After calling `query`, auto-expanding of
219
- * `update` watchlists is disabled.
329
+ * Equivalent to `query({ HAS: components })`. The listed components are also
330
+ * recorded in the type parameter `R`, so {@link sort}, {@link each}, and
331
+ * {@link update} callbacks treat them as non-nullable.
332
+ *
333
+ * @param components - Component classes to require.
334
+ * @returns This system, retyped with the required tuple as its `R`.
335
+ */
336
+ requires(...components) {
337
+ super.requires(...components);
338
+ return this;
339
+ }
340
+ /**
341
+ * Disable this system.
220
342
  *
221
- * The optional `guaranteed` tuple is a pure type-level hint: it tells
222
- * `sort`, `each`, and `update` callbacks which components are guaranteed
223
- * to be present on every matched entity, eliminating `| undefined` from
224
- * those positions. It has no effect at runtime.
343
+ * While disabled the system is effectively invisible: the inbox is cleared
344
+ * immediately, any new `enter`, `exit`, or `update` events are silently
345
+ * dropped, and {@link _run} returns without executing any callbacks. Entity
346
+ * membership in the underlying query is still maintained so the tracked set
347
+ * remains consistent and the system resumes correctly when
348
+ * {@link enable} is called.
225
349
  *
226
- * @param q - A {@link QueryDSL} expression.
227
- * @param _guaranteed - Component classes guaranteed present on every matched
228
- * entity (type hint only not validated at runtime).
229
- * @returns `this` for chaining.
350
+ * Disabling is independent from tick-source cadence: `disable` suppresses
351
+ * callbacks, but a disabled system used as a tick source still drives
352
+ * downstream consumers. Use `stop()` on an external `IntervalTickSource` or
353
+ * `RateTickSource` reference to halt that clock itself.
354
+ *
355
+ * Calling `disable` on an already-disabled system is a no-op.
356
+ *
357
+ * @returns This system, for chaining.
230
358
  *
231
359
  * @example
232
360
  * ```ts
233
- * world.system("Move")
234
- * .query({ AND: [{ HAS: Position }, { HAS: Velocity }] }, [Position, Velocity])
235
- * .each([Position, Velocity], (e, [pos, vel]) => {
236
- * pos.x += vel.vx; // no ! needed
237
- * });
361
+ * const sys = world.system("AI").requires(Enemy).run(runAI);
362
+ * // Pause AI processing during a cutscene:
363
+ * sys.disable();
364
+ * // Resume:
365
+ * sys.enable();
238
366
  * ```
239
367
  */
240
- query(q, _guaranteed) {
241
- super.query(q, _guaranteed);
368
+ disable() {
369
+ this._enabled = false;
370
+ this._inbox.length = 0;
242
371
  return this;
243
372
  }
244
373
  /**
245
- * Shorthand for `query([...components])` the system tracks entities that
246
- * have **all** of the listed component types.
374
+ * Enable this system after a previous {@link disable} call.
247
375
  *
248
- * Equivalent to `query({ HAS: components })`. Unlike `query`, passing
249
- * component classes here also informs the types of {@link sort} and
250
- * {@link each} callbacks: listed components will be non-nullable in those
251
- * tuples.
376
+ * Once re-enabled the system resumes its normal tick behaviour: enter, exit,
377
+ * and update events are queued, and {@link _run} processes the inbox and fires
378
+ * all registered callbacks. Events that occurred while the system was disabled
379
+ * are not replayed.
252
380
  *
253
- * @param components - One or more component classes.
254
- * @returns `this` for chaining.
381
+ * Calling `enable` on an already-enabled system is a no-op.
382
+ *
383
+ * @returns This system, for chaining.
255
384
  */
256
- requires(...components) {
257
- super.requires(...components);
385
+ enable() {
386
+ this._enabled = true;
258
387
  return this;
259
388
  }
389
+ /**
390
+ * Not supported on `System`. Throws unconditionally.
391
+ *
392
+ * Systems are owned by the world for the duration of the session; if you
393
+ * need a temporary reactive set use a standalone {@link Query} via
394
+ * {@link World.query}.
395
+ */
396
+ destroy() {
397
+ throw `destroy() is not supported on System '${this.name}'`;
398
+ }
260
399
  }
261
400
  //# sourceMappingURL=system.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"system.js","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGnC,OAAO,EAAE,KAAK,EAAe,MAAM,YAAY,CAAC;AAEhD,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAElB,cAAc,GACf,MAAM,YAAY,CAAC;AAwBpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,OAAO,MAA4C,SAAQ,KAAQ;IAgBvE,YAAY,IAAY,EAAE,KAAY;QACpC,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;QAhB5B,wEAAwE;QACvD,WAAM,GAAwB,EAAE,CAAC;QASlD,yEAAyE;QACjE,aAAQ,GAAG,IAAI,CAAC;QACxB,yEAAyE;QACjE,gBAAW,GAAgB,kBAAkB,CAAC;IAItD,CAAC;IAED;;;;;OAKG;IACa,MAAM,CAAC,CAAS;QAC9B,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;QACvB,CAAC,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,OAAO;SACR;QACD,IAAI,IAAI,CAAC,cAAc,KAAK,SAAS,EAAE;YACrC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,4BAAoB,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;SAC3D;QACD,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE;YACzB,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;gBAC3C,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;aACzB;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACa,KAAK,CAAC,CAAS;QAC7B,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,OAAO;SACR;QACD,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS,EAAE;YACpC,IAAI,QAA4C,CAAC;YACjD,IAAI,IAAI,CAAC,kBAAkB,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC,EAAE;gBACjE,QAAQ,GAAG,IAAI,GAAG,EAAqB,CAAC;gBACxC,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,kBAAkB,EAAE;oBAC1C,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBACvB,IAAI,CAAC,EAAE;wBACL,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;qBACvB;iBACF;aACF;YACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,2BAAmB,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;SACpE;IACH,CAAC;IAED;;;OAGG;IACa,eAAe,CAAC,CAAY;QAC1C,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE;YAC9D,OAAO;SACR;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,6BAAqB,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC;IAChE,CAAC;IAED;;;;;;OAMG;IACI,IAAI,CAAC,GAAW,EAAE,KAAa;QACpC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;YAClB,OAAO;SACR;QACD,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE;YAC7B,OAAO;SACR;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;QACjD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE;YACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC7B,QAAQ,KAAK,CAAC,IAAI,EAAE;oBAClB;wBACE,IAAI,CAAC,cAAe,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;wBACnC,MAAM;oBACR;wBACE,IAAI,CAAC,aAAc,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;wBAClD,MAAM;oBACR;wBACE,MAAM,QAAQ,GAAG,IAAI,CAAC,yBAAyB,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;wBAC1E,IAAI,QAAQ,EAAE;4BACZ,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;yBAC3B;wBACD,MAAM;iBACT;aACF;YACD,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;YAEvB,IAAI,IAAI,CAAC,YAAY,EAAE;gBACrB,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;aACnC;YAED,IAAI,IAAI,CAAC,aAAa,EAAE;gBACtB,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;gBAC9B,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;aAC5B;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,6EAA6E;IAC7E,IAAW,OAAO;QAChB,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;IAClC,CAAC;IAED,oEAAoE;IACpE,IAAW,aAAa;QACtB,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC;IACxC,CAAC;IAED,mEAAmE;IAC5D,SAAS,CAAC,KAAa,EAAE,OAAe;QAC7C,OAAO,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,4EAA4E;IACrE,SAAS,CAAC,KAAY;QAC3B,KAAK,CAAC,mBAAmB,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACI,QAAQ,CAAC,OAAe;QAC7B,IAAI,CAAC,cAAc,CAAC,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAyBM,IAAI,CAAC,CAAS,EAAE,MAAoB;QACzC,IAAI,CAAC,cAAc,CAAC,IAAI,cAAc,CAAC,CAAC,EAAE,MAAM,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACI,UAAU,CAAC,MAAmB;QACnC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,cAAc,CAAC,MAAmB;QACxC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC;QAC1B,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED;;;;;;;;;;;OAWG;IACI,KAAK,CAAC,CAAkB;QAC7B,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE;YACzB,IAAI,CAAC,CAAC,CAAC,YAAY,KAAK,CAAC,EAAE;gBACzB,MAAM,sBAAsB,CAAC;aAC9B;YACD,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,KAAK,EAAE;gBAC1B,MAAM,8CAA8C,CAAC;aACtD;SACF;QACD,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;OAYG;IACI,GAAG,CAAC,QAAqB;QAC9B,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACI,IAAI,CACT,UAA2B,EAC3B,QAAmF;QAEnF,IAAI,IAAI,CAAC,aAAa,EAAE;YACtB,MAAM,uCAAuC,IAAI,CAAC,IAAI,GAAG,CAAC;SAC3D;QACD,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;QACpE,IAAI,CAAC,aAAa,GAAG,CAAC,CAAS,EAAE,EAAE;YACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAC5C,QAAQ,CAAC,CAAC,EAAE,QAAe,CAAC,CAAC;QAC/B,CAAC,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACa,KAAK,CACnB,CAAW,EACX,WAA6B;QAE7B,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;QAC5B,OAAO,IAA4B,CAAC;IACtC,CAAC;IAED;;;;;;;;;;OAUG;IACa,QAAQ,CAAiC,GAAG,UAAkB;QAC5E,KAAK,CAAC,QAAQ,CAAC,GAAG,UAAU,CAAC,CAAC;QAC9B,OAAO,IAA4B,CAAC;IACtC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACI,OAAO;QACZ,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;OAWG;IACI,MAAM;QACX,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;OAMG;IACa,OAAO;QACrB,MAAM,yCAAyC,IAAI,CAAC,IAAI,GAAG,CAAC;IAC9D,CAAC;CACF"}
@@ -0,0 +1,50 @@
1
+ /** A clock that the world evaluates once per frame. */
2
+ export interface ITickSource {
3
+ /** True when this source fired during the current frame. */
4
+ readonly didTick: boolean;
5
+ /** Milliseconds accumulated into the most recent fire. */
6
+ readonly lastFireDelta: number;
7
+ }
8
+ /** Shared state and start/stop control for concrete tick sources. */
9
+ declare abstract class BaseTickSource implements ITickSource {
10
+ get didTick(): boolean;
11
+ get lastFireDelta(): number;
12
+ /** Resume this source without replaying time elapsed while stopped. */
13
+ start(): this;
14
+ /** Pause this source, freezing accumulators and counters until `start()`. */
15
+ stop(): this;
16
+ }
17
+ /** Fires every `intervalSeconds` of accumulated wall-clock time. */
18
+ export declare class IntervalTickSource extends BaseTickSource {
19
+ private readonly _intervalMs;
20
+ private _accumulator;
21
+ /**
22
+ * Create an interval source.
23
+ *
24
+ * Intervals are expressed in seconds, unlike `World.progress` and
25
+ * `World.beginFrame`, which receive millisecond deltas. Large deltas produce
26
+ * at most one tick; residual time is preserved by subtracting the interval.
27
+ *
28
+ * @param intervalSeconds - Positive interval duration in seconds.
29
+ * @throws When `intervalSeconds` is less than or equal to zero.
30
+ */
31
+ constructor(intervalSeconds: number);
32
+ }
33
+ /** Fires every `rate` ticks of `source`, or every `rate` frames without one. */
34
+ export declare class RateTickSource extends BaseTickSource {
35
+ private readonly _rate;
36
+ private _counter;
37
+ /**
38
+ * Create a rate filter.
39
+ *
40
+ * Without `source`, this counts world frames. With `source`, it counts ticks
41
+ * from that upstream clock. The source is immutable, so cyclic source graphs
42
+ * cannot be constructed through the public API.
43
+ *
44
+ * @param rate - Positive integer tick divisor.
45
+ * @param source - Optional upstream source to divide.
46
+ * @throws When `rate` is not a positive integer.
47
+ */
48
+ constructor(rate: number, source?: ITickSource);
49
+ }
50
+ export {};