archetype-ecs-lib 0.6.0 → 1.0.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.
package/lib/ecs/World.js CHANGED
@@ -100,8 +100,15 @@ var World = /** @class */ (function (_super) {
100
100
  _this.resources = new Map();
101
101
  _this.eventChannels = new Map();
102
102
  _this.snapshotStore = new WorldSnapshotStore_1.WorldSnapshotStore();
103
+ /**
104
+ * Query result cache: maps a sorted query signature key → matching archetypes.
105
+ * `checked` tracks how many archetypes were scanned when the entry was last updated.
106
+ * Because archetypes are append-only, new ones are scanned incrementally on next use.
107
+ */
108
+ _this._queryCache = new Map();
103
109
  /** @internal Phase -> systems mapping for Schedule */
104
110
  _this._scheduleSystems = new Map();
111
+ _this._destroyed = false;
105
112
  _this._resetArchetypes();
106
113
  return _this;
107
114
  }
@@ -235,9 +242,11 @@ var World = /** @class */ (function (_super) {
235
242
  };
236
243
  /** Queue structural changes to apply safely after systems run. */
237
244
  World.prototype.cmd = function () {
245
+ this._ensureNotDestroyed();
238
246
  return this.commands;
239
247
  };
240
248
  World.prototype.addSystem = function (fn) {
249
+ this._ensureNotDestroyed();
241
250
  this.systems.push(fn);
242
251
  return this;
243
252
  };
@@ -265,12 +274,14 @@ var World = /** @class */ (function (_super) {
265
274
  */
266
275
  World.prototype.update = function (dt) {
267
276
  var e_7, _a;
277
+ this._ensureNotDestroyed();
268
278
  // Runtime conflict detection
269
279
  if (this._scheduleSystems.size > 0) {
270
280
  this._warnAboutLifecycleConflict("World.update");
271
281
  }
272
282
  var frameStart = this._profBeginFrame(dt);
273
283
  this._iterateDepth++;
284
+ var caughtError;
274
285
  try {
275
286
  try {
276
287
  for (var _b = __values(this.systems), _c = _b.next(); !_c.done; _c = _b.next()) {
@@ -294,17 +305,30 @@ var World = /** @class */ (function (_super) {
294
305
  finally { if (e_7) throw e_7.error; }
295
306
  }
296
307
  }
308
+ catch (e) {
309
+ caughtError = e;
310
+ }
297
311
  finally {
298
312
  this._iterateDepth--;
299
- this.flush();
313
+ try {
314
+ this.flush();
315
+ }
316
+ catch (e) {
317
+ // System error takes priority; flush error only surfaced when no system threw.
318
+ if (caughtError === undefined)
319
+ caughtError = e;
320
+ }
300
321
  this.swapEvents();
301
322
  this._profAddPhase("update", this._profilingEnabled ? (performance.now() - frameStart) : 0);
302
323
  this._profEndFrame(frameStart);
303
324
  }
325
+ if (caughtError !== undefined)
326
+ throw caughtError;
304
327
  this.updateOverlay(this.stats(), this.statsHistory());
305
328
  };
306
329
  World.prototype.flush = function () {
307
330
  var e_8, _a;
331
+ this._ensureNotDestroyed();
308
332
  this._ensureNotIterating("flush");
309
333
  // Apply commands until the queue is empty. This allows spawn(init) to enqueue add/remove
310
334
  // operations that will be applied during the same flush.
@@ -327,39 +351,61 @@ var World = /** @class */ (function (_super) {
327
351
  }
328
352
  }
329
353
  };
354
+ World.prototype.destroy = function () {
355
+ if (this._destroyed)
356
+ throw new Error("World.destroy() called on an already-destroyed world.");
357
+ this._destroyed = true;
358
+ this.archetypes.length = 0;
359
+ this.archByKey.clear();
360
+ this._queryCache.clear();
361
+ this.systems.length = 0;
362
+ this._scheduleSystems.clear();
363
+ this.resources.clear();
364
+ this.eventChannels.clear();
365
+ this.entities.meta.length = 0;
366
+ };
330
367
  //#region ---------- Snapshot / Restore ----------
331
368
  World.prototype.registerComponentSnapshot = function (key, codec) {
369
+ this._ensureNotDestroyed();
332
370
  this.snapshotStore.registerComponentSnapshot(this._snapshotRuntime(), key, codec);
333
371
  return this;
334
372
  };
335
373
  World.prototype.unregisterComponentSnapshot = function (key) {
374
+ this._ensureNotDestroyed();
336
375
  return this.snapshotStore.unregisterComponentSnapshot(key);
337
376
  };
338
377
  World.prototype.registerResourceSnapshot = function (key, codec) {
378
+ this._ensureNotDestroyed();
339
379
  this.snapshotStore.registerResourceSnapshot(this._snapshotRuntime(), key, codec);
340
380
  return this;
341
381
  };
342
382
  World.prototype.unregisterResourceSnapshot = function (key) {
383
+ this._ensureNotDestroyed();
343
384
  return this.snapshotStore.unregisterResourceSnapshot(key);
344
385
  };
345
386
  World.prototype.snapshot = function () {
387
+ this._ensureNotDestroyed();
346
388
  return this.snapshotStore.snapshot(this._snapshotRuntime());
347
389
  };
348
390
  World.prototype.restore = function (snapshot) {
391
+ this._ensureNotDestroyed();
349
392
  this.snapshotStore.restore(this._snapshotRuntime(), snapshot);
350
393
  this.updateOverlay(this.stats(), this.statsHistory());
351
394
  };
352
395
  //#endregion
353
396
  //#region ---------- Resources (singletons) ----------
354
397
  World.prototype.setResource = function (key, value) {
398
+ this._ensureNotDestroyed();
355
399
  this.resources.set(key, value);
356
400
  };
357
401
  World.prototype.getResource = function (key) {
402
+ this._ensureNotDestroyed();
358
403
  if (!this.resources.has(key))
359
404
  return undefined;
360
405
  return this.resources.get(key);
361
406
  };
362
407
  World.prototype.requireResource = function (key) {
408
+ this._ensureNotDestroyed();
363
409
  if (!this.resources.has(key)) {
364
410
  var name_2 = this._formatCtor(key);
365
411
  throw new Error("requireResource(".concat(name_2, ") failed: resource missing. ") +
@@ -368,12 +414,15 @@ var World = /** @class */ (function (_super) {
368
414
  return this.resources.get(key);
369
415
  };
370
416
  World.prototype.hasResource = function (key) {
417
+ this._ensureNotDestroyed();
371
418
  return this.resources.has(key);
372
419
  };
373
420
  World.prototype.removeResource = function (key) {
421
+ this._ensureNotDestroyed();
374
422
  return this.resources.delete(key);
375
423
  };
376
424
  World.prototype.initResource = function (key, factory) {
425
+ this._ensureNotDestroyed();
377
426
  if (this.resources.has(key))
378
427
  return this.resources.get(key);
379
428
  var value = factory();
@@ -383,12 +432,15 @@ var World = /** @class */ (function (_super) {
383
432
  //#endregion
384
433
  //#region ---------- Events (phase-scoped) ----------
385
434
  World.prototype.emit = function (key, ev) {
435
+ this._ensureNotDestroyed();
386
436
  this._events(key).emit(ev);
387
437
  };
388
438
  World.prototype.events = function (key) {
439
+ this._ensureNotDestroyed();
389
440
  return this._events(key);
390
441
  };
391
442
  World.prototype.drainEvents = function (key, fn) {
443
+ this._ensureNotDestroyed();
392
444
  var ch = this.eventChannels.get(key);
393
445
  if (!ch)
394
446
  return;
@@ -396,6 +448,7 @@ var World = /** @class */ (function (_super) {
396
448
  };
397
449
  World.prototype.clearEvents = function (key) {
398
450
  var e_9, _a;
451
+ this._ensureNotDestroyed();
399
452
  if (key) {
400
453
  var ch = this.eventChannels.get(key);
401
454
  if (!ch)
@@ -421,6 +474,7 @@ var World = /** @class */ (function (_super) {
421
474
  /** @internal Called by Schedule at phase boundaries */
422
475
  World.prototype.swapEvents = function () {
423
476
  var e_10, _a;
477
+ this._ensureNotDestroyed();
424
478
  try {
425
479
  for (var _b = __values(this.eventChannels.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
426
480
  var ch = _c.value;
@@ -438,6 +492,7 @@ var World = /** @class */ (function (_super) {
438
492
  //#endregion
439
493
  //#region ---------- Entity lifecycle ----------
440
494
  World.prototype.spawn = function () {
495
+ this._ensureNotDestroyed();
441
496
  this._ensureNotIterating("spawn");
442
497
  var entity = this.entities.create();
443
498
  // place in archetype 0
@@ -448,7 +503,7 @@ var World = /** @class */ (function (_super) {
448
503
  entityMeta.row = row;
449
504
  return entity;
450
505
  };
451
- World.prototype.spawnMany = function () {
506
+ World.prototype.spawnWith = function () {
452
507
  var e_11, _a;
453
508
  var items = [];
454
509
  for (var _i = 0; _i < arguments.length; _i++) {
@@ -471,9 +526,11 @@ var World = /** @class */ (function (_super) {
471
526
  return e;
472
527
  };
473
528
  World.prototype.isAlive = function (e) {
529
+ this._ensureNotDestroyed();
474
530
  return this.entities.isAlive(e);
475
531
  };
476
532
  World.prototype.despawn = function (e) {
533
+ this._ensureNotDestroyed();
477
534
  this._assertAlive(e, 'despawn');
478
535
  this._ensureNotIterating("despawn");
479
536
  this._removeFromArchetype(e);
@@ -498,6 +555,7 @@ var World = /** @class */ (function (_super) {
498
555
  //#endregion
499
556
  //#region ---------- Components ----------
500
557
  World.prototype.has = function (e, ctor) {
558
+ this._ensureNotDestroyed();
501
559
  var meta = this.entities.meta[e.id];
502
560
  if (!meta || !meta.alive || meta.gen !== e.gen)
503
561
  return false;
@@ -505,6 +563,7 @@ var World = /** @class */ (function (_super) {
505
563
  return this.archetypes[meta.arch].has(tid);
506
564
  };
507
565
  World.prototype.get = function (e, ctor) {
566
+ this._ensureNotDestroyed();
508
567
  var meta = this.entities.meta[e.id];
509
568
  if (!meta || !meta.alive || meta.gen !== e.gen)
510
569
  return undefined;
@@ -515,6 +574,7 @@ var World = /** @class */ (function (_super) {
515
574
  return a.column(tid)[meta.row];
516
575
  };
517
576
  World.prototype.set = function (e, ctor, value) {
577
+ this._ensureNotDestroyed();
518
578
  var op = "set(".concat(this._formatCtor(ctor), ")");
519
579
  var meta = this._assertAlive(e, op);
520
580
  var tid = (0, TypeRegistry_1.typeId)(ctor);
@@ -524,6 +584,7 @@ var World = /** @class */ (function (_super) {
524
584
  a.column(tid)[meta.row] = value;
525
585
  };
526
586
  World.prototype.add = function (e, ctor, value) {
587
+ this._ensureNotDestroyed();
527
588
  var op = "add(".concat(this._formatCtor(ctor), ")");
528
589
  this._assertAlive(e, op);
529
590
  this._ensureNotIterating(op);
@@ -535,8 +596,11 @@ var World = /** @class */ (function (_super) {
535
596
  src.column(tid)[srcMeta.row] = value;
536
597
  return;
537
598
  }
538
- var dstSig = (0, Signature_1.mergeSignature)(src.sig, tid);
539
- var dst = this._getOrCreateArchetype(dstSig);
599
+ var dst = src.addEdges.get(tid);
600
+ if (!dst) {
601
+ dst = this._getOrCreateArchetype((0, Signature_1.mergeSignature)(src.sig, tid));
602
+ src.addEdges.set(tid, dst);
603
+ }
540
604
  this._moveEntity(e, src, srcMeta.row, dst, function (t) {
541
605
  if (t === tid)
542
606
  return value;
@@ -551,6 +615,7 @@ var World = /** @class */ (function (_super) {
551
615
  }
552
616
  if (items.length === 0)
553
617
  return;
618
+ this._ensureNotDestroyed();
554
619
  this._assertAlive(e, "addMany");
555
620
  this._ensureNotIterating("addMany");
556
621
  var srcMeta = this.entities.meta[e.id];
@@ -610,6 +675,7 @@ var World = /** @class */ (function (_super) {
610
675
  });
611
676
  };
612
677
  World.prototype.remove = function (e, ctor) {
678
+ this._ensureNotDestroyed();
613
679
  var op = "remove(".concat(this._formatCtor(ctor), ")");
614
680
  this._assertAlive(e, op);
615
681
  this._ensureNotIterating(op);
@@ -618,8 +684,11 @@ var World = /** @class */ (function (_super) {
618
684
  var src = this.archetypes[srcMeta.arch];
619
685
  if (!src.has(tid))
620
686
  return;
621
- var dstSig = (0, Signature_1.subtractSignature)(src.sig, tid);
622
- var dst = this._getOrCreateArchetype(dstSig);
687
+ var dst = src.removeEdges.get(tid);
688
+ if (!dst) {
689
+ dst = this._getOrCreateArchetype((0, Signature_1.subtractSignature)(src.sig, tid));
690
+ src.removeEdges.set(tid, dst);
691
+ }
623
692
  this._moveEntity(e, src, srcMeta.row, dst, function (t) {
624
693
  // copy all but removed, but dstSig guarantees t != tid
625
694
  return src.column(t)[srcMeta.row];
@@ -633,6 +702,7 @@ var World = /** @class */ (function (_super) {
633
702
  }
634
703
  if (ctors.length === 0)
635
704
  return;
705
+ this._ensureNotDestroyed();
636
706
  this._assertAlive(e, "removeMany");
637
707
  this._ensureNotIterating("removeMany");
638
708
  var srcMeta = this.entities.meta[e.id];
@@ -668,102 +738,206 @@ var World = /** @class */ (function (_super) {
668
738
  });
669
739
  };
670
740
  World.prototype.query = function () {
671
- var ctors = [];
741
+ var _a;
742
+ var args = [];
672
743
  for (var _i = 0; _i < arguments.length; _i++) {
673
- ctors[_i] = arguments[_i];
744
+ args[_i] = arguments[_i];
674
745
  }
675
- var _a = World._buildQueryTypeIds(ctors), requested = _a.requested, needSorted = _a.needSorted;
676
- function gen(world) {
677
- var _a, _b, a, cols, i, row, e, out, i, e_16_1;
678
- var e_16, _c;
679
- return __generator(this, function (_d) {
680
- switch (_d.label) {
746
+ var _b = World._splitArgs(args), ctors = _b.ctors, filter = _b.filter;
747
+ var _c = World._buildQueryTypeIds(ctors, filter), requested = _c.requested, needSorted = _c.needSorted, withoutSorted = _c.withoutSorted;
748
+ function gen(world, archs) {
749
+ var archs_1, archs_1_1, a, cols, i, ents, len, _a, a0, r, a0, a1, r, a0, a1, a2, r, a0, a1, a2, a3, r, a0, a1, a2, a3, a4, r, a0, a1, a2, a3, a4, a5, r, r, out, i, e_16_1;
750
+ var e_16, _b;
751
+ return __generator(this, function (_c) {
752
+ switch (_c.label) {
681
753
  case 0:
682
754
  world._iterateDepth++;
683
- _d.label = 1;
755
+ _c.label = 1;
684
756
  case 1:
685
- _d.trys.push([1, , 12, 13]);
686
- _d.label = 2;
757
+ _c.trys.push([1, , 44, 45]);
758
+ _c.label = 2;
687
759
  case 2:
688
- _d.trys.push([2, 9, 10, 11]);
689
- _a = __values(world.archetypes), _b = _a.next();
690
- _d.label = 3;
760
+ _c.trys.push([2, 41, 42, 43]);
761
+ archs_1 = __values(archs), archs_1_1 = archs_1.next();
762
+ _c.label = 3;
691
763
  case 3:
692
- if (!!_b.done) return [3 /*break*/, 8];
693
- a = _b.value;
694
- if (!a)
695
- return [3 /*break*/, 7];
696
- if (!(0, Signature_1.signatureHasAll)(a.sig, needSorted))
697
- return [3 /*break*/, 7];
764
+ if (!!archs_1_1.done) return [3 /*break*/, 40];
765
+ a = archs_1_1.value;
766
+ if (withoutSorted.length > 0 && (0, Signature_1.signatureHasAny)(a.sig, withoutSorted))
767
+ return [3 /*break*/, 39];
698
768
  cols = new Array(requested.length);
699
769
  for (i = 0; i < requested.length; i++)
700
770
  cols[i] = a.column(requested[i]);
701
- row = 0;
702
- _d.label = 4;
771
+ ents = a.entities;
772
+ len = ents.length;
773
+ _a = cols.length;
774
+ switch (_a) {
775
+ case 1: return [3 /*break*/, 4];
776
+ case 2: return [3 /*break*/, 9];
777
+ case 3: return [3 /*break*/, 14];
778
+ case 4: return [3 /*break*/, 19];
779
+ case 5: return [3 /*break*/, 24];
780
+ case 6: return [3 /*break*/, 29];
781
+ }
782
+ return [3 /*break*/, 34];
703
783
  case 4:
704
- if (!(row < a.entities.length)) return [3 /*break*/, 7];
705
- e = a.entities[row];
706
- out = { e: e };
707
- for (i = 0; i < cols.length; i++)
708
- out["c".concat(i + 1)] = cols[i][row];
709
- return [4 /*yield*/, out];
784
+ a0 = cols[0];
785
+ r = 0;
786
+ _c.label = 5;
710
787
  case 5:
711
- _d.sent();
712
- _d.label = 6;
788
+ if (!(r < len)) return [3 /*break*/, 8];
789
+ return [4 /*yield*/, { e: ents[r], c1: a0[r] }];
713
790
  case 6:
714
- row++;
715
- return [3 /*break*/, 4];
791
+ _c.sent();
792
+ _c.label = 7;
716
793
  case 7:
717
- _b = _a.next();
718
- return [3 /*break*/, 3];
719
- case 8: return [3 /*break*/, 11];
794
+ r++;
795
+ return [3 /*break*/, 5];
796
+ case 8: return [3 /*break*/, 39];
720
797
  case 9:
721
- e_16_1 = _d.sent();
722
- e_16 = { error: e_16_1 };
723
- return [3 /*break*/, 11];
798
+ a0 = cols[0], a1 = cols[1];
799
+ r = 0;
800
+ _c.label = 10;
724
801
  case 10:
802
+ if (!(r < len)) return [3 /*break*/, 13];
803
+ return [4 /*yield*/, { e: ents[r], c1: a0[r], c2: a1[r] }];
804
+ case 11:
805
+ _c.sent();
806
+ _c.label = 12;
807
+ case 12:
808
+ r++;
809
+ return [3 /*break*/, 10];
810
+ case 13: return [3 /*break*/, 39];
811
+ case 14:
812
+ a0 = cols[0], a1 = cols[1], a2 = cols[2];
813
+ r = 0;
814
+ _c.label = 15;
815
+ case 15:
816
+ if (!(r < len)) return [3 /*break*/, 18];
817
+ return [4 /*yield*/, { e: ents[r], c1: a0[r], c2: a1[r], c3: a2[r] }];
818
+ case 16:
819
+ _c.sent();
820
+ _c.label = 17;
821
+ case 17:
822
+ r++;
823
+ return [3 /*break*/, 15];
824
+ case 18: return [3 /*break*/, 39];
825
+ case 19:
826
+ a0 = cols[0], a1 = cols[1], a2 = cols[2], a3 = cols[3];
827
+ r = 0;
828
+ _c.label = 20;
829
+ case 20:
830
+ if (!(r < len)) return [3 /*break*/, 23];
831
+ return [4 /*yield*/, { e: ents[r], c1: a0[r], c2: a1[r], c3: a2[r], c4: a3[r] }];
832
+ case 21:
833
+ _c.sent();
834
+ _c.label = 22;
835
+ case 22:
836
+ r++;
837
+ return [3 /*break*/, 20];
838
+ case 23: return [3 /*break*/, 39];
839
+ case 24:
840
+ a0 = cols[0], a1 = cols[1], a2 = cols[2], a3 = cols[3], a4 = cols[4];
841
+ r = 0;
842
+ _c.label = 25;
843
+ case 25:
844
+ if (!(r < len)) return [3 /*break*/, 28];
845
+ return [4 /*yield*/, { e: ents[r], c1: a0[r], c2: a1[r], c3: a2[r], c4: a3[r], c5: a4[r] }];
846
+ case 26:
847
+ _c.sent();
848
+ _c.label = 27;
849
+ case 27:
850
+ r++;
851
+ return [3 /*break*/, 25];
852
+ case 28: return [3 /*break*/, 39];
853
+ case 29:
854
+ a0 = cols[0], a1 = cols[1], a2 = cols[2], a3 = cols[3], a4 = cols[4], a5 = cols[5];
855
+ r = 0;
856
+ _c.label = 30;
857
+ case 30:
858
+ if (!(r < len)) return [3 /*break*/, 33];
859
+ return [4 /*yield*/, { e: ents[r], c1: a0[r], c2: a1[r], c3: a2[r], c4: a3[r], c5: a4[r], c6: a5[r] }];
860
+ case 31:
861
+ _c.sent();
862
+ _c.label = 32;
863
+ case 32:
864
+ r++;
865
+ return [3 /*break*/, 30];
866
+ case 33: return [3 /*break*/, 39];
867
+ case 34:
868
+ r = 0;
869
+ _c.label = 35;
870
+ case 35:
871
+ if (!(r < len)) return [3 /*break*/, 38];
872
+ out = { e: ents[r] };
873
+ for (i = 0; i < cols.length; i++)
874
+ out["c".concat(i + 1)] = cols[i][r];
875
+ return [4 /*yield*/, out];
876
+ case 36:
877
+ _c.sent();
878
+ _c.label = 37;
879
+ case 37:
880
+ r++;
881
+ return [3 /*break*/, 35];
882
+ case 38: return [3 /*break*/, 39];
883
+ case 39:
884
+ archs_1_1 = archs_1.next();
885
+ return [3 /*break*/, 3];
886
+ case 40: return [3 /*break*/, 43];
887
+ case 41:
888
+ e_16_1 = _c.sent();
889
+ e_16 = { error: e_16_1 };
890
+ return [3 /*break*/, 43];
891
+ case 42:
725
892
  try {
726
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
893
+ if (archs_1_1 && !archs_1_1.done && (_b = archs_1.return)) _b.call(archs_1);
727
894
  }
728
895
  finally { if (e_16) throw e_16.error; }
729
896
  return [7 /*endfinally*/];
730
- case 11: return [3 /*break*/, 13];
731
- case 12:
897
+ case 43: return [3 /*break*/, 45];
898
+ case 44:
732
899
  world._iterateDepth--;
733
900
  return [7 /*endfinally*/];
734
- case 13: return [2 /*return*/];
901
+ case 45: return [2 /*return*/];
735
902
  }
736
903
  });
737
904
  }
738
- return gen(this);
905
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
906
+ var self = this;
907
+ return _a = {},
908
+ _a[Symbol.iterator] = function () {
909
+ self._ensureNotDestroyed();
910
+ return gen(self, self._matchingArchetypes(needSorted));
911
+ },
912
+ _a;
739
913
  };
740
914
  World.prototype.queryTables = function () {
741
- var ctors = [];
915
+ var _a;
916
+ var args = [];
742
917
  for (var _i = 0; _i < arguments.length; _i++) {
743
- ctors[_i] = arguments[_i];
918
+ args[_i] = arguments[_i];
744
919
  }
745
- var _a = World._buildQueryTypeIds(ctors), requested = _a.requested, needSorted = _a.needSorted;
746
- function gen(world) {
747
- var _a, _b, a, out, i, e_17_1;
748
- var e_17, _c;
749
- return __generator(this, function (_d) {
750
- switch (_d.label) {
920
+ var _b = World._splitArgs(args), ctors = _b.ctors, filter = _b.filter;
921
+ var _c = World._buildQueryTypeIds(ctors, filter), requested = _c.requested, needSorted = _c.needSorted, withoutSorted = _c.withoutSorted;
922
+ function gen(world, archs) {
923
+ var archs_2, archs_2_1, a, out, i, e_17_1;
924
+ var e_17, _a;
925
+ return __generator(this, function (_b) {
926
+ switch (_b.label) {
751
927
  case 0:
752
928
  world._iterateDepth++;
753
- _d.label = 1;
929
+ _b.label = 1;
754
930
  case 1:
755
- _d.trys.push([1, , 10, 11]);
756
- _d.label = 2;
931
+ _b.trys.push([1, , 10, 11]);
932
+ _b.label = 2;
757
933
  case 2:
758
- _d.trys.push([2, 7, 8, 9]);
759
- _a = __values(world.archetypes), _b = _a.next();
760
- _d.label = 3;
934
+ _b.trys.push([2, 7, 8, 9]);
935
+ archs_2 = __values(archs), archs_2_1 = archs_2.next();
936
+ _b.label = 3;
761
937
  case 3:
762
- if (!!_b.done) return [3 /*break*/, 6];
763
- a = _b.value;
764
- if (!a)
765
- return [3 /*break*/, 5];
766
- if (!(0, Signature_1.signatureHasAll)(a.sig, needSorted))
938
+ if (!!archs_2_1.done) return [3 /*break*/, 6];
939
+ a = archs_2_1.value;
940
+ if (withoutSorted.length > 0 && (0, Signature_1.signatureHasAny)(a.sig, withoutSorted))
767
941
  return [3 /*break*/, 5];
768
942
  out = { entities: a.entities };
769
943
  for (i = 0; i < requested.length; i++) {
@@ -771,19 +945,19 @@ var World = /** @class */ (function (_super) {
771
945
  }
772
946
  return [4 /*yield*/, out];
773
947
  case 4:
774
- _d.sent();
775
- _d.label = 5;
948
+ _b.sent();
949
+ _b.label = 5;
776
950
  case 5:
777
- _b = _a.next();
951
+ archs_2_1 = archs_2.next();
778
952
  return [3 /*break*/, 3];
779
953
  case 6: return [3 /*break*/, 9];
780
954
  case 7:
781
- e_17_1 = _d.sent();
955
+ e_17_1 = _b.sent();
782
956
  e_17 = { error: e_17_1 };
783
957
  return [3 /*break*/, 9];
784
958
  case 8:
785
959
  try {
786
- if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
960
+ if (archs_2_1 && !archs_2_1.done && (_a = archs_2.return)) _a.call(archs_2);
787
961
  }
788
962
  finally { if (e_17) throw e_17.error; }
789
963
  return [7 /*endfinally*/];
@@ -795,7 +969,14 @@ var World = /** @class */ (function (_super) {
795
969
  }
796
970
  });
797
971
  }
798
- return gen(this);
972
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
973
+ var self = this;
974
+ return _a = {},
975
+ _a[Symbol.iterator] = function () {
976
+ self._ensureNotDestroyed();
977
+ return gen(self, self._matchingArchetypes(needSorted));
978
+ },
979
+ _a;
799
980
  };
800
981
  World.prototype.queryEach = function () {
801
982
  var e_18, _a;
@@ -803,56 +984,77 @@ var World = /** @class */ (function (_super) {
803
984
  for (var _i = 0; _i < arguments.length; _i++) {
804
985
  args[_i] = arguments[_i];
805
986
  }
987
+ this._ensureNotDestroyed();
806
988
  var fn = args[args.length - 1];
807
- var ctors = args.slice(0, args.length - 1);
808
- var _b = World._buildQueryTypeIds(ctors), requested = _b.requested, needSorted = _b.needSorted;
989
+ var rest = args.slice(0, args.length - 1);
990
+ var filter = World._isQueryFilter(rest[rest.length - 1]) ? rest.pop() : undefined;
991
+ var ctors = rest;
992
+ var _b = World._buildQueryTypeIds(ctors, filter), requested = _b.requested, needSorted = _b.needSorted, withoutSorted = _b.withoutSorted;
993
+ var archs = this._matchingArchetypes(needSorted);
809
994
  this._iterateDepth++;
810
995
  try {
811
996
  try {
812
- for (var _c = __values(this.archetypes), _d = _c.next(); !_d.done; _d = _c.next()) {
813
- var a = _d.value;
814
- if (!a)
815
- continue;
816
- if (!(0, Signature_1.signatureHasAll)(a.sig, needSorted))
997
+ for (var archs_3 = __values(archs), archs_3_1 = archs_3.next(); !archs_3_1.done; archs_3_1 = archs_3.next()) {
998
+ var a = archs_3_1.value;
999
+ if (withoutSorted.length > 0 && (0, Signature_1.signatureHasAny)(a.sig, withoutSorted))
817
1000
  continue;
818
1001
  var cols = new Array(requested.length);
819
1002
  for (var i = 0; i < requested.length; i++)
820
1003
  cols[i] = a.column(requested[i]);
821
- var _loop_1 = function (row) {
822
- var e = a.entities[row];
823
- switch (cols.length) {
824
- case 1:
825
- fn(e, cols[0][row]);
826
- break;
827
- case 2:
828
- fn(e, cols[0][row], cols[1][row]);
829
- break;
830
- case 3:
831
- fn(e, cols[0][row], cols[1][row], cols[2][row]);
832
- break;
833
- case 4:
834
- fn(e, cols[0][row], cols[1][row], cols[2][row], cols[3][row]);
835
- break;
836
- case 5:
837
- fn(e, cols[0][row], cols[1][row], cols[2][row], cols[3][row], cols[4][row]);
838
- break;
839
- case 6:
840
- fn(e, cols[0][row], cols[1][row], cols[2][row], cols[3][row], cols[4][row], cols[5][row]);
841
- break;
842
- default:
843
- fn.apply(void 0, __spreadArray([e], __read(cols.map(function (c) { return c[row]; })), false));
844
- break;
1004
+ var ents = a.entities;
1005
+ var len = ents.length;
1006
+ switch (cols.length) {
1007
+ case 1: {
1008
+ var a0 = cols[0];
1009
+ for (var r = 0; r < len; r++)
1010
+ fn(ents[r], a0[r]);
1011
+ break;
1012
+ }
1013
+ case 2: {
1014
+ var a0 = cols[0], a1 = cols[1];
1015
+ for (var r = 0; r < len; r++)
1016
+ fn(ents[r], a0[r], a1[r]);
1017
+ break;
1018
+ }
1019
+ case 3: {
1020
+ var a0 = cols[0], a1 = cols[1], a2 = cols[2];
1021
+ for (var r = 0; r < len; r++)
1022
+ fn(ents[r], a0[r], a1[r], a2[r]);
1023
+ break;
1024
+ }
1025
+ case 4: {
1026
+ var a0 = cols[0], a1 = cols[1], a2 = cols[2], a3 = cols[3];
1027
+ for (var r = 0; r < len; r++)
1028
+ fn(ents[r], a0[r], a1[r], a2[r], a3[r]);
1029
+ break;
845
1030
  }
846
- };
847
- for (var row = 0; row < a.entities.length; row++) {
848
- _loop_1(row);
1031
+ case 5: {
1032
+ var a0 = cols[0], a1 = cols[1], a2 = cols[2], a3 = cols[3], a4 = cols[4];
1033
+ for (var r = 0; r < len; r++)
1034
+ fn(ents[r], a0[r], a1[r], a2[r], a3[r], a4[r]);
1035
+ break;
1036
+ }
1037
+ case 6: {
1038
+ var a0 = cols[0], a1 = cols[1], a2 = cols[2], a3 = cols[3], a4 = cols[4], a5 = cols[5];
1039
+ for (var r = 0; r < len; r++)
1040
+ fn(ents[r], a0[r], a1[r], a2[r], a3[r], a4[r], a5[r]);
1041
+ break;
1042
+ }
1043
+ default:
1044
+ var _loop_1 = function (r) {
1045
+ fn.apply(void 0, __spreadArray([ents[r]], __read(cols.map(function (c) { return c[r]; })), false));
1046
+ };
1047
+ for (var r = 0; r < len; r++) {
1048
+ _loop_1(r);
1049
+ }
1050
+ break;
849
1051
  }
850
1052
  }
851
1053
  }
852
1054
  catch (e_18_1) { e_18 = { error: e_18_1 }; }
853
1055
  finally {
854
1056
  try {
855
- if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
1057
+ if (archs_3_1 && !archs_3_1.done && (_a = archs_3.return)) _a.call(archs_3);
856
1058
  }
857
1059
  finally { if (e_18) throw e_18.error; }
858
1060
  }
@@ -881,19 +1083,52 @@ var World = /** @class */ (function (_super) {
881
1083
  World.prototype._resetArchetypes = function () {
882
1084
  this.archetypes.length = 0;
883
1085
  this.archByKey.clear();
1086
+ this._queryCache.clear();
884
1087
  // Archetype 0: empty signature
885
1088
  var archetype0 = new Archetype_1.Archetype(0, []);
886
1089
  this.archetypes[0] = archetype0;
887
1090
  this.archByKey.set("", archetype0);
888
1091
  };
889
- World._buildQueryTypeIds = function (ctors) {
1092
+ World._isQueryFilter = function (v) {
1093
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
1094
+ };
1095
+ World._splitArgs = function (args) {
1096
+ var last = args[args.length - 1];
1097
+ if (World._isQueryFilter(last)) {
1098
+ return { ctors: args.slice(0, args.length - 1), filter: last };
1099
+ }
1100
+ return { ctors: args, filter: undefined };
1101
+ };
1102
+ /**
1103
+ * Returns the list of archetypes matching `needSorted` (a sorted, deduped TypeId array).
1104
+ * Results are cached per query signature. Because archetypes are append-only, stale entries
1105
+ * are extended incrementally — only newly-added archetypes are re-scanned.
1106
+ */
1107
+ World.prototype._matchingArchetypes = function (needSorted) {
1108
+ var key = (0, Signature_1.signatureKey)(needSorted);
1109
+ var entry = this._queryCache.get(key);
1110
+ if (!entry) {
1111
+ entry = { archs: [], checked: 0 };
1112
+ this._queryCache.set(key, entry);
1113
+ }
1114
+ var total = this.archetypes.length;
1115
+ for (var i = entry.checked; i < total; i++) {
1116
+ var a = this.archetypes[i];
1117
+ if (a && (0, Signature_1.signatureHasAll)(a.sig, needSorted))
1118
+ entry.archs.push(a);
1119
+ }
1120
+ entry.checked = total;
1121
+ return entry.archs;
1122
+ };
1123
+ World._buildQueryTypeIds = function (ctors, filter) {
1124
+ var _a, _b, _c, _d;
890
1125
  // Preserve caller order for (c1,c2,c3,...) mapping.
891
1126
  var requested = new Array(ctors.length);
892
1127
  for (var i = 0; i < ctors.length; i++)
893
1128
  requested[i] = (0, TypeRegistry_1.typeId)(ctors[i]);
894
- // Same ids, but sorted + deduped for signatureHasAll().
895
- var needSorted = requested.slice();
896
- needSorted.sort(function (a, b) { return a - b; });
1129
+ // needSorted = requested ids + any "with-only" ids (present but not returned), sorted + deduped.
1130
+ var withOnlyIds = (_b = (_a = filter === null || filter === void 0 ? void 0 : filter.with) === null || _a === void 0 ? void 0 : _a.map(function (c) { return (0, TypeRegistry_1.typeId)(c); })) !== null && _b !== void 0 ? _b : [];
1131
+ var needSorted = __spreadArray(__spreadArray([], __read(requested), false), __read(withOnlyIds), false).sort(function (a, b) { return a - b; });
897
1132
  var w = 0;
898
1133
  for (var i = 0; i < needSorted.length; i++) {
899
1134
  var v = needSorted[i];
@@ -901,7 +1136,13 @@ var World = /** @class */ (function (_super) {
901
1136
  needSorted[w++] = v;
902
1137
  }
903
1138
  needSorted.length = w;
904
- return { requested: requested, needSorted: needSorted };
1139
+ // withoutSorted: archetypes that have ANY of these are excluded.
1140
+ var withoutSorted = ((_d = (_c = filter === null || filter === void 0 ? void 0 : filter.without) === null || _c === void 0 ? void 0 : _c.map(function (c) { return (0, TypeRegistry_1.typeId)(c); })) !== null && _d !== void 0 ? _d : []).sort(function (a, b) { return a - b; });
1141
+ return { requested: requested, needSorted: needSorted, withoutSorted: withoutSorted };
1142
+ };
1143
+ World.prototype._ensureNotDestroyed = function () {
1144
+ if (this._destroyed)
1145
+ throw new Error("Cannot use a destroyed World.");
905
1146
  };
906
1147
  World.prototype._ensureNotIterating = function (op) {
907
1148
  if (this._iterateDepth > 0) {
@@ -974,8 +1215,12 @@ var World = /** @class */ (function (_super) {
974
1215
  return this.despawn(op.e);
975
1216
  case "add":
976
1217
  return this.add(op.e, op.ctor, op.value);
1218
+ case "addMany":
1219
+ return this.addMany.apply(this, __spreadArray([op.e], __read(op.items), false));
977
1220
  case "remove":
978
1221
  return this.remove(op.e, op.ctor);
1222
+ case "removeMany":
1223
+ return this.removeMany.apply(this, __spreadArray([op.e], __read(op.ctors), false));
979
1224
  }
980
1225
  };
981
1226
  World.prototype._formatEntity = function (e) {