archetype-ecs-lib 0.6.1 → 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,6 +274,7 @@ 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");
@@ -318,6 +328,7 @@ var World = /** @class */ (function (_super) {
318
328
  };
319
329
  World.prototype.flush = function () {
320
330
  var e_8, _a;
331
+ this._ensureNotDestroyed();
321
332
  this._ensureNotIterating("flush");
322
333
  // Apply commands until the queue is empty. This allows spawn(init) to enqueue add/remove
323
334
  // operations that will be applied during the same flush.
@@ -340,39 +351,61 @@ var World = /** @class */ (function (_super) {
340
351
  }
341
352
  }
342
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
+ };
343
367
  //#region ---------- Snapshot / Restore ----------
344
368
  World.prototype.registerComponentSnapshot = function (key, codec) {
369
+ this._ensureNotDestroyed();
345
370
  this.snapshotStore.registerComponentSnapshot(this._snapshotRuntime(), key, codec);
346
371
  return this;
347
372
  };
348
373
  World.prototype.unregisterComponentSnapshot = function (key) {
374
+ this._ensureNotDestroyed();
349
375
  return this.snapshotStore.unregisterComponentSnapshot(key);
350
376
  };
351
377
  World.prototype.registerResourceSnapshot = function (key, codec) {
378
+ this._ensureNotDestroyed();
352
379
  this.snapshotStore.registerResourceSnapshot(this._snapshotRuntime(), key, codec);
353
380
  return this;
354
381
  };
355
382
  World.prototype.unregisterResourceSnapshot = function (key) {
383
+ this._ensureNotDestroyed();
356
384
  return this.snapshotStore.unregisterResourceSnapshot(key);
357
385
  };
358
386
  World.prototype.snapshot = function () {
387
+ this._ensureNotDestroyed();
359
388
  return this.snapshotStore.snapshot(this._snapshotRuntime());
360
389
  };
361
390
  World.prototype.restore = function (snapshot) {
391
+ this._ensureNotDestroyed();
362
392
  this.snapshotStore.restore(this._snapshotRuntime(), snapshot);
363
393
  this.updateOverlay(this.stats(), this.statsHistory());
364
394
  };
365
395
  //#endregion
366
396
  //#region ---------- Resources (singletons) ----------
367
397
  World.prototype.setResource = function (key, value) {
398
+ this._ensureNotDestroyed();
368
399
  this.resources.set(key, value);
369
400
  };
370
401
  World.prototype.getResource = function (key) {
402
+ this._ensureNotDestroyed();
371
403
  if (!this.resources.has(key))
372
404
  return undefined;
373
405
  return this.resources.get(key);
374
406
  };
375
407
  World.prototype.requireResource = function (key) {
408
+ this._ensureNotDestroyed();
376
409
  if (!this.resources.has(key)) {
377
410
  var name_2 = this._formatCtor(key);
378
411
  throw new Error("requireResource(".concat(name_2, ") failed: resource missing. ") +
@@ -381,12 +414,15 @@ var World = /** @class */ (function (_super) {
381
414
  return this.resources.get(key);
382
415
  };
383
416
  World.prototype.hasResource = function (key) {
417
+ this._ensureNotDestroyed();
384
418
  return this.resources.has(key);
385
419
  };
386
420
  World.prototype.removeResource = function (key) {
421
+ this._ensureNotDestroyed();
387
422
  return this.resources.delete(key);
388
423
  };
389
424
  World.prototype.initResource = function (key, factory) {
425
+ this._ensureNotDestroyed();
390
426
  if (this.resources.has(key))
391
427
  return this.resources.get(key);
392
428
  var value = factory();
@@ -396,12 +432,15 @@ var World = /** @class */ (function (_super) {
396
432
  //#endregion
397
433
  //#region ---------- Events (phase-scoped) ----------
398
434
  World.prototype.emit = function (key, ev) {
435
+ this._ensureNotDestroyed();
399
436
  this._events(key).emit(ev);
400
437
  };
401
438
  World.prototype.events = function (key) {
439
+ this._ensureNotDestroyed();
402
440
  return this._events(key);
403
441
  };
404
442
  World.prototype.drainEvents = function (key, fn) {
443
+ this._ensureNotDestroyed();
405
444
  var ch = this.eventChannels.get(key);
406
445
  if (!ch)
407
446
  return;
@@ -409,6 +448,7 @@ var World = /** @class */ (function (_super) {
409
448
  };
410
449
  World.prototype.clearEvents = function (key) {
411
450
  var e_9, _a;
451
+ this._ensureNotDestroyed();
412
452
  if (key) {
413
453
  var ch = this.eventChannels.get(key);
414
454
  if (!ch)
@@ -434,6 +474,7 @@ var World = /** @class */ (function (_super) {
434
474
  /** @internal Called by Schedule at phase boundaries */
435
475
  World.prototype.swapEvents = function () {
436
476
  var e_10, _a;
477
+ this._ensureNotDestroyed();
437
478
  try {
438
479
  for (var _b = __values(this.eventChannels.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
439
480
  var ch = _c.value;
@@ -451,6 +492,7 @@ var World = /** @class */ (function (_super) {
451
492
  //#endregion
452
493
  //#region ---------- Entity lifecycle ----------
453
494
  World.prototype.spawn = function () {
495
+ this._ensureNotDestroyed();
454
496
  this._ensureNotIterating("spawn");
455
497
  var entity = this.entities.create();
456
498
  // place in archetype 0
@@ -461,7 +503,7 @@ var World = /** @class */ (function (_super) {
461
503
  entityMeta.row = row;
462
504
  return entity;
463
505
  };
464
- World.prototype.spawnMany = function () {
506
+ World.prototype.spawnWith = function () {
465
507
  var e_11, _a;
466
508
  var items = [];
467
509
  for (var _i = 0; _i < arguments.length; _i++) {
@@ -484,9 +526,11 @@ var World = /** @class */ (function (_super) {
484
526
  return e;
485
527
  };
486
528
  World.prototype.isAlive = function (e) {
529
+ this._ensureNotDestroyed();
487
530
  return this.entities.isAlive(e);
488
531
  };
489
532
  World.prototype.despawn = function (e) {
533
+ this._ensureNotDestroyed();
490
534
  this._assertAlive(e, 'despawn');
491
535
  this._ensureNotIterating("despawn");
492
536
  this._removeFromArchetype(e);
@@ -511,6 +555,7 @@ var World = /** @class */ (function (_super) {
511
555
  //#endregion
512
556
  //#region ---------- Components ----------
513
557
  World.prototype.has = function (e, ctor) {
558
+ this._ensureNotDestroyed();
514
559
  var meta = this.entities.meta[e.id];
515
560
  if (!meta || !meta.alive || meta.gen !== e.gen)
516
561
  return false;
@@ -518,6 +563,7 @@ var World = /** @class */ (function (_super) {
518
563
  return this.archetypes[meta.arch].has(tid);
519
564
  };
520
565
  World.prototype.get = function (e, ctor) {
566
+ this._ensureNotDestroyed();
521
567
  var meta = this.entities.meta[e.id];
522
568
  if (!meta || !meta.alive || meta.gen !== e.gen)
523
569
  return undefined;
@@ -528,6 +574,7 @@ var World = /** @class */ (function (_super) {
528
574
  return a.column(tid)[meta.row];
529
575
  };
530
576
  World.prototype.set = function (e, ctor, value) {
577
+ this._ensureNotDestroyed();
531
578
  var op = "set(".concat(this._formatCtor(ctor), ")");
532
579
  var meta = this._assertAlive(e, op);
533
580
  var tid = (0, TypeRegistry_1.typeId)(ctor);
@@ -537,6 +584,7 @@ var World = /** @class */ (function (_super) {
537
584
  a.column(tid)[meta.row] = value;
538
585
  };
539
586
  World.prototype.add = function (e, ctor, value) {
587
+ this._ensureNotDestroyed();
540
588
  var op = "add(".concat(this._formatCtor(ctor), ")");
541
589
  this._assertAlive(e, op);
542
590
  this._ensureNotIterating(op);
@@ -548,8 +596,11 @@ var World = /** @class */ (function (_super) {
548
596
  src.column(tid)[srcMeta.row] = value;
549
597
  return;
550
598
  }
551
- var dstSig = (0, Signature_1.mergeSignature)(src.sig, tid);
552
- 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
+ }
553
604
  this._moveEntity(e, src, srcMeta.row, dst, function (t) {
554
605
  if (t === tid)
555
606
  return value;
@@ -564,6 +615,7 @@ var World = /** @class */ (function (_super) {
564
615
  }
565
616
  if (items.length === 0)
566
617
  return;
618
+ this._ensureNotDestroyed();
567
619
  this._assertAlive(e, "addMany");
568
620
  this._ensureNotIterating("addMany");
569
621
  var srcMeta = this.entities.meta[e.id];
@@ -623,6 +675,7 @@ var World = /** @class */ (function (_super) {
623
675
  });
624
676
  };
625
677
  World.prototype.remove = function (e, ctor) {
678
+ this._ensureNotDestroyed();
626
679
  var op = "remove(".concat(this._formatCtor(ctor), ")");
627
680
  this._assertAlive(e, op);
628
681
  this._ensureNotIterating(op);
@@ -631,8 +684,11 @@ var World = /** @class */ (function (_super) {
631
684
  var src = this.archetypes[srcMeta.arch];
632
685
  if (!src.has(tid))
633
686
  return;
634
- var dstSig = (0, Signature_1.subtractSignature)(src.sig, tid);
635
- 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
+ }
636
692
  this._moveEntity(e, src, srcMeta.row, dst, function (t) {
637
693
  // copy all but removed, but dstSig guarantees t != tid
638
694
  return src.column(t)[srcMeta.row];
@@ -646,6 +702,7 @@ var World = /** @class */ (function (_super) {
646
702
  }
647
703
  if (ctors.length === 0)
648
704
  return;
705
+ this._ensureNotDestroyed();
649
706
  this._assertAlive(e, "removeMany");
650
707
  this._ensureNotIterating("removeMany");
651
708
  var srcMeta = this.entities.meta[e.id];
@@ -682,105 +739,205 @@ var World = /** @class */ (function (_super) {
682
739
  };
683
740
  World.prototype.query = function () {
684
741
  var _a;
685
- var ctors = [];
742
+ var args = [];
686
743
  for (var _i = 0; _i < arguments.length; _i++) {
687
- ctors[_i] = arguments[_i];
744
+ args[_i] = arguments[_i];
688
745
  }
689
- var _b = World._buildQueryTypeIds(ctors), requested = _b.requested, needSorted = _b.needSorted;
690
- function gen(world) {
691
- var _a, _b, a, cols, i, row, e, out, i, e_16_1;
692
- var e_16, _c;
693
- return __generator(this, function (_d) {
694
- 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) {
695
753
  case 0:
696
754
  world._iterateDepth++;
697
- _d.label = 1;
755
+ _c.label = 1;
698
756
  case 1:
699
- _d.trys.push([1, , 12, 13]);
700
- _d.label = 2;
757
+ _c.trys.push([1, , 44, 45]);
758
+ _c.label = 2;
701
759
  case 2:
702
- _d.trys.push([2, 9, 10, 11]);
703
- _a = __values(world.archetypes), _b = _a.next();
704
- _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;
705
763
  case 3:
706
- if (!!_b.done) return [3 /*break*/, 8];
707
- a = _b.value;
708
- if (!a)
709
- return [3 /*break*/, 7];
710
- if (!(0, Signature_1.signatureHasAll)(a.sig, needSorted))
711
- 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];
712
768
  cols = new Array(requested.length);
713
769
  for (i = 0; i < requested.length; i++)
714
770
  cols[i] = a.column(requested[i]);
715
- row = 0;
716
- _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];
717
783
  case 4:
718
- if (!(row < a.entities.length)) return [3 /*break*/, 7];
719
- e = a.entities[row];
720
- out = { e: e };
721
- for (i = 0; i < cols.length; i++)
722
- out["c".concat(i + 1)] = cols[i][row];
723
- return [4 /*yield*/, out];
784
+ a0 = cols[0];
785
+ r = 0;
786
+ _c.label = 5;
724
787
  case 5:
725
- _d.sent();
726
- _d.label = 6;
788
+ if (!(r < len)) return [3 /*break*/, 8];
789
+ return [4 /*yield*/, { e: ents[r], c1: a0[r] }];
727
790
  case 6:
728
- row++;
729
- return [3 /*break*/, 4];
791
+ _c.sent();
792
+ _c.label = 7;
730
793
  case 7:
731
- _b = _a.next();
732
- return [3 /*break*/, 3];
733
- case 8: return [3 /*break*/, 11];
794
+ r++;
795
+ return [3 /*break*/, 5];
796
+ case 8: return [3 /*break*/, 39];
734
797
  case 9:
735
- e_16_1 = _d.sent();
736
- e_16 = { error: e_16_1 };
737
- return [3 /*break*/, 11];
798
+ a0 = cols[0], a1 = cols[1];
799
+ r = 0;
800
+ _c.label = 10;
738
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:
739
892
  try {
740
- 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);
741
894
  }
742
895
  finally { if (e_16) throw e_16.error; }
743
896
  return [7 /*endfinally*/];
744
- case 11: return [3 /*break*/, 13];
745
- case 12:
897
+ case 43: return [3 /*break*/, 45];
898
+ case 44:
746
899
  world._iterateDepth--;
747
900
  return [7 /*endfinally*/];
748
- case 13: return [2 /*return*/];
901
+ case 45: return [2 /*return*/];
749
902
  }
750
903
  });
751
904
  }
752
905
  // eslint-disable-next-line @typescript-eslint/no-this-alias
753
906
  var self = this;
754
- return _a = {}, _a[Symbol.iterator] = function () { return gen(self); }, _a;
907
+ return _a = {},
908
+ _a[Symbol.iterator] = function () {
909
+ self._ensureNotDestroyed();
910
+ return gen(self, self._matchingArchetypes(needSorted));
911
+ },
912
+ _a;
755
913
  };
756
914
  World.prototype.queryTables = function () {
757
915
  var _a;
758
- var ctors = [];
916
+ var args = [];
759
917
  for (var _i = 0; _i < arguments.length; _i++) {
760
- ctors[_i] = arguments[_i];
918
+ args[_i] = arguments[_i];
761
919
  }
762
- var _b = World._buildQueryTypeIds(ctors), requested = _b.requested, needSorted = _b.needSorted;
763
- function gen(world) {
764
- var _a, _b, a, out, i, e_17_1;
765
- var e_17, _c;
766
- return __generator(this, function (_d) {
767
- 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) {
768
927
  case 0:
769
928
  world._iterateDepth++;
770
- _d.label = 1;
929
+ _b.label = 1;
771
930
  case 1:
772
- _d.trys.push([1, , 10, 11]);
773
- _d.label = 2;
931
+ _b.trys.push([1, , 10, 11]);
932
+ _b.label = 2;
774
933
  case 2:
775
- _d.trys.push([2, 7, 8, 9]);
776
- _a = __values(world.archetypes), _b = _a.next();
777
- _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;
778
937
  case 3:
779
- if (!!_b.done) return [3 /*break*/, 6];
780
- a = _b.value;
781
- if (!a)
782
- return [3 /*break*/, 5];
783
- 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))
784
941
  return [3 /*break*/, 5];
785
942
  out = { entities: a.entities };
786
943
  for (i = 0; i < requested.length; i++) {
@@ -788,19 +945,19 @@ var World = /** @class */ (function (_super) {
788
945
  }
789
946
  return [4 /*yield*/, out];
790
947
  case 4:
791
- _d.sent();
792
- _d.label = 5;
948
+ _b.sent();
949
+ _b.label = 5;
793
950
  case 5:
794
- _b = _a.next();
951
+ archs_2_1 = archs_2.next();
795
952
  return [3 /*break*/, 3];
796
953
  case 6: return [3 /*break*/, 9];
797
954
  case 7:
798
- e_17_1 = _d.sent();
955
+ e_17_1 = _b.sent();
799
956
  e_17 = { error: e_17_1 };
800
957
  return [3 /*break*/, 9];
801
958
  case 8:
802
959
  try {
803
- 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);
804
961
  }
805
962
  finally { if (e_17) throw e_17.error; }
806
963
  return [7 /*endfinally*/];
@@ -814,7 +971,12 @@ var World = /** @class */ (function (_super) {
814
971
  }
815
972
  // eslint-disable-next-line @typescript-eslint/no-this-alias
816
973
  var self = this;
817
- return _a = {}, _a[Symbol.iterator] = function () { return gen(self); }, _a;
974
+ return _a = {},
975
+ _a[Symbol.iterator] = function () {
976
+ self._ensureNotDestroyed();
977
+ return gen(self, self._matchingArchetypes(needSorted));
978
+ },
979
+ _a;
818
980
  };
819
981
  World.prototype.queryEach = function () {
820
982
  var e_18, _a;
@@ -822,56 +984,77 @@ var World = /** @class */ (function (_super) {
822
984
  for (var _i = 0; _i < arguments.length; _i++) {
823
985
  args[_i] = arguments[_i];
824
986
  }
987
+ this._ensureNotDestroyed();
825
988
  var fn = args[args.length - 1];
826
- var ctors = args.slice(0, args.length - 1);
827
- 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);
828
994
  this._iterateDepth++;
829
995
  try {
830
996
  try {
831
- for (var _c = __values(this.archetypes), _d = _c.next(); !_d.done; _d = _c.next()) {
832
- var a = _d.value;
833
- if (!a)
834
- continue;
835
- 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))
836
1000
  continue;
837
1001
  var cols = new Array(requested.length);
838
1002
  for (var i = 0; i < requested.length; i++)
839
1003
  cols[i] = a.column(requested[i]);
840
- var _loop_1 = function (row) {
841
- var e = a.entities[row];
842
- switch (cols.length) {
843
- case 1:
844
- fn(e, cols[0][row]);
845
- break;
846
- case 2:
847
- fn(e, cols[0][row], cols[1][row]);
848
- break;
849
- case 3:
850
- fn(e, cols[0][row], cols[1][row], cols[2][row]);
851
- break;
852
- case 4:
853
- fn(e, cols[0][row], cols[1][row], cols[2][row], cols[3][row]);
854
- break;
855
- case 5:
856
- fn(e, cols[0][row], cols[1][row], cols[2][row], cols[3][row], cols[4][row]);
857
- break;
858
- case 6:
859
- fn(e, cols[0][row], cols[1][row], cols[2][row], cols[3][row], cols[4][row], cols[5][row]);
860
- break;
861
- default:
862
- fn.apply(void 0, __spreadArray([e], __read(cols.map(function (c) { return c[row]; })), false));
863
- 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;
1030
+ }
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;
864
1036
  }
865
- };
866
- for (var row = 0; row < a.entities.length; row++) {
867
- _loop_1(row);
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;
868
1051
  }
869
1052
  }
870
1053
  }
871
1054
  catch (e_18_1) { e_18 = { error: e_18_1 }; }
872
1055
  finally {
873
1056
  try {
874
- 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);
875
1058
  }
876
1059
  finally { if (e_18) throw e_18.error; }
877
1060
  }
@@ -900,19 +1083,52 @@ var World = /** @class */ (function (_super) {
900
1083
  World.prototype._resetArchetypes = function () {
901
1084
  this.archetypes.length = 0;
902
1085
  this.archByKey.clear();
1086
+ this._queryCache.clear();
903
1087
  // Archetype 0: empty signature
904
1088
  var archetype0 = new Archetype_1.Archetype(0, []);
905
1089
  this.archetypes[0] = archetype0;
906
1090
  this.archByKey.set("", archetype0);
907
1091
  };
908
- 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;
909
1125
  // Preserve caller order for (c1,c2,c3,...) mapping.
910
1126
  var requested = new Array(ctors.length);
911
1127
  for (var i = 0; i < ctors.length; i++)
912
1128
  requested[i] = (0, TypeRegistry_1.typeId)(ctors[i]);
913
- // Same ids, but sorted + deduped for signatureHasAll().
914
- var needSorted = requested.slice();
915
- 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; });
916
1132
  var w = 0;
917
1133
  for (var i = 0; i < needSorted.length; i++) {
918
1134
  var v = needSorted[i];
@@ -920,7 +1136,13 @@ var World = /** @class */ (function (_super) {
920
1136
  needSorted[w++] = v;
921
1137
  }
922
1138
  needSorted.length = w;
923
- 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.");
924
1146
  };
925
1147
  World.prototype._ensureNotIterating = function (op) {
926
1148
  if (this._iterateDepth > 0) {
@@ -993,8 +1215,12 @@ var World = /** @class */ (function (_super) {
993
1215
  return this.despawn(op.e);
994
1216
  case "add":
995
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));
996
1220
  case "remove":
997
1221
  return this.remove(op.e, op.ctor);
1222
+ case "removeMany":
1223
+ return this.removeMany.apply(this, __spreadArray([op.e], __read(op.ctors), false));
998
1224
  }
999
1225
  };
1000
1226
  World.prototype._formatEntity = function (e) {