@vworlds/vecs 1.0.11 → 1.0.13

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/dist/timer.js ADDED
@@ -0,0 +1,154 @@
1
+ /** Shared state and start/stop control for concrete tick sources. */
2
+ class BaseTickSource {
3
+ constructor() {
4
+ /** @internal */
5
+ this._didTick = false;
6
+ /** @internal */
7
+ this._running = true;
8
+ /** @internal */
9
+ this._lastEvaluatedFrame = -1;
10
+ /** @internal */
11
+ this._timeSinceLastTick = 0;
12
+ /** @internal Delta accumulated into the most recent tick, in milliseconds. */
13
+ this._lastFireDelta = 0;
14
+ }
15
+ get didTick() {
16
+ return this._didTick;
17
+ }
18
+ get lastFireDelta() {
19
+ return this._lastFireDelta;
20
+ }
21
+ /** Resume this source without replaying time elapsed while stopped. */
22
+ start() {
23
+ this._running = true;
24
+ return this;
25
+ }
26
+ /** Pause this source, freezing accumulators and counters until `start()`. */
27
+ stop() {
28
+ this._running = false;
29
+ return this;
30
+ }
31
+ /** @internal Register this source with `world`. */
32
+ _register(world) {
33
+ world._registerTickSource(this);
34
+ }
35
+ }
36
+ /** Fires every `intervalSeconds` of accumulated wall-clock time. */
37
+ export class IntervalTickSource extends BaseTickSource {
38
+ /**
39
+ * Create an interval source.
40
+ *
41
+ * Intervals are expressed in seconds, unlike `World.progress` and
42
+ * `World.beginFrame`, which receive millisecond deltas. Large deltas produce
43
+ * at most one tick; residual time is preserved by subtracting the interval.
44
+ *
45
+ * @param intervalSeconds - Positive interval duration in seconds.
46
+ * @throws When `intervalSeconds` is less than or equal to zero.
47
+ */
48
+ constructor(intervalSeconds) {
49
+ super();
50
+ this._accumulator = 0;
51
+ if (intervalSeconds <= 0) {
52
+ throw "interval seconds must be greater than 0";
53
+ }
54
+ this._intervalMs = intervalSeconds * 1000;
55
+ }
56
+ /** @internal */
57
+ _evalTick(deltaMs, frameId) {
58
+ if (this._lastEvaluatedFrame === frameId) {
59
+ return this._didTick;
60
+ }
61
+ this._lastEvaluatedFrame = frameId;
62
+ if (!this._running) {
63
+ this._didTick = false;
64
+ return false;
65
+ }
66
+ this._timeSinceLastTick += deltaMs;
67
+ this._accumulator += deltaMs;
68
+ let fired = false;
69
+ if (this._accumulator >= this._intervalMs) {
70
+ this._accumulator -= this._intervalMs;
71
+ fired = true;
72
+ this._lastFireDelta = this._timeSinceLastTick;
73
+ this._timeSinceLastTick = 0;
74
+ }
75
+ this._didTick = fired;
76
+ return fired;
77
+ }
78
+ }
79
+ /** Fires every `rate` ticks of `source`, or every `rate` frames without one. */
80
+ export class RateTickSource extends BaseTickSource {
81
+ /**
82
+ * Create a rate filter.
83
+ *
84
+ * Without `source`, this counts world frames. With `source`, it counts ticks
85
+ * from that upstream clock. The source is immutable, so cyclic source graphs
86
+ * cannot be constructed through the public API.
87
+ *
88
+ * @param rate - Positive integer tick divisor.
89
+ * @param source - Optional upstream source to divide.
90
+ * @throws When `rate` is not a positive integer.
91
+ */
92
+ constructor(rate, source) {
93
+ super();
94
+ this._counter = 0;
95
+ if (!Number.isInteger(rate) || rate <= 0) {
96
+ throw "rate must be a positive integer";
97
+ }
98
+ this._rate = rate;
99
+ this._source = source;
100
+ }
101
+ /** @internal */
102
+ _evalTick(deltaMs, frameId) {
103
+ if (this._lastEvaluatedFrame === frameId) {
104
+ return this._didTick;
105
+ }
106
+ this._lastEvaluatedFrame = frameId;
107
+ if (!this._running) {
108
+ this._didTick = false;
109
+ return false;
110
+ }
111
+ const upstreamFired = this._source ? this._source._evalTick(deltaMs, frameId) : true;
112
+ const upstreamDelta = this._source ? this._source.lastFireDelta : deltaMs;
113
+ let fired = false;
114
+ if (upstreamFired) {
115
+ this._timeSinceLastTick += upstreamDelta;
116
+ this._counter++;
117
+ if (this._counter >= this._rate) {
118
+ this._counter = 0;
119
+ fired = true;
120
+ this._lastFireDelta = this._timeSinceLastTick;
121
+ this._timeSinceLastTick = 0;
122
+ }
123
+ }
124
+ this._didTick = fired;
125
+ return fired;
126
+ }
127
+ /** @internal */
128
+ _register(world) {
129
+ super._register(world);
130
+ this._source?._register(world);
131
+ }
132
+ }
133
+ /** @internal Singleton source used by systems with no explicit cadence. */
134
+ class _AlwaysTickSource {
135
+ constructor() {
136
+ this._lastDelta = 0;
137
+ }
138
+ get didTick() {
139
+ return true;
140
+ }
141
+ get lastFireDelta() {
142
+ return this._lastDelta;
143
+ }
144
+ /** @internal */
145
+ _evalTick(deltaMs, _frameId) {
146
+ this._lastDelta = deltaMs;
147
+ return true;
148
+ }
149
+ /** @internal */
150
+ _register(_world) { }
151
+ }
152
+ /** @internal Shared default clock for unconfigured systems. */
153
+ export const ALWAYS_TICK_SOURCE = new _AlwaysTickSource();
154
+ //# sourceMappingURL=timer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timer.js","sourceRoot":"","sources":["../src/timer.ts"],"names":[],"mappings":"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"}
@@ -29,10 +29,11 @@ export class ArrayMap {
29
29
  * @param value - Value to store.
30
30
  */
31
31
  set(key, value) {
32
- if (this._backend[key] === undefined) {
32
+ const backend = this._backend;
33
+ if (backend[key] === undefined) {
33
34
  this._size++;
34
35
  }
35
- this._backend[key] = value;
36
+ backend[key] = value;
36
37
  }
37
38
  /**
38
39
  * Retrieve the value stored at `key`, or `undefined` if no entry exists.
@@ -56,8 +57,9 @@ export class ArrayMap {
56
57
  * @param key - Non-negative integer key.
57
58
  */
58
59
  delete(key) {
59
- if (this._backend[key] !== undefined) {
60
- this._backend[key] = undefined;
60
+ const backend = this._backend;
61
+ if (backend[key] !== undefined) {
62
+ backend[key] = undefined;
61
63
  this._size--;
62
64
  }
63
65
  }
@@ -67,11 +69,13 @@ export class ArrayMap {
67
69
  * @param callback - Invoked with `(value, key, map)` for each entry.
68
70
  */
69
71
  forEach(callback) {
70
- this._backend.forEach((value, index) => {
72
+ const backend = this._backend;
73
+ for (let i = 0; i < backend.length; i++) {
74
+ const value = backend[i];
71
75
  if (value !== undefined) {
72
- callback(value, index, this);
76
+ callback(value, i, this);
73
77
  }
74
- });
78
+ }
75
79
  }
76
80
  /** Remove all entries and reset {@link size} to zero. */
77
81
  clear() {
@@ -1 +1 @@
1
- {"version":3,"file":"array_map.js","sourceRoot":"","sources":["../../src/util/array_map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,QAAQ;IAArB;QACU,aAAQ,GAAsB,EAAE,CAAC;QACjC,UAAK,GAAW,CAAC,CAAC;IAoE5B,CAAC;IAlEC,kDAAkD;IAClD,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACI,GAAG,CAAC,GAAW,EAAE,KAAQ;QAC9B,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;YACpC,IAAI,CAAC,KAAK,EAAE,CAAC;SACd;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC7B,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,GAAW;QACvB,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;YACpC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;YAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;SACd;IACH,CAAC;IAED;;;;OAIG;IACI,OAAO,CAAC,QAA2D;QACxE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;YACrC,IAAI,KAAK,KAAK,SAAS,EAAE;gBACvB,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;aAC9B;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,yDAAyD;IAClD,KAAK;QACV,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACjB,CAAC;CACF"}
1
+ {"version":3,"file":"array_map.js","sourceRoot":"","sources":["../../src/util/array_map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,QAAQ;IAArB;QACU,aAAQ,GAAsB,EAAE,CAAC;QACjC,UAAK,GAAW,CAAC,CAAC;IAwE5B,CAAC;IAtEC,kDAAkD;IAClD,IAAW,IAAI;QACb,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACI,GAAG,CAAC,GAAW,EAAE,KAAQ;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;YAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;SACd;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,GAAW;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC;IAC1C,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,GAAW;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE;YAC9B,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;YACzB,IAAI,CAAC,KAAK,EAAE,CAAC;SACd;IACH,CAAC;IAED;;;;OAIG;IACI,OAAO,CAAC,QAA2D;QACxE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACvC,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,KAAK,KAAK,SAAS,EAAE;gBACvB,QAAQ,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;aAC1B;SACF;IACH,CAAC;IAED,yDAAyD;IAClD,KAAK;QACV,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;IACjB,CAAC;CACF"}
@@ -10,7 +10,7 @@
10
10
  * compact bit-flag fields:
11
11
  *
12
12
  * ```ts
13
- * class Tags extends Component {
13
+ * class Tags {
14
14
  * tags = new Bitset();
15
15
  * }
16
16
  *
@@ -33,12 +33,26 @@ export declare class Bitset {
33
33
  */
34
34
  addBit(bptr: BitPtr): void;
35
35
  /**
36
- * Clear bit `n`. Trailing zero words are trimmed so the internal storage
37
- * stays compact.
36
+ * Clear the bit described by `bptr` (fast path using a pre-computed
37
+ * {@link BitPtr}). Storage is not compacted automatically; call
38
+ * {@link compact} to trim trailing zero words when needed.
39
+ *
40
+ * @param bptr - Pre-computed pointer to a bit position.
41
+ */
42
+ deleteBit(bptr: BitPtr): void;
43
+ /**
44
+ * Clear bit `n`. Storage is not compacted automatically; call
45
+ * {@link compact} to trim trailing zero words when needed.
38
46
  *
39
47
  * @param n - Non-negative integer bit index.
40
48
  */
41
49
  delete(n: number): void;
50
+ /**
51
+ * Trim trailing zero words from the backing storage.
52
+ */
53
+ compact(): void;
54
+ /** Remove every set bit. */
55
+ clear(): void;
42
56
  /**
43
57
  * Return `true` when bit `n` is set.
44
58
  *
@@ -10,7 +10,7 @@
10
10
  * compact bit-flag fields:
11
11
  *
12
12
  * ```ts
13
- * class Tags extends Component {
13
+ * class Tags {
14
14
  * tags = new Bitset();
15
15
  * }
16
16
  *
@@ -29,9 +29,7 @@ export class Bitset {
29
29
  * @param n - Non-negative integer bit index.
30
30
  */
31
31
  add(n) {
32
- const arrayIndex = Math.floor(n / 32);
33
- const bitmask = 1 << (n % 32);
34
- this._addIndexBitmask(arrayIndex, bitmask);
32
+ this._bits[n >>> 5] |= 1 << n;
35
33
  }
36
34
  /**
37
35
  * Set the bit described by `bptr` (fast path using a pre-computed
@@ -40,40 +38,53 @@ export class Bitset {
40
38
  * @param bptr - Pre-computed pointer to a bit position.
41
39
  */
42
40
  addBit(bptr) {
43
- this._addIndexBitmask(bptr.arrayIndex, bptr.bitmask);
41
+ this._bits[bptr.arrayIndex] |= bptr.bitmask;
44
42
  }
45
43
  /**
46
- * Clear bit `n`. Trailing zero words are trimmed so the internal storage
47
- * stays compact.
44
+ * Clear the bit described by `bptr` (fast path using a pre-computed
45
+ * {@link BitPtr}). Storage is not compacted automatically; call
46
+ * {@link compact} to trim trailing zero words when needed.
47
+ *
48
+ * @param bptr - Pre-computed pointer to a bit position.
49
+ */
50
+ deleteBit(bptr) {
51
+ const current = this._bits[bptr.arrayIndex];
52
+ if (current) {
53
+ this._bits[bptr.arrayIndex] = current & ~bptr.bitmask;
54
+ }
55
+ }
56
+ /**
57
+ * Clear bit `n`. Storage is not compacted automatically; call
58
+ * {@link compact} to trim trailing zero words when needed.
48
59
  *
49
60
  * @param n - Non-negative integer bit index.
50
61
  */
51
62
  delete(n) {
52
- const arrayIndex = Math.floor(n / 32);
63
+ const arrayIndex = n >>> 5;
53
64
  const current = this._bits[arrayIndex];
54
- if (current === undefined) {
55
- return;
56
- }
57
- else {
58
- this._bits[arrayIndex] = current & ~(1 << (n % 32));
65
+ if (current) {
66
+ this._bits[arrayIndex] = current & ~(1 << n);
59
67
  }
68
+ }
69
+ /**
70
+ * Trim trailing zero words from the backing storage.
71
+ */
72
+ compact() {
60
73
  while (this._bits.length && this._bits[this._bits.length - 1] === 0) {
61
74
  this._bits.pop();
62
75
  }
63
76
  }
77
+ /** Remove every set bit. */
78
+ clear() {
79
+ this._bits.length = 0;
80
+ }
64
81
  /**
65
82
  * Return `true` when bit `n` is set.
66
83
  *
67
84
  * @param n - Non-negative integer bit index.
68
85
  */
69
86
  has(n) {
70
- const arrayIndex = Math.floor(n / 32);
71
- if (arrayIndex >= this._bits.length) {
72
- return false;
73
- }
74
- const bitIndex = n % 32;
75
- const bitmask = 1 << bitIndex;
76
- return this._hasIndexBitmask(arrayIndex, bitmask);
87
+ return (this._bits[n >>> 5] & (1 << n)) !== 0;
77
88
  }
78
89
  /**
79
90
  * Return `true` when the bit described by `bptr` is set (fast path).
@@ -81,7 +92,7 @@ export class Bitset {
81
92
  * @param bptr - Pre-computed pointer to a bit position.
82
93
  */
83
94
  hasBit(bptr) {
84
- return this._hasIndexBitmask(bptr.arrayIndex, bptr.bitmask);
95
+ return (this._bits[bptr.arrayIndex] & bptr.bitmask) !== 0;
85
96
  }
86
97
  /**
87
98
  * Return `true` when this bitset and `other` have exactly the same bits set.
@@ -89,7 +100,13 @@ export class Bitset {
89
100
  * @param other - Bitset to compare against.
90
101
  */
91
102
  equal(other) {
92
- return (this._bits.length === other._bits.length && this._bits.every((v, i) => other._bits[i] === v));
103
+ const maxLength = Math.max(this._bits.length, other._bits.length);
104
+ for (let i = 0; i < maxLength; i++) {
105
+ if ((this._bits[i] || 0) !== (other._bits[i] || 0)) {
106
+ return false;
107
+ }
108
+ }
109
+ return true;
93
110
  }
94
111
  /**
95
112
  * Return `true` when every bit set in `other` is also set in this bitset
@@ -101,11 +118,11 @@ export class Bitset {
101
118
  * @param other - Bitset whose set bits must all appear in this bitset.
102
119
  */
103
120
  hasBitset(other) {
104
- if (this._bits.length < other._bits.length) {
105
- return false;
106
- }
107
- for (let i = 0; i < other._bits.length; i++) {
108
- if ((this._bits[i] & other._bits[i]) !== (other._bits[i] || 0)) {
121
+ const bits = this._bits;
122
+ const otherBits = other._bits;
123
+ for (let i = 0; i < otherBits.length; i++) {
124
+ const otherWord = otherBits[i] | 0;
125
+ if ((bits[i] & otherWord) !== otherWord) {
109
126
  return false;
110
127
  }
111
128
  }
@@ -177,8 +194,8 @@ export class BitPtr {
177
194
  /** The raw bit index this pointer refers to. */
178
195
  value) {
179
196
  this.value = value;
180
- this.arrayIndex = Math.floor(value / 32);
181
- this.bitmask = 1 << (value % 32);
197
+ this.arrayIndex = value >>> 5;
198
+ this.bitmask = 1 << value;
182
199
  }
183
200
  /**
184
201
  * Return `true` when both pointers refer to the same bit position.
@@ -186,7 +203,7 @@ export class BitPtr {
186
203
  * @param other - Pointer to compare against.
187
204
  */
188
205
  equals(other) {
189
- return this.arrayIndex == other.arrayIndex && this.bitmask == other.bitmask;
206
+ return this.arrayIndex === other.arrayIndex && this.bitmask === other.bitmask;
190
207
  }
191
208
  }
192
209
  //# sourceMappingURL=bitset.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bitset.js","sourceRoot":"","sources":["../../src/util/bitset.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bitset.js","sourceRoot":"","sources":["../../src/util/bitset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,OAAO,MAAM;IAAnB;QACE,4DAA4D;QACrD,UAAK,GAAa,EAAE,CAAC;IA0K9B,CAAC;IAxKC;;;;OAIG;IACI,GAAG,CAAC,CAAS;QAClB,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,IAAY;QACxB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC;IAC9C,CAAC;IAED;;;;;;OAMG;IACI,SAAS,CAAC,IAAY;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,OAAO,EAAE;YACX,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC;SACvD;IACH,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,CAAS;QACrB,MAAM,UAAU,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,OAAO,EAAE;YACX,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;SAC9C;IACH,CAAC;IAED;;OAEG;IACI,OAAO;QACZ,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE;YACnE,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;SAClB;IACH,CAAC;IAED,4BAA4B;IACrB,KAAK;QACV,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACI,GAAG,CAAC,CAAS;QAClB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,IAAY;QACxB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,KAAa;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE;YAClC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE;gBAClD,OAAO,KAAK,CAAC;aACd;SACF;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;OAQG;IACI,SAAS,CAAC,KAAa;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC;QAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;YACzC,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACnC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,KAAK,SAAS,EAAE;gBACvC,OAAO,KAAK,CAAC;aACd;SACF;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACI,OAAO,CAAC,QAA6B;QAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;gBAC3B,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE;oBACjB,QAAQ,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;iBACtB;gBACD,CAAC,KAAK,CAAC,CAAC;aACT;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACI,OAAO;QACZ,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACjC,OAAO,GAAG,CAAC;IACb,CAAC;IAED;;;;;OAKG;IACI,gBAAgB,CAAC,UAAkB,EAAE,OAAe;QACzD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,OAAO,CAAC;IACpC,CAAC;IAED;;;;OAIG;IACI,gBAAgB,CAAC,UAAkB,EAAE,OAAe;QACzD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC;IACnC,CAAC;IAED;;;;;OAKG;IACI,gBAAgB,CAAC,UAAkB,EAAE,OAAe;QACzD,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;CACF;AAED;;;;;;;;;;GAUG;AACH,MAAM,OAAO,MAAM;IAMjB;IACE,gDAAgD;IAChC,KAAa;QAAb,UAAK,GAAL,KAAK,CAAQ;QAE7B,IAAI,CAAC,UAAU,GAAG,KAAK,KAAK,CAAC,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,KAAa;QACzB,OAAO,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC;IAChF,CAAC;CACF"}
package/dist/world.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Component, ComponentClassOrType, ComponentMeta, Hook } from "./component.js";
1
+ import { type ComponentClass, ComponentClassOrType, ComponentMeta, Hook } from "./component.js";
2
2
  import { Entity } from "./entity.js";
3
3
  import { Query } from "./query.js";
4
4
  import { System } from "./system.js";
@@ -23,6 +23,9 @@ import { IPhase } from "./phase.js";
23
23
  * ```ts
24
24
  * const world = new World();
25
25
  *
26
+ * class Position { x = 0; y = 0; }
27
+ * class Velocity { vx = 0; vy = 0; }
28
+ *
26
29
  * world.registerComponent(Position);
27
30
  * world.registerComponent(Velocity);
28
31
  *
@@ -30,6 +33,7 @@ import { IPhase } from "./phase.js";
30
33
  * .requires(Position, Velocity)
31
34
  * .each([Position, Velocity], (e, [pos, vel]) => {
32
35
  * pos.x += vel.vx;
36
+ * e.modified(Position);
33
37
  * });
34
38
  *
35
39
  * world.start();
@@ -48,6 +52,8 @@ import { IPhase } from "./phase.js";
48
52
  * drains the queue at top level.
49
53
  */
50
54
  export declare class World {
55
+ /** Hidden property key used to store this world's meta on component classes. */
56
+ readonly worldKey: string;
51
57
  constructor();
52
58
  /** Read-only view of the live entities, keyed by entity id. */
53
59
  get entities(): Omit<Map<number, Entity>, "set" | "delete" | "clear">;
@@ -102,9 +108,9 @@ export declare class World {
102
108
  /**
103
109
  * Register a component class with the world.
104
110
  *
105
- * Must be called before any entity uses the component. Registration is
106
- * disabled once {@link start} (or {@link disableComponentRegistration}) is
107
- * called.
111
+ * Must be called before any entity uses the component. Components are plain
112
+ * classes constructed with no arguments. Registration is disabled once
113
+ * {@link start} (or {@link disableComponentRegistration}) is called.
108
114
  *
109
115
  * **Overloads:**
110
116
  * - `registerComponent(Class)` — type id auto-assigned from the
@@ -116,13 +122,14 @@ export declare class World {
116
122
  * - `registerComponent(Class, type, componentName)` — explicit id + name.
117
123
  *
118
124
  * @param ComponentClass - Component class to register.
119
- * @throws When the class has already been registered or registration is
120
- * disabled.
125
+ * @returns The world-specific metadata record for the component class.
126
+ * @throws When the class has already been registered in this world or
127
+ * registration is disabled.
121
128
  */
122
- registerComponent(ComponentClass: typeof Component): void;
123
- registerComponent(ComponentClass: typeof Component, type: number): void;
124
- registerComponent(ComponentClass: typeof Component, componentName?: string): void;
125
- registerComponent(ComponentClass: typeof Component, type: number, componentName: string): void;
129
+ registerComponent(ComponentClass: ComponentClass): ComponentMeta;
130
+ registerComponent(ComponentClass: ComponentClass, type: number): ComponentMeta;
131
+ registerComponent(ComponentClass: ComponentClass, componentName?: string): ComponentMeta;
132
+ registerComponent(ComponentClass: ComponentClass, type: number, componentName: string): ComponentMeta;
126
133
  /**
127
134
  * Look up the {@link ComponentMeta} for a registered component.
128
135
  *
@@ -147,14 +154,14 @@ export declare class World {
147
154
  *
148
155
  * ```ts
149
156
  * world.hook(Sprite)
150
- * .onAdd(c => c.initialize(scene))
151
- * .onRemove(c => c.destroy());
157
+ * .onAdd((entity, c) => c.initialize(scene, entity))
158
+ * .onRemove((entity, c) => c.destroy(scene, entity));
152
159
  * ```
153
160
  *
154
161
  * @param C - Component class.
155
162
  * @returns The hook bound to that component type.
156
163
  */
157
- hook<T extends typeof Component>(C: T): Hook<InstanceType<T>>;
164
+ hook<T extends ComponentClass>(C: T): Hook<InstanceType<T>>;
158
165
  /**
159
166
  * Declare a group of mutually exclusive components.
160
167
  *
@@ -176,7 +183,7 @@ export declare class World {
176
183
  * @param components - Two or more component classes that cannot coexist.
177
184
  * @throws When any class has not been registered.
178
185
  */
179
- setExclusiveComponents(...components: (typeof Component)[]): void;
186
+ setExclusiveComponents(...components: ComponentClass[]): void;
180
187
  /**
181
188
  * Set the starting value of the auto-incrementing entity id counter.
182
189
  *
@@ -298,7 +305,7 @@ export declare class World {
298
305
  * guaranteed present (not validated at runtime).
299
306
  */
300
307
  filter<Q extends QueryDSL>(q: Q): Filter<ExtractRequired<Q>>;
301
- filter<T extends (typeof Component)[]>(q: QueryDSL, _guaranteed: readonly [...T]): Filter<T>;
308
+ filter<T extends ComponentClass[]>(q: QueryDSL, _guaranteed: readonly [...T]): Filter<T>;
302
309
  /**
303
310
  * Add a named phase to the update pipeline.
304
311
  *
@@ -334,22 +341,46 @@ export declare class World {
334
341
  */
335
342
  start(): void;
336
343
  /**
337
- * Execute every system in `phase` for one tick.
344
+ * Open a new frame and evaluate every registered tick source once.
345
+ *
346
+ * Call this before one or more {@link runPhase} calls when manually driving
347
+ * phases. {@link progress} wraps this automatically for the full pipeline.
348
+ *
349
+ * @param delta - Milliseconds elapsed since the previous frame.
350
+ * @throws When a frame is already open.
351
+ */
352
+ beginFrame(delta: number): void;
353
+ /**
354
+ * Close the current frame.
355
+ *
356
+ * @throws When no frame is currently open.
357
+ */
358
+ endFrame(): void;
359
+ /**
360
+ * Execute every system in `phase` within the current frame.
338
361
  *
339
362
  * Pending top-level mutations are drained before the first system runs so
340
363
  * each system observes a consistent world. Each system body executes in a
341
364
  * deferred scope; mutations made by callbacks land in the world queue and
342
365
  * are processed before the next system runs.
343
366
  *
367
+ * `runPhase` is safe to call re-entrantly from a system body: it reuses the
368
+ * frame opened by {@link beginFrame} and does not advance `_frameCounter` or
369
+ * re-evaluate tick sources.
370
+ *
344
371
  * @param phase - Phase reference returned from {@link addPhase}.
345
372
  * @param now - Absolute timestamp in milliseconds (e.g. `Date.now()`).
346
373
  * @param delta - Milliseconds elapsed since the previous tick.
374
+ * @throws When called outside an open frame.
347
375
  */
348
376
  runPhase(phase: IPhase, now: number, delta: number): void;
349
377
  /**
350
378
  * Run every phase in the pipeline in registration order.
351
379
  *
352
- * Equivalent to calling {@link runPhase} for each phase manually.
380
+ * Equivalent to `beginFrame(delta)`, calling {@link runPhase} for each
381
+ * phase, then {@link endFrame}. All registered tick sources are evaluated
382
+ * once up front for the whole frame, and the frame is closed in a `finally`
383
+ * block if a system throws.
353
384
  *
354
385
  * @param now - Absolute timestamp in milliseconds (e.g. `Date.now()`).
355
386
  * @param delta - Milliseconds elapsed since the previous tick.