@oasys/oecs 0.2.1 → 0.3.0

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/LICENSE +1 -1
  2. package/README.md +169 -182
  3. package/dist/archetype.d.ts +12 -9
  4. package/dist/archetype.d.ts.map +1 -1
  5. package/dist/component.d.ts +10 -0
  6. package/dist/component.d.ts.map +1 -1
  7. package/dist/ecs.d.ts +14 -14
  8. package/dist/ecs.d.ts.map +1 -1
  9. package/dist/entity.d.ts.map +1 -1
  10. package/dist/event.d.ts +6 -0
  11. package/dist/event.d.ts.map +1 -1
  12. package/dist/index.cjs +1 -1
  13. package/dist/index.d.ts +8 -6
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +621 -459
  16. package/dist/query.d.ts +36 -16
  17. package/dist/query.d.ts.map +1 -1
  18. package/dist/ref.d.ts +4 -0
  19. package/dist/ref.d.ts.map +1 -1
  20. package/dist/resource.d.ts +21 -21
  21. package/dist/resource.d.ts.map +1 -1
  22. package/dist/schedule.d.ts +8 -6
  23. package/dist/schedule.d.ts.map +1 -1
  24. package/dist/store.d.ts +13 -6
  25. package/dist/store.d.ts.map +1 -1
  26. package/dist/system.d.ts.map +1 -1
  27. package/dist/type_primitives/assertions.d.ts +1 -0
  28. package/dist/type_primitives/assertions.d.ts.map +1 -1
  29. package/dist/type_primitives/binary_heap/binary_heap.d.ts +33 -0
  30. package/dist/type_primitives/binary_heap/binary_heap.d.ts.map +1 -0
  31. package/dist/type_primitives/bitset/bitset.d.ts +5 -0
  32. package/dist/type_primitives/bitset/bitset.d.ts.map +1 -1
  33. package/dist/type_primitives/error.d.ts +2 -1
  34. package/dist/type_primitives/error.d.ts.map +1 -1
  35. package/dist/type_primitives/index.d.ts +2 -0
  36. package/dist/type_primitives/index.d.ts.map +1 -1
  37. package/dist/type_primitives/topological_sort/topological_sort.d.ts +25 -0
  38. package/dist/type_primitives/topological_sort/topological_sort.d.ts.map +1 -0
  39. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +2 -0
  40. package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -1
  41. package/dist/utils/arrays.d.ts.map +1 -1
  42. package/dist/utils/constants.d.ts +0 -8
  43. package/dist/utils/constants.d.ts.map +1 -1
  44. package/dist/utils/error.d.ts +4 -1
  45. package/dist/utils/error.d.ts.map +1 -1
  46. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,34 +1,34 @@
