archetype-ecs-lib 0.5.0 → 0.6.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
@@ -1,4 +1,19 @@
1
1
  "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
2
17
  var __generator = (this && this.__generator) || function (thisArg, body) {
3
18
  var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
4
19
  return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
@@ -53,6 +68,15 @@ var __read = (this && this.__read) || function (o, n) {
53
68
  }
54
69
  return ar;
55
70
  };
71
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
72
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
73
+ if (ar || !(i in from)) {
74
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
75
+ ar[i] = from[i];
76
+ }
77
+ }
78
+ return to.concat(ar || Array.prototype.slice.call(from));
79
+ };
56
80
  Object.defineProperty(exports, "__esModule", { value: true });
57
81
  exports.World = void 0;
58
82
  var Archetype_1 = require("./Archetype");
@@ -61,21 +85,154 @@ var EntityManager_1 = require("./EntityManager");
61
85
  var Events_1 = require("./Events");
62
86
  var Signature_1 = require("./Signature");
63
87
  var TypeRegistry_1 = require("./TypeRegistry");
64
- var World = /** @class */ (function () {
65
- function World() {
66
- this.entities = new EntityManager_1.EntityManager();
67
- this.archetypes = [];
68
- this.archByKey = new Map();
69
- this.systems = [];
70
- this.commands = new Commands_1.Commands();
71
- this._iterateDepth = 0;
72
- this.resources = new Map();
73
- this.eventChannels = new Map();
74
- // Archetype 0: empty signature
75
- var archetype0 = new Archetype_1.Archetype(0, []);
76
- this.archetypes[0] = archetype0;
77
- this.archByKey.set("", archetype0);
88
+ var WorldSnapshotStore_1 = require("./WorldSnapshotStore");
89
+ var StatsOverlay_1 = require("./stats/StatsOverlay");
90
+ var World = /** @class */ (function (_super) {
91
+ __extends(World, _super);
92
+ function World(options) {
93
+ var _this = _super.call(this, options === null || options === void 0 ? void 0 : options.statsOverlayOptions) || this;
94
+ _this.entities = new EntityManager_1.EntityManager();
95
+ _this.archetypes = [];
96
+ _this.archByKey = new Map();
97
+ _this.systems = [];
98
+ _this.commands = new Commands_1.Commands();
99
+ _this._iterateDepth = 0;
100
+ _this.resources = new Map();
101
+ _this.eventChannels = new Map();
102
+ _this.snapshotStore = new WorldSnapshotStore_1.WorldSnapshotStore();
103
+ /** @internal Phase -> systems mapping for Schedule */
104
+ _this._scheduleSystems = new Map();
105
+ _this._resetArchetypes();
106
+ return _this;
78
107
  }
108
+ World.prototype.statsHistory = function () {
109
+ var e_1, _a, e_2, _b;
110
+ var phaseMs = Object.create(null);
111
+ try {
112
+ for (var _c = __values(this._histPhaseMs), _d = _c.next(); !_d.done; _d = _c.next()) {
113
+ var _e = __read(_d.value, 2), k = _e[0], v = _e[1];
114
+ phaseMs[k] = v;
115
+ }
116
+ }
117
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
118
+ finally {
119
+ try {
120
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
121
+ }
122
+ finally { if (e_1) throw e_1.error; }
123
+ }
124
+ var systemMs = Object.create(null);
125
+ try {
126
+ for (var _f = __values(this._histSystemMs), _g = _f.next(); !_g.done; _g = _f.next()) {
127
+ var _h = __read(_g.value, 2), k = _h[0], v = _h[1];
128
+ systemMs[k] = v;
129
+ }
130
+ }
131
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
132
+ finally {
133
+ try {
134
+ if (_g && !_g.done && (_b = _f.return)) _b.call(_f);
135
+ }
136
+ finally { if (e_2) throw e_2.error; }
137
+ }
138
+ return {
139
+ capacity: this._historyCapacity,
140
+ size: this._histFrameMs.length,
141
+ dt: this._histDt,
142
+ frameMs: this._histFrameMs,
143
+ phaseMs: phaseMs,
144
+ systemMs: systemMs
145
+ };
146
+ };
147
+ /**
148
+ * Rich runtime statistics (counts + last-frame timings).
149
+ * Note: `aliveEntities` is computed on demand (O(n) over entity meta).
150
+ */
151
+ World.prototype.stats = function () {
152
+ var e_3, _a, e_4, _b, e_5, _c, e_6, _d;
153
+ var alive = 0;
154
+ for (var id = 1; id < this.entities.meta.length; id++) {
155
+ var m = this.entities.meta[id];
156
+ if (m && m.alive)
157
+ alive++;
158
+ }
159
+ var archCount = 0;
160
+ var rows = 0;
161
+ try {
162
+ for (var _e = __values(this.archetypes), _f = _e.next(); !_f.done; _f = _e.next()) {
163
+ var a = _f.value;
164
+ if (!a)
165
+ continue;
166
+ archCount++;
167
+ rows += a.entities.length;
168
+ }
169
+ }
170
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
171
+ finally {
172
+ try {
173
+ if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
174
+ }
175
+ finally { if (e_3) throw e_3.error; }
176
+ }
177
+ var phaseMs = Object.create(null);
178
+ try {
179
+ for (var _g = __values(this._phaseMs), _h = _g.next(); !_h.done; _h = _g.next()) {
180
+ var _j = __read(_h.value, 2), k = _j[0], v = _j[1];
181
+ phaseMs[k] = v;
182
+ }
183
+ }
184
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
185
+ finally {
186
+ try {
187
+ if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
188
+ }
189
+ finally { if (e_4) throw e_4.error; }
190
+ }
191
+ var systemMs = Object.create(null);
192
+ try {
193
+ for (var _k = __values(this._systemMs), _l = _k.next(); !_l.done; _l = _k.next()) {
194
+ var _m = __read(_l.value, 2), k = _m[0], v = _m[1];
195
+ systemMs[k] = v;
196
+ }
197
+ }
198
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
199
+ finally {
200
+ try {
201
+ if (_l && !_l.done && (_c = _k.return)) _c.call(_k);
202
+ }
203
+ finally { if (e_5) throw e_5.error; }
204
+ }
205
+ var systemCount = this.systems.length;
206
+ if (this._scheduleSystems.size > 0) {
207
+ try {
208
+ for (var _o = __values(this._scheduleSystems.values()), _p = _o.next(); !_p.done; _p = _o.next()) {
209
+ var list = _p.value;
210
+ systemCount += list.length;
211
+ }
212
+ }
213
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
214
+ finally {
215
+ try {
216
+ if (_p && !_p.done && (_d = _o.return)) _d.call(_o);
217
+ }
218
+ finally { if (e_6) throw e_6.error; }
219
+ }
220
+ }
221
+ return {
222
+ aliveEntities: alive,
223
+ archetypes: archCount,
224
+ rows: rows,
225
+ systems: systemCount,
226
+ resources: this.resources.size,
227
+ eventChannels: this.eventChannels.size,
228
+ pendingCommands: this.commands.hasPending(),
229
+ frame: this._frameCounter,
230
+ dt: this._lastDt,
231
+ frameMs: this._lastFrameMs,
232
+ phaseMs: phaseMs,
233
+ systemMs: systemMs
234
+ };
235
+ };
79
236
  /** Queue structural changes to apply safely after systems run. */
80
237
  World.prototype.cmd = function () {
81
238
  return this.commands;
@@ -85,57 +242,114 @@ var World = /** @class */ (function () {
85
242
  return this;
86
243
  };
87
244
  /**
88
- * Run a frame:
89
- * - run systems in order
90
- * - flush queued commands (structural changes)
245
+ * Simple single-phase update.
246
+ * Runs all systems added via `addSystem()`, flushes commands, and swaps events once.
247
+ *
248
+ * This is the recommended approach for:
249
+ * - Simple applications with basic game loops
250
+ * - Single-phase system execution
251
+ * - Rapid prototyping
252
+ *
253
+ * @example
254
+ * ```TypeScript
255
+ * // Simple game loop
256
+ * function gameLoop(dt: number) {
257
+ * world.update(dt);
258
+ * }
259
+ * ```
260
+ *
261
+ * @note If you are using `Schedule` for multiphase updates, do NOT use this method.
262
+ * Use `schedule.run(world, dt, phases)` instead.
263
+ *
264
+ * @throws {Error} If both World.update() and Schedule.run() are used on the same World instance
91
265
  */
92
266
  World.prototype.update = function (dt) {
93
- var e_1, _a;
267
+ var e_7, _a;
268
+ // Runtime conflict detection
269
+ if (this._scheduleSystems.size > 0) {
270
+ this._warnAboutLifecycleConflict("World.update");
271
+ }
272
+ var frameStart = this._profBeginFrame(dt);
94
273
  this._iterateDepth++;
95
274
  try {
96
275
  try {
97
276
  for (var _b = __values(this.systems), _c = _b.next(); !_c.done; _c = _b.next()) {
98
277
  var s = _c.value;
99
- s(this, dt);
278
+ if (this._profilingEnabled) {
279
+ var t0 = performance.now();
280
+ s(this, dt);
281
+ var name_1 = s.name && s.name.length > 0 ? s.name : "<anonymous>";
282
+ this._profAddSystem(name_1, performance.now() - t0);
283
+ }
284
+ else {
285
+ s(this, dt);
286
+ }
100
287
  }
101
288
  }
102
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
289
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
103
290
  finally {
104
291
  try {
105
292
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
106
293
  }
107
- finally { if (e_1) throw e_1.error; }
294
+ finally { if (e_7) throw e_7.error; }
108
295
  }
109
296
  }
110
297
  finally {
111
298
  this._iterateDepth--;
112
299
  this.flush();
300
+ this.swapEvents();
301
+ this._profAddPhase("update", this._profilingEnabled ? (performance.now() - frameStart) : 0);
302
+ this._profEndFrame(frameStart);
113
303
  }
304
+ this.updateOverlay(this.stats(), this.statsHistory());
114
305
  };
115
306
  World.prototype.flush = function () {
116
- var e_2, _a;
307
+ var e_8, _a;
117
308
  this._ensureNotIterating("flush");
118
- // Apply commands until queue is empty. This allows spawn(init) to enqueue add/remove
309
+ // Apply commands until the queue is empty. This allows spawn(init) to enqueue add/remove
119
310
  // operations that will be applied during the same flush.
120
311
  while (true) {
121
312
  var ops = this.commands.drain();
122
313
  if (ops.length === 0)
123
314
  break;
124
315
  try {
125
- for (var ops_1 = (e_2 = void 0, __values(ops)), ops_1_1 = ops_1.next(); !ops_1_1.done; ops_1_1 = ops_1.next()) {
316
+ for (var ops_1 = (e_8 = void 0, __values(ops)), ops_1_1 = ops_1.next(); !ops_1_1.done; ops_1_1 = ops_1.next()) {
126
317
  var op = ops_1_1.value;
127
318
  this._apply(op);
128
319
  }
129
320
  }
130
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
321
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
131
322
  finally {
132
323
  try {
133
324
  if (ops_1_1 && !ops_1_1.done && (_a = ops_1.return)) _a.call(ops_1);
134
325
  }
135
- finally { if (e_2) throw e_2.error; }
326
+ finally { if (e_8) throw e_8.error; }
136
327
  }
137
328
  }
138
329
  };
330
+ //#region ---------- Snapshot / Restore ----------
331
+ World.prototype.registerComponentSnapshot = function (key, codec) {
332
+ this.snapshotStore.registerComponentSnapshot(this._snapshotRuntime(), key, codec);
333
+ return this;
334
+ };
335
+ World.prototype.unregisterComponentSnapshot = function (key) {
336
+ return this.snapshotStore.unregisterComponentSnapshot(key);
337
+ };
338
+ World.prototype.registerResourceSnapshot = function (key, codec) {
339
+ this.snapshotStore.registerResourceSnapshot(this._snapshotRuntime(), key, codec);
340
+ return this;
341
+ };
342
+ World.prototype.unregisterResourceSnapshot = function (key) {
343
+ return this.snapshotStore.unregisterResourceSnapshot(key);
344
+ };
345
+ World.prototype.snapshot = function () {
346
+ return this.snapshotStore.snapshot(this._snapshotRuntime());
347
+ };
348
+ World.prototype.restore = function (snapshot) {
349
+ this.snapshotStore.restore(this._snapshotRuntime(), snapshot);
350
+ this.updateOverlay(this.stats(), this.statsHistory());
351
+ };
352
+ //#endregion
139
353
  //#region ---------- Resources (singletons) ----------
140
354
  World.prototype.setResource = function (key, value) {
141
355
  this.resources.set(key, value);
@@ -147,9 +361,9 @@ var World = /** @class */ (function () {
147
361
  };
148
362
  World.prototype.requireResource = function (key) {
149
363
  if (!this.resources.has(key)) {
150
- var name = this._formatCtor(key);
151
- throw new Error("Missing resource ".concat(name, ". ") +
152
- "Insert it via world.setResource(".concat(name, ", value) or world.initResource(").concat(name, ", () => value)."));
364
+ var name_2 = this._formatCtor(key);
365
+ throw new Error("requireResource(".concat(name_2, ") failed: resource missing. ") +
366
+ "Insert it via world.setResource(".concat(name_2, ", value) or world.initResource(").concat(name_2, ", () => value)."));
153
367
  }
154
368
  return this.resources.get(key);
155
369
  };
@@ -181,7 +395,7 @@ var World = /** @class */ (function () {
181
395
  ch.drain(fn);
182
396
  };
183
397
  World.prototype.clearEvents = function (key) {
184
- var e_3, _a;
398
+ var e_9, _a;
185
399
  if (key) {
186
400
  var ch = this.eventChannels.get(key);
187
401
  if (!ch)
@@ -196,34 +410,35 @@ var World = /** @class */ (function () {
196
410
  ch.clear();
197
411
  }
198
412
  }
199
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
413
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
200
414
  finally {
201
415
  try {
202
416
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
203
417
  }
204
- finally { if (e_3) throw e_3.error; }
418
+ finally { if (e_9) throw e_9.error; }
205
419
  }
206
420
  };
207
421
  /** @internal Called by Schedule at phase boundaries */
208
422
  World.prototype.swapEvents = function () {
209
- var e_4, _a;
423
+ var e_10, _a;
210
424
  try {
211
425
  for (var _b = __values(this.eventChannels.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
212
426
  var ch = _c.value;
213
427
  ch.swapBuffers();
214
428
  }
215
429
  }
216
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
430
+ catch (e_10_1) { e_10 = { error: e_10_1 }; }
217
431
  finally {
218
432
  try {
219
433
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
220
434
  }
221
- finally { if (e_4) throw e_4.error; }
435
+ finally { if (e_10) throw e_10.error; }
222
436
  }
223
437
  };
224
438
  //#endregion
225
439
  //#region ---------- Entity lifecycle ----------
226
440
  World.prototype.spawn = function () {
441
+ this._ensureNotIterating("spawn");
227
442
  var entity = this.entities.create();
228
443
  // place in archetype 0
229
444
  var archetype0 = this.archetypes[0];
@@ -234,7 +449,7 @@ var World = /** @class */ (function () {
234
449
  return entity;
235
450
  };
236
451
  World.prototype.spawnMany = function () {
237
- var e_5, _a;
452
+ var e_11, _a;
238
453
  var items = [];
239
454
  for (var _i = 0; _i < arguments.length; _i++) {
240
455
  items[_i] = arguments[_i];
@@ -246,12 +461,12 @@ var World = /** @class */ (function () {
246
461
  this.add(e, ctor, value);
247
462
  }
248
463
  }
249
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
464
+ catch (e_11_1) { e_11 = { error: e_11_1 }; }
250
465
  finally {
251
466
  try {
252
467
  if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
253
468
  }
254
- finally { if (e_5) throw e_5.error; }
469
+ finally { if (e_11) throw e_11.error; }
255
470
  }
256
471
  return e;
257
472
  };
@@ -265,19 +480,19 @@ var World = /** @class */ (function () {
265
480
  this.entities.kill(e);
266
481
  };
267
482
  World.prototype.despawnMany = function (entities) {
268
- var e_6, _a;
483
+ var e_12, _a;
269
484
  try {
270
485
  for (var entities_1 = __values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) {
271
486
  var e = entities_1_1.value;
272
487
  this.despawn(e);
273
488
  }
274
489
  }
275
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
490
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
276
491
  finally {
277
492
  try {
278
493
  if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
279
494
  }
280
- finally { if (e_6) throw e_6.error; }
495
+ finally { if (e_12) throw e_12.error; }
281
496
  }
282
497
  };
283
498
  //#endregion
@@ -300,7 +515,8 @@ var World = /** @class */ (function () {
300
515
  return a.column(tid)[meta.row];
301
516
  };
302
517
  World.prototype.set = function (e, ctor, value) {
303
- var meta = this._assertAlive(e, "set(".concat(this._formatCtor(ctor), ")"));
518
+ var op = "set(".concat(this._formatCtor(ctor), ")");
519
+ var meta = this._assertAlive(e, op);
304
520
  var tid = (0, TypeRegistry_1.typeId)(ctor);
305
521
  var a = this.archetypes[meta.arch];
306
522
  if (!a.has(tid))
@@ -308,8 +524,9 @@ var World = /** @class */ (function () {
308
524
  a.column(tid)[meta.row] = value;
309
525
  };
310
526
  World.prototype.add = function (e, ctor, value) {
311
- this._assertAlive(e, "add(".concat(this._formatCtor(ctor), ")"));
312
- this._ensureNotIterating("add");
527
+ var op = "add(".concat(this._formatCtor(ctor), ")");
528
+ this._assertAlive(e, op);
529
+ this._ensureNotIterating(op);
313
530
  var tid = (0, TypeRegistry_1.typeId)(ctor);
314
531
  var srcMeta = this.entities.meta[e.id];
315
532
  var src = this.archetypes[srcMeta.arch];
@@ -327,28 +544,75 @@ var World = /** @class */ (function () {
327
544
  });
328
545
  };
329
546
  World.prototype.addMany = function (e) {
330
- var e_7, _a;
547
+ var e_13, _a, e_14, _b;
331
548
  var items = [];
332
549
  for (var _i = 1; _i < arguments.length; _i++) {
333
550
  items[_i - 1] = arguments[_i];
334
551
  }
552
+ if (items.length === 0)
553
+ return;
554
+ this._assertAlive(e, "addMany");
555
+ this._ensureNotIterating("addMany");
556
+ var srcMeta = this.entities.meta[e.id];
557
+ var src = this.archetypes[srcMeta.arch];
558
+ // Build component map: TypeId -> value
559
+ var newComps = new Map();
335
560
  try {
336
561
  for (var items_2 = __values(items), items_2_1 = items_2.next(); !items_2_1.done; items_2_1 = items_2.next()) {
337
- var _b = __read(items_2_1.value, 2), ctor = _b[0], value = _b[1];
338
- this.add(e, ctor, value);
562
+ var _c = __read(items_2_1.value, 2), ctor = _c[0], value = _c[1];
563
+ var tid = (0, TypeRegistry_1.typeId)(ctor);
564
+ newComps.set(tid, value);
339
565
  }
340
566
  }
341
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
567
+ catch (e_13_1) { e_13 = { error: e_13_1 }; }
342
568
  finally {
343
569
  try {
344
570
  if (items_2_1 && !items_2_1.done && (_a = items_2.return)) _a.call(items_2);
345
571
  }
346
- finally { if (e_7) throw e_7.error; }
572
+ finally { if (e_13) throw e_13.error; }
573
+ }
574
+ // Compute final signature: src.sig + new components
575
+ var dstSig = src.sig.slice();
576
+ try {
577
+ for (var _d = __values(newComps.keys()), _e = _d.next(); !_e.done; _e = _d.next()) {
578
+ var tid = _e.value;
579
+ if (!src.has(tid)) {
580
+ // Insert in sorted order
581
+ var i = 0;
582
+ while (i < dstSig.length && dstSig[i] < tid)
583
+ i++;
584
+ if (dstSig[i] !== tid) {
585
+ dstSig.splice(i, 0, tid);
586
+ }
587
+ }
588
+ else {
589
+ // Component already exists, update in-place
590
+ src.column(tid)[srcMeta.row] = newComps.get(tid);
591
+ newComps.delete(tid);
592
+ }
593
+ }
347
594
  }
595
+ catch (e_14_1) { e_14 = { error: e_14_1 }; }
596
+ finally {
597
+ try {
598
+ if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
599
+ }
600
+ finally { if (e_14) throw e_14.error; }
601
+ }
602
+ // If all components were in-place updates, no move needed
603
+ if (newComps.size === 0)
604
+ return;
605
+ // Single move to final archetype
606
+ var dst = this._getOrCreateArchetype(dstSig);
607
+ this._moveEntity(e, src, srcMeta.row, dst, function (t) {
608
+ // Use new value if adding this component, otherwise copy from src
609
+ return newComps.has(t) ? newComps.get(t) : src.column(t)[srcMeta.row];
610
+ });
348
611
  };
349
612
  World.prototype.remove = function (e, ctor) {
350
- this._assertAlive(e, "remove(".concat(this._formatCtor(ctor), ")"));
351
- this._ensureNotIterating("remove");
613
+ var op = "remove(".concat(this._formatCtor(ctor), ")");
614
+ this._assertAlive(e, op);
615
+ this._ensureNotIterating(op);
352
616
  var tid = (0, TypeRegistry_1.typeId)(ctor);
353
617
  var srcMeta = this.entities.meta[e.id];
354
618
  var src = this.archetypes[srcMeta.arch];
@@ -362,47 +626,56 @@ var World = /** @class */ (function () {
362
626
  });
363
627
  };
364
628
  World.prototype.removeMany = function (e) {
365
- var e_8, _a;
629
+ var e_15, _a;
366
630
  var ctors = [];
367
631
  for (var _i = 1; _i < arguments.length; _i++) {
368
632
  ctors[_i - 1] = arguments[_i];
369
633
  }
634
+ if (ctors.length === 0)
635
+ return;
636
+ this._assertAlive(e, "removeMany");
637
+ this._ensureNotIterating("removeMany");
638
+ var srcMeta = this.entities.meta[e.id];
639
+ var src = this.archetypes[srcMeta.arch];
640
+ // Collect TypeIds to remove
641
+ var toRemove = new Set();
370
642
  try {
371
643
  for (var ctors_1 = __values(ctors), ctors_1_1 = ctors_1.next(); !ctors_1_1.done; ctors_1_1 = ctors_1.next()) {
372
644
  var ctor = ctors_1_1.value;
373
- this.remove(e, ctor);
645
+ var tid = (0, TypeRegistry_1.typeId)(ctor);
646
+ if (src.has(tid)) {
647
+ toRemove.add(tid);
648
+ }
374
649
  }
375
650
  }
376
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
651
+ catch (e_15_1) { e_15 = { error: e_15_1 }; }
377
652
  finally {
378
653
  try {
379
654
  if (ctors_1_1 && !ctors_1_1.done && (_a = ctors_1.return)) _a.call(ctors_1);
380
655
  }
381
- finally { if (e_8) throw e_8.error; }
656
+ finally { if (e_15) throw e_15.error; }
382
657
  }
658
+ // If nothing to remove, no-op
659
+ if (toRemove.size === 0)
660
+ return;
661
+ // Compute final signature: src.sig - removed components
662
+ var dstSig = src.sig.filter(function (tid) { return !toRemove.has(tid); });
663
+ // Single move to final archetype
664
+ var dst = this._getOrCreateArchetype(dstSig);
665
+ this._moveEntity(e, src, srcMeta.row, dst, function (t) {
666
+ // Copy all components except removed ones (dstSig guarantees t is not removed)
667
+ return src.column(t)[srcMeta.row];
668
+ });
383
669
  };
384
670
  World.prototype.query = function () {
385
671
  var ctors = [];
386
672
  for (var _i = 0; _i < arguments.length; _i++) {
387
673
  ctors[_i] = arguments[_i];
388
674
  }
389
- // Preserve caller order for (c1,c2,c3,...) mapping.
390
- var requested = new Array(ctors.length);
391
- for (var i = 0; i < ctors.length; i++)
392
- requested[i] = (0, TypeRegistry_1.typeId)(ctors[i]);
393
- // Same ids, but sorted + deduped for signatureHasAll().
394
- var needSorted = requested.slice();
395
- needSorted.sort(function (a, b) { return a - b; });
396
- var w = 0;
397
- for (var i = 0; i < needSorted.length; i++) {
398
- var v = needSorted[i];
399
- if (i === 0 || v !== needSorted[w - 1])
400
- needSorted[w++] = v;
401
- }
402
- needSorted.length = w;
675
+ var _a = World._buildQueryTypeIds(ctors), requested = _a.requested, needSorted = _a.needSorted;
403
676
  function gen(world) {
404
- var _a, _b, a, cols, i, row, e, out, i, e_9_1;
405
- var e_9, _c;
677
+ var _a, _b, a, cols, i, row, e, out, i, e_16_1;
678
+ var e_16, _c;
406
679
  return __generator(this, function (_d) {
407
680
  switch (_d.label) {
408
681
  case 0:
@@ -445,14 +718,14 @@ var World = /** @class */ (function () {
445
718
  return [3 /*break*/, 3];
446
719
  case 8: return [3 /*break*/, 11];
447
720
  case 9:
448
- e_9_1 = _d.sent();
449
- e_9 = { error: e_9_1 };
721
+ e_16_1 = _d.sent();
722
+ e_16 = { error: e_16_1 };
450
723
  return [3 /*break*/, 11];
451
724
  case 10:
452
725
  try {
453
726
  if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
454
727
  }
455
- finally { if (e_9) throw e_9.error; }
728
+ finally { if (e_16) throw e_16.error; }
456
729
  return [7 /*endfinally*/];
457
730
  case 11: return [3 /*break*/, 13];
458
731
  case 12:
@@ -464,8 +737,172 @@ var World = /** @class */ (function () {
464
737
  }
465
738
  return gen(this);
466
739
  };
740
+ World.prototype.queryTables = function () {
741
+ var ctors = [];
742
+ for (var _i = 0; _i < arguments.length; _i++) {
743
+ ctors[_i] = arguments[_i];
744
+ }
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) {
751
+ case 0:
752
+ world._iterateDepth++;
753
+ _d.label = 1;
754
+ case 1:
755
+ _d.trys.push([1, , 10, 11]);
756
+ _d.label = 2;
757
+ case 2:
758
+ _d.trys.push([2, 7, 8, 9]);
759
+ _a = __values(world.archetypes), _b = _a.next();
760
+ _d.label = 3;
761
+ 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))
767
+ return [3 /*break*/, 5];
768
+ out = { entities: a.entities };
769
+ for (i = 0; i < requested.length; i++) {
770
+ out["c".concat(i + 1)] = a.column(requested[i]);
771
+ }
772
+ return [4 /*yield*/, out];
773
+ case 4:
774
+ _d.sent();
775
+ _d.label = 5;
776
+ case 5:
777
+ _b = _a.next();
778
+ return [3 /*break*/, 3];
779
+ case 6: return [3 /*break*/, 9];
780
+ case 7:
781
+ e_17_1 = _d.sent();
782
+ e_17 = { error: e_17_1 };
783
+ return [3 /*break*/, 9];
784
+ case 8:
785
+ try {
786
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
787
+ }
788
+ finally { if (e_17) throw e_17.error; }
789
+ return [7 /*endfinally*/];
790
+ case 9: return [3 /*break*/, 11];
791
+ case 10:
792
+ world._iterateDepth--;
793
+ return [7 /*endfinally*/];
794
+ case 11: return [2 /*return*/];
795
+ }
796
+ });
797
+ }
798
+ return gen(this);
799
+ };
800
+ World.prototype.queryEach = function () {
801
+ var e_18, _a;
802
+ var args = [];
803
+ for (var _i = 0; _i < arguments.length; _i++) {
804
+ args[_i] = arguments[_i];
805
+ }
806
+ 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;
809
+ this._iterateDepth++;
810
+ try {
811
+ 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))
817
+ continue;
818
+ var cols = new Array(requested.length);
819
+ for (var i = 0; i < requested.length; i++)
820
+ 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;
845
+ }
846
+ };
847
+ for (var row = 0; row < a.entities.length; row++) {
848
+ _loop_1(row);
849
+ }
850
+ }
851
+ }
852
+ catch (e_18_1) { e_18 = { error: e_18_1 }; }
853
+ finally {
854
+ try {
855
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
856
+ }
857
+ finally { if (e_18) throw e_18.error; }
858
+ }
859
+ }
860
+ finally {
861
+ this._iterateDepth--;
862
+ }
863
+ };
467
864
  //#endregion
468
865
  //#region ---------- Internals ----------
866
+ World.prototype._snapshotRuntime = function () {
867
+ var _this = this;
868
+ return {
869
+ ensureNotIterating: function (op) { return _this._ensureNotIterating(op); },
870
+ formatCtor: function (ctor) { return _this._formatCtor(ctor); },
871
+ flush: function () { return _this.flush(); },
872
+ resetArchetypes: function () { return _this._resetArchetypes(); },
873
+ getOrCreateArchetype: function (sig) { return _this._getOrCreateArchetype(sig); },
874
+ commands: this.commands,
875
+ entities: this.entities,
876
+ archetypes: this.archetypes,
877
+ resources: this.resources,
878
+ eventChannels: this.eventChannels
879
+ };
880
+ };
881
+ World.prototype._resetArchetypes = function () {
882
+ this.archetypes.length = 0;
883
+ this.archByKey.clear();
884
+ // Archetype 0: empty signature
885
+ var archetype0 = new Archetype_1.Archetype(0, []);
886
+ this.archetypes[0] = archetype0;
887
+ this.archByKey.set("", archetype0);
888
+ };
889
+ World._buildQueryTypeIds = function (ctors) {
890
+ // Preserve caller order for (c1,c2,c3,...) mapping.
891
+ var requested = new Array(ctors.length);
892
+ for (var i = 0; i < ctors.length; i++)
893
+ 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; });
897
+ var w = 0;
898
+ for (var i = 0; i < needSorted.length; i++) {
899
+ var v = needSorted[i];
900
+ if (i === 0 || v !== needSorted[w - 1])
901
+ needSorted[w++] = v;
902
+ }
903
+ needSorted.length = w;
904
+ return { requested: requested, needSorted: needSorted };
905
+ };
469
906
  World.prototype._ensureNotIterating = function (op) {
470
907
  if (this._iterateDepth > 0) {
471
908
  throw new Error("Cannot do structural change (".concat(op, ") while iterating. Use world.cmd() and flush at end of frame."));
@@ -497,7 +934,7 @@ var World = /** @class */ (function () {
497
934
  * Then swap-remove from src.
498
935
  */
499
936
  World.prototype._moveEntity = function (e, src, srcRow, dst, pick) {
500
- var e_10, _a;
937
+ var e_19, _a;
501
938
  // add row in dst
502
939
  var dstRow = dst.addRow(e);
503
940
  try {
@@ -506,12 +943,12 @@ var World = /** @class */ (function () {
506
943
  dst.column(t).push(pick(t));
507
944
  }
508
945
  }
509
- catch (e_10_1) { e_10 = { error: e_10_1 }; }
946
+ catch (e_19_1) { e_19 = { error: e_19_1 }; }
510
947
  finally {
511
948
  try {
512
949
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
513
950
  }
514
- finally { if (e_10) throw e_10.error; }
951
+ finally { if (e_19) throw e_19.error; }
515
952
  }
516
953
  // update meta to dst before removing from src (in case src==dst should never happen here)
517
954
  var m = this.entities.meta[e.id];
@@ -552,10 +989,10 @@ var World = /** @class */ (function () {
552
989
  * Throws an error if the entity is not alive
553
990
  */
554
991
  World.prototype._assertAlive = function (e, op) {
555
- var _a, _b;
556
992
  var meta = this.entities.meta[e.id];
557
993
  if (!this.entities.isAlive(e)) {
558
- throw new Error("".concat(op, " on stale entity ").concat(this._formatEntity(e), " (alive=").concat((_a = meta === null || meta === void 0 ? void 0 : meta.alive) !== null && _a !== void 0 ? _a : false, ", gen=").concat((_b = meta === null || meta === void 0 ? void 0 : meta.gen) !== null && _b !== void 0 ? _b : "n/a", ")"));
994
+ var status_1 = meta ? "alive=".concat(meta.alive, ", gen=").concat(meta.gen) : "not found";
995
+ throw new Error("".concat(op, " failed: stale entity ").concat(this._formatEntity(e), " (").concat(status_1, ")"));
559
996
  }
560
997
  return meta;
561
998
  };
@@ -567,6 +1004,30 @@ var World = /** @class */ (function () {
567
1004
  }
568
1005
  return ch;
569
1006
  };
1007
+ /**
1008
+ * @internal Warns about lifecycle method conflicts in development mode
1009
+ */
1010
+ World.prototype._warnAboutLifecycleConflict = function (method) {
1011
+ var hint = method === "World.update"
1012
+ ? "You have systems registered via schedule.add() but are calling world.update()."
1013
+ : "You have systems registered via world.addSystem() but are calling schedule.run().";
1014
+ throw new Error("\u26A0\uFE0F ECS Lifecycle Conflict Detected!\n" +
1015
+ "".concat(hint, "\n") +
1016
+ "This can cause:\n" +
1017
+ "- Double command flushes\n" +
1018
+ "- Confusing event visibility\n" +
1019
+ "- Unclear lifecycle semantics\n\n" +
1020
+ "Recommended fix:\n" +
1021
+ "- Use World.update() for simple single-phase applications (with world.addSystem())\n" +
1022
+ "- Use Schedule.run() for multi-phase applications (with schedule.add())\n\n" +
1023
+ "Choose ONE approach and stick with it.");
1024
+ };
1025
+ /**
1026
+ * @internal Returns the number of systems registered via addSystem()
1027
+ */
1028
+ World.prototype._getSystemCount = function () {
1029
+ return this.systems.length;
1030
+ };
570
1031
  return World;
571
- }());
1032
+ }(StatsOverlay_1.StatsOverlay));
572
1033
  exports.World = World;