1
- class H extends Error {
1
+ class J extends Error {
2
2
  constructor(t, e, s) {
3
3
  super(t), this.is_operational = e, this.context = s, this.name = this.constructor.name, Error.captureStackTrace(this, this.constructor);
4
4
  }
5
5
  }
6
- var z = /* @__PURE__ */ ((l) => (l.EID_MAX_INDEX_OVERFLOW = "EID_MAX_INDEX_OVERFLOW", l.EID_MAX_GEN_OVERFLOW = "EID_MAX_GEN_OVERFLOW", l.COMPONENT_NOT_REGISTERED = "COMPONENT_NOT_REGISTERED", l.ENTITY_NOT_ALIVE = "ENTITY_NOT_ALIVE", l.CIRCULAR_SYSTEM_DEPENDENCY = "CIRCULAR_SYSTEM_DEPENDENCY", l.DUPLICATE_SYSTEM = "DUPLICATE_SYSTEM", l.ARCHETYPE_NOT_FOUND = "ARCHETYPE_NOT_FOUND", l.RESOURCE_NOT_REGISTERED = "RESOURCE_NOT_REGISTERED", l))(z || {});
7
- class Q extends H {
6
+ var D = /* @__PURE__ */ ((a) => (a.EID_MAX_INDEX_OVERFLOW = "EID_MAX_INDEX_OVERFLOW", a.EID_MAX_GEN_OVERFLOW = "EID_MAX_GEN_OVERFLOW", a.COMPONENT_NOT_REGISTERED = "COMPONENT_NOT_REGISTERED", a.ENTITY_NOT_ALIVE = "ENTITY_NOT_ALIVE", a.CIRCULAR_SYSTEM_DEPENDENCY = "CIRCULAR_SYSTEM_DEPENDENCY", a.DUPLICATE_SYSTEM = "DUPLICATE_SYSTEM", a.ARCHETYPE_NOT_FOUND = "ARCHETYPE_NOT_FOUND", a.RESOURCE_NOT_REGISTERED = "RESOURCE_NOT_REGISTERED", a.RESOURCE_ALREADY_REGISTERED = "RESOURCE_ALREADY_REGISTERED", a.EVENT_ALREADY_REGISTERED = "EVENT_ALREADY_REGISTERED", a.EVENT_NOT_REGISTERED = "EVENT_NOT_REGISTERED", a))(D || {});
7
+ class O extends J {
8
8
  constructor(t, e, s) {
9
9
  super(e ?? t, !0, s), this.category = t;
10
10
  }
11
11
  }
12
- function N(l, t, e) {
13
- return l;
12
+ function F(a, t, e) {
13
+ return a;
14
14
  }
15
- const v = -1, y = -1, D = Object.freeze(/* @__PURE__ */ Object.create(null)), k = 5, I = 31, J = 2166136261, Z = 16777619, tt = 2654435769, et = 1367130551, b = 16, C = 2, V = 1024, R = 0, j = 0, st = 31, nt = 1 / 60, rt = 4, it = 0, ot = 4;
16
- class x {
15
+ const I = 5, S = 31, Z = 2166136261, tt = 16777619, et = 4;
16
+ class k {
17
17
  _words;
18
18
  constructor(t) {
19
- this._words = t ?? new Array(ot).fill(0);
19
+ this._words = t ?? new Array(et).fill(0);
20
20
  }
21
21
  has(t) {
22
- const e = t >>> k;
23
- return e >= this._words.length ? !1 : (this._words[e] & 1 << (t & I)) !== 0;
22
+ const e = t >>> I;
23
+ return e >= this._words.length ? !1 : (this._words[e] & 1 << (t & S)) !== 0;
24
24
  }
25
25
  set(t) {
26
- const e = t >>> k;
27
- e >= this._words.length && this.grow(e + 1), this._words[e] |= 1 << (t & I);
26
+ const e = t >>> I;
27
+ e >= this._words.length && this.grow(e + 1), this._words[e] |= 1 << (t & S);
28
28
  }
29
29
  clear(t) {
30
- const e = t >>> k;
31
- e >= this._words.length || (this._words[e] &= ~(1 << (t & I)));
30
+ const e = t >>> I;
31
+ e >= this._words.length || (this._words[e] &= ~(1 << (t & S)));
32
32
  }
33
33
  /** True if any bit is set in both this and other (non-empty intersection). */
34
34
  overlaps(t) {
@@ -50,31 +50,31 @@ class x {
50
50
  equals(t) {
51
51
  const e = this._words, s = t._words, n = e.length > s.length ? e.length : s.length;
52
52
  for (let r = 0; r < n; r++) {
53
- const i = r < e.length ? e[r] : 0, _ = r < s.length ? s[r] : 0;
54
- if (i !== _) return !1;
53
+ const i = r < e.length ? e[r] : 0, c = r < s.length ? s[r] : 0;
54
+ if (i !== c) return !1;
55
55
  }
56
56
  return !0;
57
57
  }
58
58
  copy() {
59
- return new x(this._words.slice());
59
+ return new k(this._words.slice());
60
60
  }
61
61
  copy_with_set(t) {
62
- const e = t >>> k, s = e + 1, n = this._words.length > s ? this._words.length : s, r = new Array(n).fill(0);
62
+ const e = t >>> I, s = e + 1, n = this._words.length > s ? this._words.length : s, r = new Array(n).fill(0);
63
63
  for (let i = 0; i < this._words.length; i++) r[i] = this._words[i];
64
- return r[e] |= 1 << (t & I), new x(r);
64
+ return r[e] |= 1 << (t & S), new k(r);
65
65
  }
66
66
  copy_with_clear(t) {
67
- const e = this._words.slice(), s = t >>> k;
68
- return s < e.length && (e[s] &= ~(1 << (t & I))), new x(e);
67
+ const e = this._words.slice(), s = t >>> I;
68
+ return s < e.length && (e[s] &= ~(1 << (t & S))), new k(e);
69
69
  }
70
70
  /** FNV-1a hash. Skips trailing zero words so differently-sized arrays with the same bits hash equally. */
71
71
  hash() {
72
- let t = J;
72
+ let t = Z;
73
73
  const e = this._words;
74
74
  let s = e.length - 1;
75
75
  for (; s >= 0 && e[s] === 0; ) s--;
76
76
  for (let n = 0; n <= s; n++)
77
- t ^= e[n], t = Math.imul(t, Z);
77
+ t ^= e[n], t = Math.imul(t, tt);
78
78
  return t;
79
79
  }
80
80
  /** Iterate all set bits via lowest-set-bit extraction. */
@@ -83,10 +83,10 @@ class x {
83
83
  for (let s = 0; s < e.length; s++) {
84
84
  let n = e[s];
85
85
  if (n === 0) continue;
86
- const r = s << k;
86
+ const r = s << I;
87
87
  for (; n !== 0; ) {
88
- const i = n & -n >>> 0, _ = I - Math.clz32(i);
89
- t(r + _), n ^= i;
88
+ const i = n & -n >>> 0, c = S - Math.clz32(i);
89
+ t(r + c), n ^= i;
90
90
  }
91
91
  }
92
92
  }
@@ -98,8 +98,9 @@ class x {
98
98
  this._words = s;
99
99
  }
100
100
  }
101
+ const b = 16, X = 2;
101
102
  class A {
102
- constructor(t, e = b) {
103
+ constructor(t, e = 16) {
103
104
  this._ctor = t, this._buf = new t(e);
104
105
  }
105
106
  _buf;
@@ -158,7 +159,7 @@ class A {
158
159
  ensure_capacity(t) {
159
160
  if (t <= this._buf.length) return;
160
161
  let e = this._buf.length || 1;
161
- for (; e < t; ) e *= C;
162
+ for (; e < t; ) e *= X;
162
163
  const s = new this._ctor(e);
163
164
  s.set(this._buf.subarray(0, this._len)), this._buf = s;
164
165
  }
@@ -174,65 +175,151 @@ class A {
174
175
  this.ensure_capacity(this._len + t), this._buf.fill(0, this._len, this._len + t), this._len += t;
175
176
  }
176
177
  _grow() {
177
- const t = new this._ctor(this._buf.length * C);
178
+ const t = new this._ctor(this._buf.length * X);
178
179
  t.set(this._buf), this._buf = t;
179
180
  }
180
181
  }
181
- class _t extends A {
182
+ class st extends A {
182
183
  constructor(t = b) {
183
184
  super(Float32Array, t);
184
185
  }
185
186
  }
186
- class ct extends A {
187
+ class nt extends A {
187
188
  constructor(t = b) {
188
189
  super(Float64Array, t);
189
190
  }
190
191
  }
191
- class ht extends A {
192
+ class rt extends A {
192
193
  constructor(t = b) {
193
194
  super(Int8Array, t);
194
195
  }
195
196
  }
196
- class lt extends A {
197
+ class it extends A {
197
198
  constructor(t = b) {
198
199
  super(Int16Array, t);
199
200
  }
200
201
  }
201
- class at extends A {
202
+ class _t extends A {
202
203
  constructor(t = b) {
203
204
  super(Int32Array, t);
204
205
  }
205
206
  }
206
- class dt extends A {
207
+ class ot extends A {
207
208
  constructor(t = b) {
208
209
  super(Uint8Array, t);
209
210
  }
210
211
  }
211
- class ut extends A {
212
+ class ct extends A {
212
213
  constructor(t = b) {
213
214
  super(Uint16Array, t);
214
215
  }
215
216
  }
216
- class B extends A {
217
+ class $ extends A {
217
218
  constructor(t = b) {
218
219
  super(Uint32Array, t);
219
220
  }
220
221
  }
221
- const ft = {
222
- f32: _t,
223
- f64: ct,
224
- i8: ht,
225
- i16: lt,
226
- i32: at,
227
- u8: dt,
228
- u16: ut,
229
- u32: B
230
- }, P = 20, F = (1 << P) - 1, gt = st - P, X = (1 << gt) - 1, mt = (l, t) => t << P | l, m = (l) => l & F, Y = (l) => l >> P, yt = (l) => N(
231
- l
232
- ), pt = (l) => N(
233
- l
222
+ const ht = {
223
+ f32: st,
224
+ f64: nt,
225
+ i8: rt,
226
+ i16: it,
227
+ i32: _t,
228
+ u8: ot,
229
+ u16: ct,
230
+ u32: $
231
+ };
232
+ class at {
233
+ _compare;
234
+ _data = [];
235
+ constructor(t) {
236
+ this._compare = t;
237
+ }
238
+ get size() {
239
+ return this._data.length;
240
+ }
241
+ /** Returns the highest-priority element without removing it, or undefined if empty. */
242
+ peek() {
243
+ return this._data[0];
244
+ }
245
+ /** Inserts a value and restores heap order by sifting up. O(log n). */
246
+ push(t) {
247
+ this._data.push(t), this._sift_up(this._data.length - 1);
248
+ }
249
+ /** Removes and returns the highest-priority element, or undefined if empty. O(log n). */
250
+ pop() {
251
+ const t = this._data;
252
+ if (t.length === 0) return;
253
+ const s = t[0], n = t.pop();
254
+ return t.length > 0 && (t[0] = n, this._sift_down(0)), s;
255
+ }
256
+ /** Removes all elements. O(1). */
257
+ clear() {
258
+ this._data.length = 0;
259
+ }
260
+ /** Moves a node up toward the root until heap order is restored. */
261
+ _sift_up(t) {
262
+ const e = this._data, s = this._compare, n = e[t];
263
+ for (; t > 0; ) {
264
+ const r = t - 1 >> 1;
265
+ if (s(n, e[r]) >= 0) break;
266
+ e[t] = e[r], t = r;
267
+ }
268
+ e[t] = n;
269
+ }
270
+ /** Moves a node down away from the root until heap order is restored. */
271
+ _sift_down(t) {
272
+ const e = this._data, s = this._compare, n = e.length, r = n >> 1, i = e[t];
273
+ for (; t < r; ) {
274
+ let c = (t << 1) + 1;
275
+ const o = c + 1;
276
+ if (o < n && s(e[o], e[c]) < 0 && (c = o), s(e[c], i) >= 0) break;
277
+ e[t] = e[c], t = c;
278
+ }
279
+ e[t] = i;
280
+ }
281
+ }
282
+ function lt(a, t, e, s) {
283
+ const n = s ?? ((o) => String(o)), r = /* @__PURE__ */ new Map();
284
+ for (let o = 0; o < a.length; o++)
285
+ r.set(a[o], 0);
286
+ for (const [, o] of t)
287
+ for (let _ = 0; _ < o.length; _++) {
288
+ const h = o[_];
289
+ r.has(h) && r.set(h, r.get(h) + 1);
290
+ }
291
+ const i = new at(e);
292
+ for (let o = 0; o < a.length; o++)
293
+ r.get(a[o]) === 0 && i.push(a[o]);
294
+ const c = [];
295
+ for (; i.size > 0; ) {
296
+ const o = i.pop();
297
+ c.push(o);
298
+ const _ = t.get(o);
299
+ if (_ !== void 0)
300
+ for (let h = 0; h < _.length; h++) {
301
+ const l = _[h];
302
+ if (!r.has(l)) continue;
303
+ const d = r.get(l) - 1;
304
+ r.set(l, d), d === 0 && i.push(l);
305
+ }
306
+ }
307
+ if (c.length !== a.length) {
308
+ const o = [];
309
+ for (const [_, h] of r)
310
+ h > 0 && o.push(n(_));
311
+ throw new globalThis.TypeError(
312
+ `Cycle detected in topological sort. Nodes still pending: ${o.join(", ")}`
313
+ );
314
+ }
315
+ return c;
316
+ }
317
+ const E = -1, m = -1, U = Object.freeze(/* @__PURE__ */ Object.create(null)), dt = 2654435769, ut = 1367130551, H = 1024, j = 0, ft = 31, gt = 1 / 60, yt = 4, mt = 0, R = 20, G = (1 << R) - 1, pt = ft - R, V = (1 << pt) - 1, wt = (a, t) => t << R | a, y = (a) => a & G, z = (a) => a >> R, vt = (a) => F(
318
+ a
319
+ ), Et = (a) => F(
320
+ a
234
321
  );
235
- class wt {
322
+ class kt {
236
323
  field_names;
237
324
  columns;
238
325
  // any: type-erased storage — channel is stored in Map<number, EventChannel>, F is lost
@@ -263,47 +350,16 @@ class wt {
263
350
  t[e].length = 0;
264
351
  }
265
352
  }
266
- const vt = (l) => N(
267
- l
268
- );
269
- class xt {
270
- field_names;
271
- field_index;
272
- columns;
273
- // any: type-erased storage — channel is stored in Map<number, ResourceChannel>, F is lost
274
- reader;
275
- constructor(t, e) {
276
- this.field_names = t, this.field_index = /* @__PURE__ */ Object.create(null), this.columns = [];
277
- for (let r = 0; r < t.length; r++)
278
- this.field_index[t[r]] = r, this.columns.push([e[t[r]] ?? 0]);
279
- const s = /* @__PURE__ */ Object.create(null), n = this.columns;
280
- for (let r = 0; r < t.length; r++) {
281
- const i = n[r];
282
- Object.defineProperty(s, t[r], {
283
- get() {
284
- return i[R];
285
- },
286
- enumerable: !0
287
- });
288
- }
289
- this.reader = s;
290
- }
291
- write(t) {
292
- const e = this.field_names, s = this.columns;
293
- for (let n = 0; n < e.length; n++)
294
- e[n] in t && (s[n][R] = t[e[n]]);
295
- }
296
- read_field(t) {
297
- return this.columns[t][R];
298
- }
299
- write_field(t, e) {
300
- this.columns[t][R] = e;
301
- }
353
+ function Ot(a) {
354
+ return Symbol(a);
302
355
  }
303
- const bt = (l) => N(
304
- l
356
+ function Ut(a) {
357
+ return Symbol(a);
358
+ }
359
+ const Tt = (a) => F(
360
+ a
305
361
  );
306
- class Tt {
362
+ class bt {
307
363
  id;
308
364
  mask;
309
365
  has_columns;
@@ -325,17 +381,19 @@ class Tt {
325
381
  column_groups = [];
326
382
  // Dense list of ComponentIDs that have columns — used for copy_shared_from.
327
383
  _column_ids = [];
328
- constructor(t, e, s, n = V) {
329
- if (this.id = t, this.mask = e, this._entity_ids = new B(n), s) {
384
+ // Sparse by ComponentID last tick that modified this component's columns.
385
+ _changed_tick = [];
386
+ constructor(t, e, s, n = H) {
387
+ if (this.id = t, this.mask = e, this._entity_ids = new $(n), s) {
330
388
  let r = 0;
331
389
  for (let i = 0; i < s.length; i++) {
332
- const _ = s[i], c = _.component_id, o = new Array(_.field_names.length);
333
- this._col_offset[c] = r, this._field_count[c] = _.field_names.length, this._field_index[c] = _.field_index, this._field_names[c] = _.field_names;
334
- for (let h = 0; h < _.field_names.length; h++) {
335
- const a = new ft[_.field_types[h]](n);
336
- o[h] = a, this._flat_columns[r++] = a;
390
+ const c = s[i], o = c.component_id, _ = new Array(c.field_names.length);
391
+ this._col_offset[o] = r, this._field_count[o] = c.field_names.length, this._field_index[o] = c.field_index, this._field_names[o] = c.field_names;
392
+ for (let h = 0; h < c.field_names.length; h++) {
393
+ const l = new ht[c.field_types[h]](n);
394
+ _[h] = l, this._flat_columns[r++] = l;
337
395
  }
338
- this.column_groups[c] = { layout: _, columns: o }, this._column_ids.push(c);
396
+ this.column_groups[o] = { layout: c, columns: _ }, this._column_ids.push(o), this._changed_tick[o] = 0;
339
397
  }
340
398
  }
341
399
  this.has_columns = this._column_ids.length > 0;
@@ -356,25 +414,34 @@ class Tt {
356
414
  matches(t) {
357
415
  return this.mask.contains(t);
358
416
  }
359
- /** Get a single field's column. Valid data: indices 0..entity_count-1. */
417
+ /** Get a single field's column (read-only). Valid data: indices 0..entity_count-1. */
360
418
  get_column(t, e) {
361
419
  const s = t, n = this._field_index[s][e];
362
420
  return this._flat_columns[this._col_offset[s] + n].buf;
363
421
  }
364
- write_fields(t, e, s) {
365
- const n = e, r = this._col_offset[n];
366
- if (r === void 0) return;
367
- const i = this._field_names[n], _ = this._flat_columns;
368
- for (let c = 0; c < i.length; c++)
369
- _[r + c].buf[t] = s[i[c]];
422
+ /** Get a single field's column (mutable). Marks the component as changed at the given tick. */
423
+ get_column_mut(t, e, s) {
424
+ const n = t;
425
+ this._changed_tick[n] = s;
426
+ const r = this._field_index[n][e];
427
+ return this._flat_columns[this._col_offset[n] + r].buf;
428
+ }
429
+ write_fields(t, e, s, n) {
430
+ const r = e, i = this._col_offset[r];
431
+ if (i === void 0) return;
432
+ this._changed_tick[r] = n;
433
+ const c = this._field_names[r], o = this._flat_columns;
434
+ for (let _ = 0; _ < c.length; _++)
435
+ o[i + _].buf[t] = s[c[_]];
370
436
  }
371
437
  /** Fast positional write: values[i] → field[i] in declaration order. No string lookup. */
372
- write_fields_positional(t, e, s) {
373
- const n = e, r = this._col_offset[n];
374
- if (r === void 0) return;
375
- const i = this._flat_columns;
376
- for (let _ = 0; _ < s.length; _++)
377
- i[r + _].buf[t] = s[_];
438
+ write_fields_positional(t, e, s, n) {
439
+ const r = e, i = this._col_offset[r];
440
+ if (i === void 0) return;
441
+ this._changed_tick[r] = n;
442
+ const c = this._flat_columns;
443
+ for (let o = 0; o < s.length; o++)
444
+ c[i + o].buf[t] = s[o];
378
445
  }
379
446
  read_field(t, e, s) {
380
447
  const n = e, r = this._col_offset[n];
@@ -383,14 +450,15 @@ class Tt {
383
450
  return i === void 0 ? NaN : this._flat_columns[r + i].buf[t];
384
451
  }
385
452
  /** Copy all shared component columns from source archetype at src_row into dst_row. */
386
- copy_shared_from(t, e, s) {
387
- const n = t._col_offset, r = t._field_count, i = t._flat_columns, _ = this._flat_columns, c = this._column_ids;
388
- for (let o = 0; o < c.length; o++) {
389
- const h = c[o], a = n[h];
390
- if (a === void 0) continue;
391
- const u = this._col_offset[h], d = r[h];
392
- for (let g = 0; g < d; g++)
393
- _[u + g].buf[s] = i[a + g].buf[e];
453
+ copy_shared_from(t, e, s, n) {
454
+ const r = t._col_offset, i = t._field_count, c = t._flat_columns, o = this._flat_columns, _ = this._column_ids;
455
+ for (let h = 0; h < _.length; h++) {
456
+ const l = _[h], d = r[l];
457
+ if (d === void 0) continue;
458
+ this._changed_tick[l] = n;
459
+ const u = this._col_offset[l], g = i[l];
460
+ for (let p = 0; p < g; p++)
461
+ o[u + p].buf[s] = c[d + p].buf[e];
394
462
  }
395
463
  }
396
464
  /**
@@ -412,10 +480,10 @@ class Tt {
412
480
  */
413
481
  remove_entity(t) {
414
482
  const e = this.length - 1;
415
- let s = y;
483
+ let s = m;
416
484
  const n = this._flat_columns, r = this._entity_ids.buf;
417
485
  if (t !== e) {
418
- r[t] = r[e], s = m(r[t]);
486
+ r[t] = r[e], s = y(r[t]);
419
487
  for (let i = 0; i < n.length; i++)
420
488
  n[i].swap_remove(t);
421
489
  } else
@@ -431,9 +499,9 @@ class Tt {
431
499
  /** Tag-optimized remove via swap-and-pop: skip column swap/pop entirely. */
432
500
  remove_entity_tag(t) {
433
501
  const e = this.length - 1;
434
- let s = y;
502
+ let s = m;
435
503
  const n = this._entity_ids.buf;
436
- return t !== e && (n[t] = n[e], s = m(n[t])), this._entity_ids.pop(), this.length--, s;
504
+ return t !== e && (n[t] = n[e], s = y(n[t])), this._entity_ids.pop(), this.length--, s;
437
505
  }
438
506
  /**
439
507
  * Move an entity from src archetype into this archetype in a single pass.
@@ -441,17 +509,20 @@ class Tt {
441
509
  * Uses a pre-computed transition map for branchless column copy.
442
510
  * Writes dst_row to _move_result[0], swapped entity index to _move_result[1].
443
511
  */
444
- move_entity_from(t, e, s, n) {
445
- const r = this.length;
512
+ move_entity_from(t, e, s, n, r) {
513
+ const i = this.length;
446
514
  this._entity_ids.push(s);
447
- const i = this._flat_columns, _ = t._flat_columns;
448
- for (let o = 0; o < i.length; o++) {
449
- const h = n[o];
450
- i[o].push(h >= 0 ? _[h].buf[e] : 0);
515
+ const c = this._flat_columns, o = t._flat_columns;
516
+ for (let l = 0; l < c.length; l++) {
517
+ const d = n[l];
518
+ c[l].push(d >= 0 ? o[d].buf[e] : 0);
451
519
  }
520
+ const _ = this._column_ids;
521
+ for (let l = 0; l < _.length; l++)
522
+ this._changed_tick[_[l]] = r;
452
523
  this.length++;
453
- const c = t.has_columns ? t.remove_entity(e) : t.remove_entity_tag(e);
454
- f[0] = r, f[1] = c;
524
+ const h = t.has_columns ? t.remove_entity(e) : t.remove_entity_tag(e);
525
+ f[0] = i, f[1] = h;
455
526
  }
456
527
  /**
457
528
  * Move an entity from src into this archetype (tag-only: no columns to copy).
@@ -468,19 +539,22 @@ class Tt {
468
539
  * Much faster than per-entity move_entity_from when the entire source is moving.
469
540
  * After this call, src is empty. Returns the starting dst_row for the batch.
470
541
  */
471
- bulk_move_all_from(t, e) {
472
- const s = t.length;
473
- if (s === 0) return this.length;
474
- const n = this.length, r = this._flat_columns, i = t._flat_columns;
475
- this._entity_ids.bulk_append(t._entity_ids.buf, 0, s);
476
- for (let _ = 0; _ < r.length; _++) {
477
- const c = e[_];
478
- c >= 0 ? r[_].bulk_append(i[c].buf, 0, s) : r[_].bulk_append_zeroes(s);
542
+ bulk_move_all_from(t, e, s) {
543
+ const n = t.length;
544
+ if (n === 0) return this.length;
545
+ const r = this.length, i = this._flat_columns, c = t._flat_columns;
546
+ this._entity_ids.bulk_append(t._entity_ids.buf, 0, n);
547
+ for (let _ = 0; _ < i.length; _++) {
548
+ const h = e[_];
549
+ h >= 0 ? i[_].bulk_append(c[h].buf, 0, n) : i[_].bulk_append_zeroes(n);
479
550
  }
480
- this.length += s, t.length = 0, t._entity_ids.clear();
481
- for (let _ = 0; _ < i.length; _++)
482
- i[_].clear();
483
- return n;
551
+ const o = this._column_ids;
552
+ for (let _ = 0; _ < o.length; _++)
553
+ this._changed_tick[o[_]] = s;
554
+ this.length += n, t.length = 0, t._entity_ids.clear();
555
+ for (let _ = 0; _ < c.length; _++)
556
+ c[_].clear();
557
+ return r;
484
558
  }
485
559
  get_edge(t) {
486
560
  return this.edges[t];
@@ -489,23 +563,23 @@ class Tt {
489
563
  this.edges[t] = e;
490
564
  }
491
565
  }
492
- const f = [0, y];
493
- function M(l, t) {
494
- const e = t._flat_columns, s = new Int16Array(e.length), n = t._column_ids, r = l._col_offset, i = t._col_offset, _ = t._field_count;
495
- for (let c = 0; c < n.length; c++) {
496
- const o = n[c], h = i[o], a = _[o], u = r[o];
497
- if (u !== void 0)
498
- for (let d = 0; d < a; d++)
499
- s[h + d] = u + d;
566
+ const f = [0, m];
567
+ function M(a, t) {
568
+ const e = t._flat_columns, s = new Int16Array(e.length), n = t._column_ids, r = a._col_offset, i = t._col_offset, c = t._field_count;
569
+ for (let o = 0; o < n.length; o++) {
570
+ const _ = n[o], h = i[_], l = c[_], d = r[_];
571
+ if (d !== void 0)
572
+ for (let u = 0; u < l; u++)
573
+ s[h + u] = d + u;
500
574
  else
501
- for (let d = 0; d < a; d++)
502
- s[h + d] = -1;
575
+ for (let u = 0; u < l; u++)
576
+ s[h + u] = -1;
503
577
  }
504
578
  return s;
505
579
  }
506
- function K(l, t, e) {
507
- const s = l.get(t);
508
- s !== void 0 ? s.push(e) : l.set(t, [e]);
580
+ function K(a, t, e) {
581
+ const s = a.get(t);
582
+ s !== void 0 ? s.push(e) : a.set(t, [e]);
509
583
  }
510
584
  class At {
511
585
  // --- Entity ID management ---
@@ -524,10 +598,6 @@ class At {
524
598
  // Parallel array indexed by EventID: each channel holds SoA columns + reader.
525
599
  event_channels = [];
526
600
  event_count = 0;
527
- // --- Resource channels ---
528
- // Parallel array indexed by ResourceID: each channel holds a single row of SoA columns.
529
- resource_channels = [];
530
- resource_count = 0;
531
601
  // --- Archetype management ---
532
602
  archetypes = [];
533
603
  // Hash-bucketed lookup: BitSet.hash() → ArchetypeID[] for deduplication
@@ -553,9 +623,10 @@ class At {
553
623
  pending_add_values = [];
554
624
  pending_remove_ids = [];
555
625
  pending_remove_defs = [];
626
+ _tick = 0;
556
627
  initial_capacity;
557
628
  constructor(t) {
558
- this.initial_capacity = t ?? V, this.empty_archetype_id = this.arch_get_or_create_from_mask(new x());
629
+ this.initial_capacity = t ?? H, this.empty_archetype_id = this.arch_get_or_create_from_mask(new k());
559
630
  }
560
631
  // =======================================================
561
632
  // Archetype graph
@@ -570,30 +641,30 @@ class At {
570
641
  arch_get_or_create_from_mask(t) {
571
642
  const e = t.hash(), s = this.archetype_map.get(e);
572
643
  if (s !== void 0) {
573
- for (let c = 0; c < s.length; c++)
574
- if (this.archetypes[s[c]].mask.equals(t))
575
- return s[c];
644
+ for (let o = 0; o < s.length; o++)
645
+ if (this.archetypes[s[o]].mask.equals(t))
646
+ return s[o];
576
647
  }
577
- const n = bt(this.next_archetype_id++), r = [];
578
- t.for_each((c) => {
579
- const o = c, h = this.component_metas[o];
648
+ const n = Tt(this.next_archetype_id++), r = [];
649
+ t.for_each((o) => {
650
+ const _ = o, h = this.component_metas[_];
580
651
  h && h.field_names.length > 0 && r.push({
581
- component_id: o,
652
+ component_id: _,
582
653
  field_names: h.field_names,
583
654
  field_index: h.field_index,
584
655
  field_types: h.field_types
585
656
  });
586
657
  });
587
- const i = new Tt(n, t, r, this.initial_capacity);
588
- this.archetypes.push(i), K(this.archetype_map, e, n), t.for_each((c) => {
589
- const o = c;
590
- let h = this.component_index.get(o);
591
- h || (h = /* @__PURE__ */ new Set(), this.component_index.set(o, h)), h.add(n);
658
+ const i = new bt(n, t, r, this.initial_capacity);
659
+ this.archetypes.push(i), K(this.archetype_map, e, n), t.for_each((o) => {
660
+ const _ = o;
661
+ let h = this.component_index.get(_);
662
+ h || (h = /* @__PURE__ */ new Set(), this.component_index.set(_, h)), h.add(n);
592
663
  });
593
- const _ = this.registered_queries;
594
- for (let c = 0; c < _.length; c++) {
595
- const o = _[c];
596
- i.matches(o.include_mask) && (!o.exclude_mask || !i.mask.overlaps(o.exclude_mask)) && (!o.any_of_mask || i.mask.overlaps(o.any_of_mask)) && o.result.push(i);
664
+ const c = this.registered_queries;
665
+ for (let o = 0; o < c.length; o++) {
666
+ const _ = c[o];
667
+ i.matches(_.include_mask) && (!_.exclude_mask || !i.mask.overlaps(_.exclude_mask)) && (!_.any_of_mask || i.mask.overlaps(_.any_of_mask)) && (_.result.push(i), _.query?.mark_non_empty_dirty());
597
668
  }
598
669
  return n;
599
670
  }
@@ -642,29 +713,34 @@ class At {
642
713
  create_entity() {
643
714
  let t, e;
644
715
  this.entity_free_indices.length > 0 ? (t = this.entity_free_indices.pop(), e = this.entity_generations[t]) : (t = this.entity_high_water++, this.entity_generations[t] = j, e = j), this.entity_alive_count++;
645
- const s = mt(t, e);
646
- return this.entity_archetype[t] = this.empty_archetype_id, this.entity_row[t] = v, s;
716
+ const s = wt(t, e);
717
+ return this.entity_archetype[t] = this.empty_archetype_id, this.entity_row[t] = E, s;
647
718
  }
648
719
  /** Immediately destroy an entity, removing it from its archetype. */
649
720
  destroy_entity(t) {
650
721
  if (!this.is_alive(t))
651
722
  return;
652
- const e = m(t), s = this.entity_row[e];
653
- if (s !== v) {
723
+ const e = y(t), s = this.entity_row[e];
724
+ if (s !== E) {
654
725
  const i = this.arch_get(this.entity_archetype[e]).remove_entity(s);
655
- i !== y && (this.entity_row[i] = s);
726
+ i !== m && (this.entity_row[i] = s);
656
727
  }
657
- this.entity_archetype[e] = v, this.entity_row[e] = v;
658
- const n = Y(t);
659
- this.entity_generations[e] = n + 1 & X, this.entity_free_indices.push(e), this.entity_alive_count--;
728
+ this.entity_archetype[e] = E, this.entity_row[e] = E;
729
+ const n = z(t);
730
+ this.entity_generations[e] = n + 1 & V, this.entity_free_indices.push(e), this.entity_alive_count--;
660
731
  }
661
732
  is_alive(t) {
662
- const e = m(t);
663
- return e < this.entity_high_water && this.entity_generations[e] === Y(t);
733
+ const e = y(t);
734
+ return e < this.entity_high_water && this.entity_generations[e] === z(t);
664
735
  }
665
736
  get entity_count() {
666
737
  return this.entity_alive_count;
667
738
  }
739
+ _mark_queries_dirty() {
740
+ const t = this.registered_queries;
741
+ for (let e = 0; e < t.length; e++)
742
+ t[e].query?.mark_non_empty_dirty();
743
+ }
668
744
  // =======================================================
669
745
  // Deferred destruction
670
746
  // =======================================================
@@ -676,73 +752,74 @@ class At {
676
752
  const t = this.pending_destroy;
677
753
  if (t.length === 0) return;
678
754
  const e = this.entity_archetype, s = this.entity_row, n = this.entity_generations, r = this.archetypes, i = this.entity_high_water;
679
- for (let _ = 0; _ < t.length; _++) {
680
- const c = t[_], o = c & F, h = c >> P;
681
- if (o >= i || n[o] !== h) continue;
682
- const a = s[o];
683
- if (a !== v) {
684
- const u = r[e[o]], d = u.has_columns ? u.remove_entity(a) : u.remove_entity_tag(a);
685
- d !== y && (s[d] = a);
755
+ for (let c = 0; c < t.length; c++) {
756
+ const o = t[c], _ = o & G, h = o >> R;
757
+ if (_ >= i || n[_] !== h) continue;
758
+ const l = s[_];
759
+ if (l !== E) {
760
+ const d = r[e[_]], u = d.has_columns ? d.remove_entity(l) : d.remove_entity_tag(l);
761
+ u !== m && (s[u] = l);
686
762
  }
687
- e[o] = v, s[o] = v, n[o] = h + 1 & X, this.entity_free_indices.push(o), this.entity_alive_count--;
763
+ e[_] = E, s[_] = E, n[_] = h + 1 & V, this.entity_free_indices.push(_), this.entity_alive_count--;
688
764
  }
689
- t.length = 0;
765
+ t.length = 0, this._mark_queries_dirty();
690
766
  }
691
767
  get pending_destroy_count() {
692
768
  return this.pending_destroy.length;
693
769
  }
694
770
  add_component_deferred(t, e, s) {
695
- this.pending_add_ids.push(t), this.pending_add_defs.push(e), this.pending_add_values.push(s ?? D);
771
+ this.pending_add_ids.push(t), this.pending_add_defs.push(e), this.pending_add_values.push(s ?? U);
696
772
  }
697
773
  remove_component_deferred(t, e) {
698
774
  this.pending_remove_ids.push(t), this.pending_remove_defs.push(e);
699
775
  }
700
776
  flush_structural() {
701
- this.pending_add_ids.length > 0 && this._flush_adds(), this.pending_remove_ids.length > 0 && this._flush_removes();
777
+ const t = this.pending_add_ids.length > 0, e = this.pending_remove_ids.length > 0;
778
+ t && this._flush_adds(), e && this._flush_removes(), (t || e) && this._mark_queries_dirty();
702
779
  }
703
780
  /** Batch-apply all deferred component additions. */
704
781
  _flush_adds() {
705
- const t = this.pending_add_ids, e = this.pending_add_defs, s = this.pending_add_values, n = t.length, r = this.entity_archetype, i = this.entity_row, _ = this.entity_generations, c = this.archetypes, o = this.component_metas, h = this.entity_high_water;
706
- for (let a = 0; a < n; a++) {
707
- const u = t[a], d = u & F, g = u >> P;
708
- if (d >= h || _[d] !== g) continue;
709
- const T = r[d], p = e[a], w = c[T];
710
- if (w.mask.has(p)) {
711
- o[p].field_names.length > 0 && w.write_fields(i[d], p, s[a]);
782
+ const t = this.pending_add_ids, e = this.pending_add_defs, s = this.pending_add_values, n = t.length, r = this.entity_archetype, i = this.entity_row, c = this.entity_generations, o = this.archetypes, _ = this.component_metas, h = this.entity_high_water, l = this._tick;
783
+ for (let d = 0; d < n; d++) {
784
+ const u = t[d], g = u & G, p = u >> R;
785
+ if (g >= h || c[g] !== p) continue;
786
+ const T = r[g], w = e[d], v = o[T];
787
+ if (v.mask.has(w)) {
788
+ _[w].field_names.length > 0 && v.write_fields(i[g], w, s[d], l);
712
789
  continue;
713
790
  }
714
- const S = this.arch_resolve_add(T, p), E = c[S], O = i[d], L = !E.has_columns && !w.has_columns;
715
- let U;
716
- if (O !== v) {
717
- if (L)
718
- E.move_entity_from_tag(w, O, u);
791
+ const N = this.arch_resolve_add(T, w), x = o[N], P = i[g], Y = !x.has_columns && !v.has_columns;
792
+ let q;
793
+ if (P !== E) {
794
+ if (Y)
795
+ x.move_entity_from_tag(v, P, u);
719
796
  else {
720
- const $ = w.get_edge(p);
721
- E.move_entity_from(w, O, u, $.add_map);
797
+ const Q = v.get_edge(w);
798
+ x.move_entity_from(v, P, u, Q.add_map, l);
722
799
  }
723
- U = f[0], f[1] !== y && (i[f[1]] = O);
800
+ q = f[0], f[1] !== m && (i[f[1]] = P);
724
801
  } else
725
- U = L ? E.add_entity_tag(u) : E.add_entity(u);
726
- o[p].field_names.length > 0 && E.write_fields(U, p, s[a]), r[d] = S, i[d] = U;
802
+ q = Y ? x.add_entity_tag(u) : x.add_entity(u);
803
+ _[w].field_names.length > 0 && x.write_fields(q, w, s[d], l), r[g] = N, i[g] = q;
727
804
  }
728
805
  t.length = 0, e.length = 0, s.length = 0;
729
806
  }
730
807
  /** Batch-apply all deferred component removals. */
731
808
  _flush_removes() {
732
- const t = this.pending_remove_ids, e = this.pending_remove_defs, s = t.length, n = this.entity_archetype, r = this.entity_row, i = this.entity_generations, _ = this.archetypes, c = this.entity_high_water;
733
- for (let o = 0; o < s; o++) {
734
- const h = t[o], a = h & F, u = h >> P;
735
- if (a >= c || i[a] !== u) continue;
736
- const d = n[a], g = e[o], T = _[d];
737
- if (!T.mask.has(g)) continue;
738
- const p = this.arch_resolve_remove(d, g), w = _[p], S = r[a];
739
- if (!w.has_columns && !T.has_columns)
740
- w.move_entity_from_tag(T, S, h);
809
+ const t = this.pending_remove_ids, e = this.pending_remove_defs, s = t.length, n = this.entity_archetype, r = this.entity_row, i = this.entity_generations, c = this.archetypes, o = this.entity_high_water, _ = this._tick;
810
+ for (let h = 0; h < s; h++) {
811
+ const l = t[h], d = l & G, u = l >> R;
812
+ if (d >= o || i[d] !== u) continue;
813
+ const g = n[d], p = e[h], T = c[g];
814
+ if (!T.mask.has(p)) continue;
815
+ const w = this.arch_resolve_remove(g, p), v = c[w], N = r[d];
816
+ if (!v.has_columns && !T.has_columns)
817
+ v.move_entity_from_tag(T, N, l);
741
818
  else {
742
- const O = T.get_edge(g);
743
- w.move_entity_from(T, S, h, O.remove_map);
819
+ const P = T.get_edge(p);
820
+ v.move_entity_from(T, N, l, P.remove_map, _);
744
821
  }
745
- f[1] !== y && (r[f[1]] = S), n[a] = p, r[a] = f[0];
822
+ f[1] !== m && (r[f[1]] = N), n[d] = w, r[d] = f[0];
746
823
  }
747
824
  t.length = 0, e.length = 0;
748
825
  }
@@ -753,7 +830,7 @@ class At {
753
830
  // Component registration
754
831
  // =======================================================
755
832
  register_component(t) {
756
- const e = yt(this.component_count++), s = Object.keys(t), n = new Array(s.length), r = /* @__PURE__ */ Object.create(null);
833
+ const e = vt(this.component_count++), s = Object.keys(t), n = new Array(s.length), r = /* @__PURE__ */ Object.create(null);
757
834
  for (let i = 0; i < s.length; i++)
758
835
  r[s[i]] = i, n[i] = t[s[i]];
759
836
  return this.component_metas.push({ field_names: s, field_index: r, field_types: n }), e;
@@ -761,83 +838,78 @@ class At {
761
838
  add_component(t, e, s) {
762
839
  if (!this.is_alive(t))
763
840
  return;
764
- const n = m(t), r = this.entity_archetype[n], i = this.arch_get(r);
841
+ const n = y(t), r = this.entity_archetype[n], i = this.arch_get(r);
765
842
  if (i.has_component(e)) {
766
843
  i.write_fields(
767
844
  this.entity_row[n],
768
845
  e,
769
- s
846
+ s,
847
+ this._tick
770
848
  );
771
849
  return;
772
850
  }
773
- const _ = this.arch_resolve_add(
774
- r,
775
- e
776
- ), c = this.arch_get(_), o = this.entity_row[n];
851
+ const c = this.arch_resolve_add(r, e), o = this.arch_get(c), _ = this.entity_row[n];
777
852
  let h;
778
- if (o !== v) {
779
- const a = i.get_edge(e);
780
- !c.has_columns && !i.has_columns ? c.move_entity_from_tag(i, o, t) : c.move_entity_from(i, o, t, a.add_map), h = f[0], f[1] !== y && (this.entity_row[f[1]] = o);
853
+ if (_ !== E) {
854
+ const l = i.get_edge(e);
855
+ !o.has_columns && !i.has_columns ? o.move_entity_from_tag(i, _, t) : o.move_entity_from(i, _, t, l.add_map, this._tick), h = f[0], f[1] !== m && (this.entity_row[f[1]] = _);
781
856
  } else
782
- h = c.has_columns ? c.add_entity(t) : c.add_entity_tag(t);
783
- c.write_fields(
857
+ h = o.has_columns ? o.add_entity(t) : o.add_entity_tag(t);
858
+ o.write_fields(
784
859
  h,
785
860
  e,
786
- s
787
- ), this.entity_archetype[n] = _, this.entity_row[n] = h;
861
+ s,
862
+ this._tick
863
+ ), this.entity_archetype[n] = c, this.entity_row[n] = h, this._mark_queries_dirty();
788
864
  }
789
865
  /** Add multiple components in one transition (resolves final archetype, then moves once). */
790
866
  add_components(t, e) {
791
867
  if (!this.is_alive(t))
792
868
  return;
793
- const s = m(t), n = this.entity_archetype[s];
869
+ const s = y(t), n = this.entity_archetype[s];
794
870
  let r = n;
795
871
  for (let i = 0; i < e.length; i++)
796
- r = this.arch_resolve_add(
797
- r,
798
- e[i].def
799
- );
872
+ r = this.arch_resolve_add(r, e[i].def);
800
873
  if (r !== n) {
801
- const i = this.arch_get(n), _ = this.arch_get(r), c = this.entity_row[s];
802
- let o;
803
- if (c !== v) {
804
- const h = M(i, _);
805
- _.move_entity_from(i, c, t, h), o = f[0], f[1] !== y && (this.entity_row[f[1]] = c);
874
+ const i = this.arch_get(n), c = this.arch_get(r), o = this.entity_row[s];
875
+ let _;
876
+ if (o !== E) {
877
+ const h = M(i, c);
878
+ c.move_entity_from(i, o, t, h, this._tick), _ = f[0], f[1] !== m && (this.entity_row[f[1]] = o);
806
879
  } else
807
- o = _.add_entity(t);
880
+ _ = c.add_entity(t);
808
881
  for (let h = 0; h < e.length; h++)
809
- _.write_fields(
810
- o,
882
+ c.write_fields(
883
+ _,
811
884
  e[h].def,
812
- e[h].values ?? D
885
+ e[h].values ?? U,
886
+ this._tick
813
887
  );
814
- this.entity_archetype[s] = r, this.entity_row[s] = o;
888
+ this.entity_archetype[s] = r, this.entity_row[s] = _, this._mark_queries_dirty();
815
889
  } else {
816
- const i = this.arch_get(n), _ = this.entity_row[s];
817
- for (let c = 0; c < e.length; c++)
890
+ const i = this.arch_get(n), c = this.entity_row[s];
891
+ for (let o = 0; o < e.length; o++)
818
892
  i.write_fields(
819
- _,
820
- e[c].def,
821
- e[c].values ?? D
893
+ c,
894
+ e[o].def,
895
+ e[o].values ?? U,
896
+ this._tick
822
897
  );
823
898
  }
824
899
  }
825
900
  remove_component(t, e) {
826
901
  if (!this.is_alive(t))
827
902
  return;
828
- const s = m(t), n = this.entity_archetype[s], r = this.arch_get(n);
903
+ const s = y(t), n = this.entity_archetype[s], r = this.arch_get(n);
829
904
  if (!r.has_component(e)) return;
830
- const i = this.arch_resolve_remove(
831
- n,
832
- e
833
- ), _ = this.arch_get(i), c = this.entity_row[s], o = r.get_edge(e);
834
- !_.has_columns && !r.has_columns ? _.move_entity_from_tag(r, c, t) : _.move_entity_from(r, c, t, o.remove_map), f[1] !== y && (this.entity_row[f[1]] = c), this.entity_archetype[s] = i, this.entity_row[s] = f[0];
905
+ const i = this.arch_resolve_remove(n, e), c = this.arch_get(i), o = this.entity_row[s], _ = r.get_edge(e);
906
+ !c.has_columns && !r.has_columns ? c.move_entity_from_tag(r, o, t) : c.move_entity_from(r, o, t, _.remove_map, this._tick), f[1] !== m && (this.entity_row[f[1]] = o), this.entity_archetype[s] = i, this.entity_row[s] = f[0], this._mark_queries_dirty();
835
907
  }
836
908
  /** Remove multiple components in one transition (resolves final archetype, then moves once). */
837
909
  remove_components(t, e) {
838
910
  if (!this.is_alive(t))
839
911
  return;
840
- const s = m(t), n = this.entity_archetype[s];
912
+ const s = y(t), n = this.entity_archetype[s];
841
913
  let r = n;
842
914
  for (let h = 0; h < e.length; h++)
843
915
  r = this.arch_resolve_remove(
@@ -845,16 +917,14 @@ class At {
845
917
  e[h]
846
918
  );
847
919
  if (r === n) return;
848
- const i = this.arch_get(n), _ = this.arch_get(r), c = this.entity_row[s], o = M(i, _);
849
- _.move_entity_from(i, c, t, o), f[1] !== y && (this.entity_row[f[1]] = c), this.entity_archetype[s] = r, this.entity_row[s] = f[0];
920
+ const i = this.arch_get(n), c = this.arch_get(r), o = this.entity_row[s], _ = M(i, c);
921
+ c.move_entity_from(i, o, t, _, this._tick), f[1] !== m && (this.entity_row[f[1]] = o), this.entity_archetype[s] = r, this.entity_row[s] = f[0], this._mark_queries_dirty();
850
922
  }
851
923
  has_component(t, e) {
852
924
  if (!this.is_alive(t))
853
925
  return !1;
854
- const s = m(t);
855
- return this.arch_get(
856
- this.entity_archetype[s]
857
- ).has_component(e);
926
+ const s = y(t);
927
+ return this.arch_get(this.entity_archetype[s]).has_component(e);
858
928
  }
859
929
  /**
860
930
  * Bulk add a component to ALL entities in the given archetype.
@@ -865,14 +935,15 @@ class At {
865
935
  if (t.length === 0) return;
866
936
  const n = e;
867
937
  if (t.mask.has(n)) return;
868
- const r = this.arch_resolve_add(t.id, n), i = this.arch_get(r), _ = t.get_edge(n), c = t.length, o = this.entity_archetype, h = this.entity_row, a = i.bulk_move_all_from(t, _.add_map);
869
- for (let d = 0; d < c; d++) {
870
- const g = m(i.entity_ids[a + d]);
871
- o[g] = r, h[g] = a + d;
938
+ const r = this.arch_resolve_add(t.id, n), i = this.arch_get(r), c = t.get_edge(n), o = t.length, _ = this.entity_archetype, h = this.entity_row, l = i.bulk_move_all_from(t, c.add_map, this._tick);
939
+ for (let u = 0; u < o; u++) {
940
+ const g = y(i.entity_ids[l + u]);
941
+ _[g] = r, h[g] = l + u;
872
942
  }
873
943
  if (this.component_metas[n].field_names.length > 0 && s)
874
- for (let d = 0; d < c; d++)
875
- i.write_fields(a + d, n, s);
944
+ for (let u = 0; u < o; u++)
945
+ i.write_fields(l + u, n, s, this._tick);
946
+ this._mark_queries_dirty();
876
947
  }
877
948
  /**
878
949
  * Bulk remove a component from ALL entities in the given archetype.
@@ -883,22 +954,21 @@ class At {
883
954
  if (t.length === 0) return;
884
955
  const s = e;
885
956
  if (!t.mask.has(s)) return;
886
- const n = this.arch_resolve_remove(t.id, s), r = this.arch_get(n), i = t.get_edge(s), _ = t.length, c = r.bulk_move_all_from(t, i.remove_map), o = this.entity_archetype, h = this.entity_row;
887
- for (let a = 0; a < _; a++) {
888
- const u = m(r.entity_ids[c + a]);
889
- o[u] = n, h[u] = c + a;
957
+ const n = this.arch_resolve_remove(t.id, s), r = this.arch_get(n), i = t.get_edge(s), c = t.length, o = r.bulk_move_all_from(t, i.remove_map, this._tick), _ = this.entity_archetype, h = this.entity_row;
958
+ for (let l = 0; l < c; l++) {
959
+ const d = y(r.entity_ids[o + l]);
960
+ _[d] = n, h[d] = o + l;
890
961
  }
962
+ this._mark_queries_dirty();
891
963
  }
892
964
  // =======================================================
893
965
  // Direct data access (used by SystemContext)
894
966
  // =======================================================
895
967
  get_entity_archetype(t) {
896
- return this.arch_get(
897
- this.entity_archetype[m(t)]
898
- );
968
+ return this.arch_get(this.entity_archetype[y(t)]);
899
969
  }
900
970
  get_entity_row(t) {
901
- return this.entity_row[m(t)];
971
+ return this.entity_row[y(t)];
902
972
  }
903
973
  // =======================================================
904
974
  // Query support
@@ -911,57 +981,62 @@ class At {
911
981
  get_matching_archetypes(t, e, s) {
912
982
  const n = t._words;
913
983
  let r = !1;
914
- for (let o = 0; o < n.length; o++)
915
- if (n[o] !== 0) {
984
+ for (let _ = 0; _ < n.length; _++)
985
+ if (n[_] !== 0) {
916
986
  r = !0;
917
987
  break;
918
988
  }
919
989
  if (!r)
920
990
  return this.archetypes.filter(
921
- (o) => (!e || !o.mask.overlaps(e)) && (!s || o.mask.overlaps(s))
991
+ (_) => (!e || !_.mask.overlaps(e)) && (!s || _.mask.overlaps(s))
922
992
  );
923
- let i, _ = !1;
924
- for (let o = 0; o < n.length; o++) {
925
- let h = n[o];
993
+ let i, c = !1;
994
+ for (let _ = 0; _ < n.length; _++) {
995
+ let h = n[_];
926
996
  if (h === 0) continue;
927
- const a = o << k;
997
+ const l = _ << I;
928
998
  for (; h !== 0; ) {
929
- const u = h & -h >>> 0, d = a + (I - Math.clz32(u));
930
- h ^= u;
931
- const g = this.component_index.get(d);
999
+ const d = h & -h >>> 0, u = l + (S - Math.clz32(d));
1000
+ h ^= d;
1001
+ const g = this.component_index.get(u);
932
1002
  if (!g || g.size === 0) {
933
- _ = !0;
1003
+ c = !0;
934
1004
  break;
935
1005
  }
936
1006
  (!i || g.size < i.size) && (i = g);
937
1007
  }
938
- if (_) break;
1008
+ if (c) break;
939
1009
  }
940
- if (_ || !i) return [];
941
- const c = [];
942
- for (const o of i) {
943
- const h = this.arch_get(o);
944
- h.matches(t) && (!e || !h.mask.overlaps(e)) && (!s || h.mask.overlaps(s)) && c.push(h);
1010
+ if (c || !i) return [];
1011
+ const o = [];
1012
+ for (const _ of i) {
1013
+ const h = this.arch_get(_);
1014
+ h.matches(t) && (!e || !h.mask.overlaps(e)) && (!s || h.mask.overlaps(s)) && o.push(h);
945
1015
  }
946
- return c;
1016
+ return o;
947
1017
  }
948
1018
  /**
949
1019
  * Register a live query. Returns a mutable Archetype[] that this Store will
950
1020
  * push newly-created matching archetypes into, keeping the query always up-to-date.
951
1021
  */
952
1022
  register_query(t, e, s) {
953
- const n = this.get_matching_archetypes(
954
- t,
955
- e,
956
- s
957
- );
1023
+ const n = this.get_matching_archetypes(t, e, s);
958
1024
  return this.registered_queries.push({
959
1025
  include_mask: t.copy(),
960
1026
  exclude_mask: e ? e.copy() : null,
961
1027
  any_of_mask: s ? s.copy() : null,
962
- result: n
1028
+ result: n,
1029
+ query: null
963
1030
  }), n;
964
1031
  }
1032
+ update_query_ref(t, e) {
1033
+ const s = this.registered_queries;
1034
+ for (let n = 0; n < s.length; n++)
1035
+ if (s[n].result === t) {
1036
+ s[n].query = e;
1037
+ return;
1038
+ }
1039
+ }
965
1040
  get archetype_count() {
966
1041
  return this.archetypes.length;
967
1042
  }
@@ -969,7 +1044,7 @@ class At {
969
1044
  // Event channels
970
1045
  // =======================================================
971
1046
  register_event(t) {
972
- const e = pt(this.event_count++), s = new wt(t);
1047
+ const e = Et(this.event_count++), s = new kt(t);
973
1048
  return this.event_channels.push(s), e;
974
1049
  }
975
1050
  emit_event(t, e) {
@@ -987,42 +1062,73 @@ class At {
987
1062
  t[e].clear();
988
1063
  }
989
1064
  // =======================================================
990
- // Resource channels
1065
+ // Event key storage
1066
+ // =======================================================
1067
+ // any: type-erased — EventDef<F> phantom is lost in the map, recovered by callers via EventKey<F>
1068
+ event_key_map = /* @__PURE__ */ new Map();
1069
+ register_event_by_key(t, e) {
1070
+ if (this.event_key_map.has(t))
1071
+ throw new O(D.EVENT_ALREADY_REGISTERED, "Event key already registered");
1072
+ const s = this.register_event(e);
1073
+ return this.event_key_map.set(t, s), s;
1074
+ }
1075
+ // any: type-erased — caller recovers F from EventKey<F>
1076
+ get_event_def_by_key(t) {
1077
+ const e = this.event_key_map.get(t);
1078
+ if (e === void 0)
1079
+ throw new O(D.EVENT_NOT_REGISTERED, "Event key not registered");
1080
+ return e;
1081
+ }
1082
+ has_event_key(t) {
1083
+ return this.event_key_map.has(t);
1084
+ }
1085
+ // =======================================================
1086
+ // Resource storage
991
1087
  // =======================================================
1088
+ resource_key_map = /* @__PURE__ */ new Map();
992
1089
  register_resource(t, e) {
993
- const s = vt(this.resource_count++), n = new xt(t, e);
994
- return this.resource_channels.push(n), s;
1090
+ if (this.resource_key_map.has(t))
1091
+ throw new O(D.RESOURCE_ALREADY_REGISTERED, "Resource key already registered");
1092
+ this.resource_key_map.set(t, e);
995
1093
  }
996
- get_resource_reader(t) {
997
- return this.resource_channels[t].reader;
1094
+ get_resource(t) {
1095
+ if (!this.resource_key_map.has(t))
1096
+ throw new O(D.RESOURCE_NOT_REGISTERED, "Resource key not registered");
1097
+ return this.resource_key_map.get(t);
998
1098
  }
999
- get_resource_channel(t) {
1000
- return this.resource_channels[t];
1099
+ set_resource(t, e) {
1100
+ if (!this.resource_key_map.has(t))
1101
+ throw new O(D.RESOURCE_NOT_REGISTERED, "Resource key not registered");
1102
+ this.resource_key_map.set(t, e);
1103
+ }
1104
+ has_resource(t) {
1105
+ return this.resource_key_map.has(t);
1001
1106
  }
1002
1107
  }
1003
- var Et = /* @__PURE__ */ ((l) => (l.PRE_STARTUP = "PRE_STARTUP", l.STARTUP = "STARTUP", l.POST_STARTUP = "POST_STARTUP", l.FIXED_UPDATE = "FIXED_UPDATE", l.PRE_UPDATE = "PRE_UPDATE", l.UPDATE = "UPDATE", l.POST_UPDATE = "POST_UPDATE", l))(Et || {});
1004
- const q = [
1108
+ var xt = /* @__PURE__ */ ((a) => (a.PRE_STARTUP = "PRE_STARTUP", a.STARTUP = "STARTUP", a.POST_STARTUP = "POST_STARTUP", a.FIXED_UPDATE = "FIXED_UPDATE", a.PRE_UPDATE = "PRE_UPDATE", a.UPDATE = "UPDATE", a.POST_UPDATE = "POST_UPDATE", a))(xt || {});
1109
+ const L = [
1005
1110
  "PRE_STARTUP",
1006
1111
  "STARTUP",
1007
1112
  "POST_STARTUP"
1008
1113
  /* POST_STARTUP */
1009
- ], G = [
1114
+ ], C = [
1010
1115
  "PRE_UPDATE",
1011
1116
  "UPDATE",
1012
1117
  "POST_UPDATE"
1013
1118
  /* POST_UPDATE */
1014
1119
  ];
1015
- class kt {
1120
+ class It {
1016
1121
  label_systems = /* @__PURE__ */ new Map();
1017
1122
  sorted_cache = /* @__PURE__ */ new Map();
1018
1123
  system_index = /* @__PURE__ */ new Map();
1124
+ system_last_run = /* @__PURE__ */ new Map();
1019
1125
  next_insertion_order = 0;
1020
1126
  constructor() {
1021
- for (let t = 0; t < q.length; t++)
1022
- this.label_systems.set(q[t], []);
1127
+ for (let t = 0; t < L.length; t++)
1128
+ this.label_systems.set(L[t], []);
1023
1129
  this.label_systems.set("FIXED_UPDATE", []);
1024
- for (let t = 0; t < G.length; t++)
1025
- this.label_systems.set(G[t], []);
1130
+ for (let t = 0; t < C.length; t++)
1131
+ this.label_systems.set(C[t], []);
1026
1132
  }
1027
1133
  add_systems(t, ...e) {
1028
1134
  for (const s of e) {
@@ -1032,7 +1138,7 @@ class kt {
1032
1138
  before: new Set(r?.before ?? []),
1033
1139
  after: new Set(r?.after ?? [])
1034
1140
  };
1035
- this.label_systems.get(t).push(i), this.system_index.set(n, t), this.sorted_cache.delete(t);
1141
+ this.label_systems.get(t).push(i), this.system_index.set(n, t), this.system_last_run.set(n, 0), this.sorted_cache.delete(t);
1036
1142
  }
1037
1143
  }
1038
1144
  remove_system(t) {
@@ -1045,18 +1151,18 @@ class kt {
1045
1151
  for (const i of s)
1046
1152
  i.before.delete(t), i.after.delete(t);
1047
1153
  }
1048
- this.system_index.delete(t), this.sorted_cache.delete(e);
1154
+ this.system_index.delete(t), this.system_last_run.delete(t), this.sorted_cache.delete(e);
1049
1155
  }
1050
- run_startup(t) {
1051
- for (const e of q)
1052
- this.run_label(e, t, it);
1156
+ run_startup(t, e) {
1157
+ for (const s of L)
1158
+ this.run_label(s, t, mt, e);
1053
1159
  }
1054
- run_update(t, e) {
1055
- for (const s of G)
1056
- this.run_label(s, t, e);
1160
+ run_update(t, e, s) {
1161
+ for (const n of C)
1162
+ this.run_label(n, t, e, s);
1057
1163
  }
1058
- run_fixed_update(t, e) {
1059
- this.run_label("FIXED_UPDATE", t, e);
1164
+ run_fixed_update(t, e, s) {
1165
+ this.run_label("FIXED_UPDATE", t, e, s);
1060
1166
  }
1061
1167
  has_fixed_systems() {
1062
1168
  return this.label_systems.get(
@@ -1077,91 +1183,84 @@ class kt {
1077
1183
  clear() {
1078
1184
  for (const t of this.label_systems.values())
1079
1185
  t.length = 0;
1080
- this.sorted_cache.clear(), this.system_index.clear();
1186
+ this.sorted_cache.clear(), this.system_index.clear(), this.system_last_run.clear();
1081
1187
  }
1082
- run_label(t, e, s) {
1083
- const n = this.get_sorted(t);
1084
- for (let r = 0; r < n.length; r++)
1085
- n[r].fn(e, s);
1188
+ run_label(t, e, s, n) {
1189
+ const r = this.get_sorted(t);
1190
+ for (let i = 0; i < r.length; i++)
1191
+ this.system_last_run.set(r[i], n), e.last_run_tick = n, r[i].fn(e, s);
1086
1192
  e.flush();
1087
1193
  }
1088
1194
  get_sorted(t) {
1089
1195
  const e = this.sorted_cache.get(t);
1090
1196
  if (e !== void 0) return e;
1091
- const s = this.label_systems.get(t), n = this.topological_sort(s, t);
1197
+ const s = this.label_systems.get(t), n = this.sort_systems(s, t);
1092
1198
  return this.sorted_cache.set(t, n), n;
1093
1199
  }
1094
1200
  /**
1095
- * Kahn's algorithm: BFS-based topological sort.
1096
- * Uses insertion_order as a stable tiebreaker (lower insertion order runs first).
1201
+ * Delegates to the shared topological_sort utility.
1202
+ * Builds the dependency edge map from before/after constraints, then
1203
+ * catches any cycle TypeError and re-throws as ECSError.
1097
1204
  */
1098
- topological_sort(t, e) {
1205
+ sort_systems(t, e) {
1099
1206
  if (t.length === 0) return [];
1100
- const s = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map(), i = /* @__PURE__ */ new Set();
1101
- for (const o of t)
1102
- s.set(o.descriptor, /* @__PURE__ */ new Set()), n.set(o.descriptor, 0), r.set(o.descriptor, o.insertion_order), i.add(o.descriptor);
1103
- for (const o of t) {
1104
- for (const h of o.before)
1105
- i.has(h) && (s.get(o.descriptor).add(h), n.set(h, n.get(h) + 1));
1106
- for (const h of o.after)
1107
- i.has(h) && (s.get(h).add(o.descriptor), n.set(o.descriptor, n.get(o.descriptor) + 1));
1207
+ const s = [], n = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Set();
1208
+ for (const _ of t)
1209
+ s.push(_.descriptor), n.set(_.descriptor, _.insertion_order), r.add(_.descriptor);
1210
+ const i = /* @__PURE__ */ new Map();
1211
+ for (const _ of t)
1212
+ i.set(_.descriptor, []);
1213
+ for (const _ of t) {
1214
+ for (const h of _.before)
1215
+ r.has(h) && i.get(_.descriptor).push(h);
1216
+ for (const h of _.after)
1217
+ r.has(h) && i.get(h).push(_.descriptor);
1108
1218
  }
1109
- let _ = [];
1110
- for (const o of t)
1111
- n.get(o.descriptor) === 0 && _.push(o.descriptor);
1112
- _.sort((o, h) => r.get(h) - r.get(o));
1113
- const c = [];
1114
- for (; _.length > 0; ) {
1115
- const o = _.pop();
1116
- c.push(o);
1117
- for (const h of s.get(o)) {
1118
- const a = n.get(h) - 1;
1119
- n.set(h, a), a === 0 && _.push(h);
1120
- }
1121
- _.sort((h, a) => r.get(a) - r.get(h));
1122
- }
1123
- if (c.length !== t.length) {
1124
- const o = new Set(c), h = t.filter((a) => !o.has(a.descriptor)).map((a) => a.descriptor.name ?? `system_${a.descriptor.id}`);
1125
- throw new Q(
1126
- z.CIRCULAR_SYSTEM_DEPENDENCY,
1127
- `Circular system dependency detected in ${e}: [${h.join(", ")}]`
1128
- );
1219
+ const c = (_, h) => n.get(_) - n.get(h), o = (_) => _.name ?? `system_${_.id}`;
1220
+ try {
1221
+ return lt(s, i, c, o);
1222
+ } catch (_) {
1223
+ throw _ instanceof TypeError ? new O(
1224
+ D.CIRCULAR_SYSTEM_DEPENDENCY,
1225
+ `Circular system dependency detected in ${e}: ${_.message}`
1226
+ ) : _;
1129
1227
  }
1130
- return c;
1131
1228
  }
1132
1229
  }
1133
1230
  const W = /* @__PURE__ */ new WeakMap();
1134
- function It(l, t) {
1135
- let e = W.get(l);
1231
+ function B(a, t) {
1232
+ let e = W.get(a);
1136
1233
  if (!e) {
1137
1234
  e = /* @__PURE__ */ Object.create(null);
1138
- const { field_names: r } = l.layout;
1235
+ const { field_names: r } = a.layout;
1139
1236
  for (let i = 0; i < r.length; i++) {
1140
- const _ = i;
1237
+ const c = i;
1141
1238
  Object.defineProperty(e, r[i], {
1142
1239
  get() {
1143
- return this._columns[_][this._row];
1240
+ return this._columns[c][this._row];
1144
1241
  },
1145
- set(c) {
1146
- this._columns[_][this._row] = c;
1242
+ set(o) {
1243
+ this._columns[c][this._row] = o;
1147
1244
  },
1148
1245
  enumerable: !0,
1149
1246
  configurable: !1
1150
1247
  });
1151
1248
  }
1152
- W.set(l, e);
1249
+ W.set(a, e);
1153
1250
  }
1154
- const s = Object.create(e), n = new Array(l.columns.length);
1155
- for (let r = 0; r < l.columns.length; r++) n[r] = l.columns[r].buf;
1251
+ const s = Object.create(e), n = new Array(a.columns.length);
1252
+ for (let r = 0; r < a.columns.length; r++) n[r] = a.columns[r].buf;
1156
1253
  return s._columns = n, s._row = t, s;
1157
1254
  }
1158
- class Pt {
1255
+ class St {
1159
1256
  _archetypes;
1160
1257
  _defs;
1161
1258
  _resolver;
1162
1259
  _include;
1163
1260
  _exclude;
1164
1261
  _any_of;
1262
+ _non_empty_archetypes = [];
1263
+ _non_empty_dirty = !0;
1165
1264
  constructor(t, e, s, n, r, i) {
1166
1265
  this._archetypes = t, this._defs = e, this._resolver = s, this._include = n, this._exclude = r, this._any_of = i;
1167
1266
  }
@@ -1179,27 +1278,16 @@ class Pt {
1179
1278
  get archetypes() {
1180
1279
  return this._archetypes;
1181
1280
  }
1182
- /** Iterate non-empty archetypes. Skips archetypes with zero entities. */
1183
- *[Symbol.iterator]() {
1184
- const t = this._archetypes;
1185
- for (let e = 0; e < t.length; e++)
1186
- t[e].entity_count > 0 && (yield t[e]);
1187
- }
1188
1281
  /** Extend required component set. Returns a new (cached) Query. */
1189
1282
  and(...t) {
1190
1283
  const e = this._include.copy(), s = this._defs.slice();
1191
1284
  for (let n = 0; n < t.length; n++)
1192
1285
  e.has(t[n]) || (e.set(t[n]), s.push(t[n]));
1193
- return this._resolver._resolve_query(
1194
- e,
1195
- this._exclude,
1196
- this._any_of,
1197
- s
1198
- );
1286
+ return this._resolver._resolve_query(e, this._exclude, this._any_of, s);
1199
1287
  }
1200
1288
  /** Exclude archetypes that have any of these components. */
1201
1289
  not(...t) {
1202
- const e = this._exclude ? this._exclude.copy() : new x();
1290
+ const e = this._exclude ? this._exclude.copy() : new k();
1203
1291
  for (let s = 0; s < t.length; s++) e.set(t[s]);
1204
1292
  return this._resolver._resolve_query(
1205
1293
  this._include,
@@ -1208,9 +1296,29 @@ class Pt {
1208
1296
  this._defs
1209
1297
  );
1210
1298
  }
1299
+ for_each(t) {
1300
+ const e = this._non_empty();
1301
+ for (let s = 0; s < e.length; s++)
1302
+ t(e[s]);
1303
+ }
1304
+ /** @internal — called by Store after flush and archetype push. */
1305
+ mark_non_empty_dirty() {
1306
+ this._non_empty_dirty = !0;
1307
+ }
1308
+ /** @internal — used by ChangedQuery. Rebuild non-empty archetype list if dirty, return cached result. */
1309
+ _non_empty() {
1310
+ if (this._non_empty_dirty) {
1311
+ const t = this._archetypes, e = this._non_empty_archetypes;
1312
+ e.length = 0;
1313
+ for (let s = 0; s < t.length; s++)
1314
+ t[s].entity_count > 0 && e.push(t[s]);
1315
+ this._non_empty_dirty = !1;
1316
+ }
1317
+ return this._non_empty_archetypes;
1318
+ }
1211
1319
  /** Require at least one of these components. */
1212
1320
  any_of(...t) {
1213
- const e = this._any_of ? this._any_of.copy() : new x();
1321
+ const e = this._any_of ? this._any_of.copy() : new k();
1214
1322
  for (let s = 0; s < t.length; s++) e.set(t[s]);
1215
1323
  return this._resolver._resolve_query(
1216
1324
  this._include,
@@ -1219,19 +1327,34 @@ class Pt {
1219
1327
  this._defs
1220
1328
  );
1221
1329
  }
1330
+ /** Create a ChangedQuery that filters archetypes by change tick. */
1331
+ changed(...t) {
1332
+ const e = new Array(t.length);
1333
+ for (let s = 0; s < t.length; s++) e[s] = t[s];
1334
+ return new Nt(this, e);
1335
+ }
1336
+ /** @internal — reads last_run_tick from the resolver (ECS). */
1337
+ _ctx_last_run_tick() {
1338
+ return this._resolver._get_last_run_tick();
1339
+ }
1222
1340
  }
1223
- class St {
1341
+ class Dt {
1224
1342
  constructor(t) {
1225
1343
  this._resolver = t;
1226
1344
  }
1227
1345
  every(...t) {
1228
- const e = new x();
1346
+ const e = new k();
1229
1347
  for (let s = 0; s < t.length; s++) e.set(t[s]);
1230
1348
  return this._resolver._resolve_query(e, null, null, t);
1231
1349
  }
1232
1350
  }
1233
- class Ot {
1351
+ class Rt {
1234
1352
  store;
1353
+ last_run_tick = 0;
1354
+ /** Current world tick. Use this for write ticks in get_column_mut. */
1355
+ get world_tick() {
1356
+ return this.store._tick;
1357
+ }
1235
1358
  constructor(t) {
1236
1359
  this.store = t;
1237
1360
  }
@@ -1243,20 +1366,25 @@ class Ot {
1243
1366
  return n.read_field(r, e, s);
1244
1367
  }
1245
1368
  set_field(t, e, s, n) {
1246
- const r = this.store.get_entity_archetype(t), i = this.store.get_entity_row(t), _ = r.get_column(e, s);
1247
- _[i] = n;
1369
+ const r = this.store.get_entity_archetype(t), i = this.store.get_entity_row(t), c = r.get_column_mut(e, s, this.store._tick);
1370
+ c[i] = n;
1248
1371
  }
1249
- /** Create a cached component reference for a single entity. See ref.ts. */
1372
+ /** Create a cached read-only component reference for a single entity. See ref.ts. */
1250
1373
  ref(t, e) {
1251
1374
  const s = this.store.get_entity_archetype(e), n = this.store.get_entity_row(e);
1252
- return It(s.column_groups[t], n);
1375
+ return B(s.column_groups[t], n);
1376
+ }
1377
+ /** Create a cached mutable component reference. Marks the component as changed. */
1378
+ ref_mut(t, e) {
1379
+ const s = this.store.get_entity_archetype(e), n = this.store.get_entity_row(e);
1380
+ return s._changed_tick[t] = this.store._tick, B(s.column_groups[t], n);
1253
1381
  }
1254
1382
  /** Buffer an entity for deferred destruction (applied at phase flush). */
1255
1383
  destroy_entity(t) {
1256
1384
  return this.store.destroy_entity_deferred(t), this;
1257
1385
  }
1258
1386
  add_component(t, e, s) {
1259
- return this.store.add_component_deferred(t, e, s ?? D), this;
1387
+ return this.store.add_component_deferred(t, e, s ?? U), this;
1260
1388
  }
1261
1389
  remove_component(t, e) {
1262
1390
  return this.store.remove_component_deferred(t, e), this;
@@ -1266,30 +1394,55 @@ class Ot {
1266
1394
  this.store.flush_structural(), this.store.flush_destroyed();
1267
1395
  }
1268
1396
  emit(t, e) {
1269
- e === void 0 ? this.store.emit_signal(t) : this.store.emit_event(t, e);
1397
+ const s = this.store.get_event_def_by_key(t);
1398
+ e === void 0 ? this.store.emit_signal(s) : this.store.emit_event(s, e);
1270
1399
  }
1271
1400
  read(t) {
1272
- return this.store.get_event_reader(t);
1401
+ const e = this.store.get_event_def_by_key(t);
1402
+ return this.store.get_event_reader(e);
1273
1403
  }
1274
1404
  // =======================================================
1275
1405
  // Resources
1276
1406
  // =======================================================
1277
1407
  resource(t) {
1278
- return this.store.get_resource_reader(t);
1408
+ return this.store.get_resource(t);
1279
1409
  }
1280
1410
  set_resource(t, e) {
1281
- this.store.get_resource_channel(t).write(e);
1411
+ this.store.set_resource(t, e);
1412
+ }
1413
+ has_resource(t) {
1414
+ return this.store.has_resource(t);
1282
1415
  }
1283
1416
  }
1284
- const Dt = (l) => N(
1285
- l
1286
- );
1287
1417
  class Nt {
1418
+ _query;
1419
+ _changed_ids;
1420
+ constructor(t, e) {
1421
+ this._query = t, this._changed_ids = e;
1422
+ }
1423
+ for_each(t) {
1424
+ const e = this._query._ctx_last_run_tick(), s = this._query._non_empty(), n = this._changed_ids;
1425
+ for (let r = 0; r < s.length; r++) {
1426
+ const i = s[r];
1427
+ for (let c = 0; c < n.length; c++)
1428
+ if (i._changed_tick[n[c]] >= e) {
1429
+ t(i);
1430
+ break;
1431
+ }
1432
+ }
1433
+ }
1434
+ }
1435
+ const Pt = (a) => F(
1436
+ a
1437
+ );
1438
+ class qt {
1288
1439
  store;
1289
1440
  schedule;
1290
1441
  ctx;
1291
1442
  systems = /* @__PURE__ */ new Set();
1292
1443
  next_system_id = 0;
1444
+ // Tick counter for change detection
1445
+ _tick = 0;
1293
1446
  // Fixed timestep accumulator
1294
1447
  _fixed_timestep;
1295
1448
  _accumulator = 0;
@@ -1298,9 +1451,9 @@ class Nt {
1298
1451
  // Multiple queries can share the same hash (collision), so each bucket is an array.
1299
1452
  query_cache = /* @__PURE__ */ new Map();
1300
1453
  // Reusable BitSet for building query masks — avoids allocation per query() call
1301
- scratch_mask = new x();
1454
+ scratch_mask = new k();
1302
1455
  constructor(t) {
1303
- this.store = new At(t?.initial_capacity), this.schedule = new kt(), this.ctx = new Ot(this.store), this._fixed_timestep = t?.fixed_timestep ?? nt, this._max_fixed_steps = t?.max_fixed_steps ?? rt;
1456
+ this.store = new At(t?.initial_capacity), this.schedule = new It(), this.ctx = new Rt(this.store), this._fixed_timestep = t?.fixed_timestep ?? gt, this._max_fixed_steps = t?.max_fixed_steps ?? yt;
1304
1457
  }
1305
1458
  get fixed_timestep() {
1306
1459
  return this._fixed_timestep;
@@ -1323,23 +1476,23 @@ class Nt {
1323
1476
  register_tag() {
1324
1477
  return this.store.register_component({});
1325
1478
  }
1326
- register_event(t) {
1327
- return this.store.register_event(t);
1479
+ register_event(t, e) {
1480
+ this.store.register_event_by_key(t, e);
1328
1481
  }
1329
- register_signal() {
1330
- return this.store.register_event([]);
1482
+ register_signal(t) {
1483
+ this.store.register_event_by_key(t, []);
1331
1484
  }
1332
1485
  register_resource(t, e) {
1333
- return this.store.register_resource(
1334
- t,
1335
- e
1336
- );
1486
+ this.store.register_resource(t, e);
1337
1487
  }
1338
1488
  resource(t) {
1339
- return this.store.get_resource_reader(t);
1489
+ return this.store.get_resource(t);
1340
1490
  }
1341
1491
  set_resource(t, e) {
1342
- this.store.get_resource_channel(t).write(e);
1492
+ this.store.set_resource(t, e);
1493
+ }
1494
+ has_resource(t) {
1495
+ return this.store.has_resource(t);
1343
1496
  }
1344
1497
  create_entity() {
1345
1498
  return this.store.create_entity();
@@ -1354,7 +1507,7 @@ class Nt {
1354
1507
  return this.store.entity_count;
1355
1508
  }
1356
1509
  add_component(t, e, s) {
1357
- return this.store.add_component(t, e, s ?? D), this;
1510
+ return this.store.add_component(t, e, s ?? U), this;
1358
1511
  }
1359
1512
  add_components(t, e) {
1360
1513
  this.store.add_components(t, e);
@@ -1383,11 +1536,16 @@ class Nt {
1383
1536
  return n.read_field(r, e, s);
1384
1537
  }
1385
1538
  set_field(t, e, s, n) {
1386
- const r = this.store.get_entity_archetype(t), i = this.store.get_entity_row(t), _ = r.get_column(e, s);
1387
- _[i] = n;
1539
+ const r = this.store.get_entity_archetype(t), i = this.store.get_entity_row(t), c = r.get_column_mut(e, s, this.store._tick);
1540
+ c[i] = n;
1388
1541
  }
1389
1542
  emit(t, e) {
1390
- e === void 0 ? this.store.emit_signal(t) : this.store.emit_event(t, e);
1543
+ const s = this.store.get_event_def_by_key(t);
1544
+ e === void 0 ? this.store.emit_signal(s) : this.store.emit_event(s, e);
1545
+ }
1546
+ read(t) {
1547
+ const e = this.store.get_event_def_by_key(t);
1548
+ return this.store.get_event_reader(e);
1391
1549
  }
1392
1550
  query(...t) {
1393
1551
  const e = this.scratch_mask;
@@ -1396,15 +1554,14 @@ class Nt {
1396
1554
  e.set(t[s]);
1397
1555
  return this._resolve_query(e.copy(), null, null, t);
1398
1556
  }
1557
+ _get_last_run_tick() {
1558
+ return this.ctx.last_run_tick;
1559
+ }
1399
1560
  /** QueryResolver implementation — creates or retrieves a cached Query. */
1400
1561
  _resolve_query(t, e, s, n) {
1401
- const r = t.hash(), i = e ? e.hash() : 0, _ = s ? s.hash() : 0, c = r ^ Math.imul(i, tt) ^ Math.imul(_, et) | 0, o = this._find_cached(c, t, e, s);
1402
- if (o !== void 0) return o.query;
1403
- const h = this.store.register_query(
1404
- t,
1405
- e ?? void 0,
1406
- s ?? void 0
1407
- ), a = new Pt(
1562
+ const r = t.hash(), i = e ? e.hash() : 0, c = s ? s.hash() : 0, o = r ^ Math.imul(i, dt) ^ Math.imul(c, ut) | 0, _ = this._find_cached(o, t, e, s);
1563
+ if (_ !== void 0) return _.query;
1564
+ const h = this.store.register_query(t, e ?? void 0, s ?? void 0), l = new St(
1408
1565
  h,
1409
1566
  n,
1410
1567
  this,
@@ -1412,20 +1569,20 @@ class Nt {
1412
1569
  e?.copy() ?? null,
1413
1570
  s?.copy() ?? null
1414
1571
  );
1415
- return K(this.query_cache, c, {
1572
+ return this.store.update_query_ref(h, l), K(this.query_cache, o, {
1416
1573
  include_mask: t.copy(),
1417
1574
  exclude_mask: e?.copy() ?? null,
1418
1575
  any_of_mask: s?.copy() ?? null,
1419
- query: a
1420
- }), a;
1576
+ query: l
1577
+ }), l;
1421
1578
  }
1422
1579
  _find_cached(t, e, s, n) {
1423
1580
  const r = this.query_cache.get(t);
1424
1581
  if (r)
1425
1582
  for (let i = 0; i < r.length; i++) {
1426
- const _ = r[i];
1427
- if (!(!_.include_mask.equals(e) || !(s === null ? _.exclude_mask === null : _.exclude_mask !== null && _.exclude_mask.equals(s)) || !(n === null ? _.any_of_mask === null : _.any_of_mask !== null && _.any_of_mask.equals(n))))
1428
- return _;
1583
+ const c = r[i];
1584
+ if (!(!c.include_mask.equals(e) || !(s === null ? c.exclude_mask === null : c.exclude_mask !== null && c.exclude_mask.equals(s)) || !(n === null ? c.any_of_mask === null : c.any_of_mask !== null && c.any_of_mask.equals(n))))
1585
+ return c;
1429
1586
  }
1430
1587
  }
1431
1588
  // any: overload implementation must unify bare fn, (fn, query_fn), and SystemConfig
@@ -1433,15 +1590,13 @@ class Nt {
1433
1590
  let s;
1434
1591
  if (typeof t == "function")
1435
1592
  if (e !== void 0) {
1436
- const i = e(new St(this)), _ = this.ctx, c = t;
1437
- s = { fn: (o, h) => c(i, _, h) };
1593
+ const i = e(new Dt(this)), c = this.ctx, o = t;
1594
+ s = { fn: (_, h) => o(i, c, h) };
1438
1595
  } else
1439
1596
  s = { fn: t };
1440
1597
  else
1441
1598
  s = t;
1442
- const n = Dt(this.next_system_id++), r = Object.freeze(
1443
- Object.assign({ id: n }, s)
1444
- );
1599
+ const n = Pt(this.next_system_id++), r = Object.freeze(Object.assign({ id: n }, s));
1445
1600
  return this.systems.add(r), r;
1446
1601
  }
1447
1602
  add_systems(t, ...e) {
@@ -1456,16 +1611,16 @@ class Nt {
1456
1611
  startup() {
1457
1612
  for (const t of this.systems.values())
1458
1613
  t.on_added?.(this.ctx);
1459
- this.schedule.run_startup(this.ctx);
1614
+ this.schedule.run_startup(this.ctx, this._tick);
1460
1615
  }
1461
1616
  update(t) {
1462
- if (this.schedule.has_fixed_systems()) {
1617
+ if (this.store._tick = this._tick, this.schedule.has_fixed_systems()) {
1463
1618
  this._accumulator += t;
1464
1619
  const e = this._max_fixed_steps * this._fixed_timestep;
1465
1620
  for (this._accumulator > e && (this._accumulator = e); this._accumulator >= this._fixed_timestep; )
1466
- this.schedule.run_fixed_update(this.ctx, this._fixed_timestep), this._accumulator -= this._fixed_timestep;
1621
+ this.schedule.run_fixed_update(this.ctx, this._fixed_timestep, this._tick), this._accumulator -= this._fixed_timestep;
1467
1622
  }
1468
- this.schedule.run_update(this.ctx, t), this.store.clear_events();
1623
+ this.schedule.run_update(this.ctx, t, this._tick), this.store.clear_events(), this._tick++;
1469
1624
  }
1470
1625
  flush() {
1471
1626
  this.ctx.flush();
@@ -1476,10 +1631,17 @@ class Nt {
1476
1631
  this.systems.clear(), this.schedule.clear();
1477
1632
  }
1478
1633
  }
1634
+ function Mt(a) {
1635
+ return Symbol(a);
1636
+ }
1479
1637
  export {
1480
- Nt as ECS,
1481
- Pt as Query,
1482
- St as QueryBuilder,
1483
- Et as SCHEDULE,
1484
- Ot as SystemContext
1638
+ Nt as ChangedQuery,
1639
+ qt as ECS,
1640
+ St as Query,
1641
+ Dt as QueryBuilder,
1642
+ xt as SCHEDULE,
1643
+ Rt as SystemContext,
1644
+ Ot as event_key,
1645
+ Mt as resource_key,
1646
+ Ut as signal_key
1485
1647
  };