archetype-ecs-lib 0.5.0 → 0.6.1

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,127 @@ 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++;
274
+ var caughtError;
95
275
  try {
96
276
  try {
97
277
  for (var _b = __values(this.systems), _c = _b.next(); !_c.done; _c = _b.next()) {
98
278
  var s = _c.value;
99
- s(this, dt);
279
+ if (this._profilingEnabled) {
280
+ var t0 = performance.now();
281
+ s(this, dt);
282
+ var name_1 = s.name && s.name.length > 0 ? s.name : "<anonymous>";
283
+ this._profAddSystem(name_1, performance.now() - t0);
284
+ }
285
+ else {
286
+ s(this, dt);
287
+ }
100
288
  }
101
289
  }
102
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
290
+ catch (e_7_1) { e_7 = { error: e_7_1 }; }
103
291
  finally {
104
292
  try {
105
293
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
106
294
  }
107
- finally { if (e_1) throw e_1.error; }
295
+ finally { if (e_7) throw e_7.error; }
108
296
  }
109
297
  }
298
+ catch (e) {
299
+ caughtError = e;
300
+ }
110
301
  finally {
111
302
  this._iterateDepth--;
112
- this.flush();
303
+ try {
304
+ this.flush();
305
+ }
306
+ catch (e) {
307
+ // System error takes priority; flush error only surfaced when no system threw.
308
+ if (caughtError === undefined)
309
+ caughtError = e;
310
+ }
311
+ this.swapEvents();
312
+ this._profAddPhase("update", this._profilingEnabled ? (performance.now() - frameStart) : 0);
313
+ this._profEndFrame(frameStart);
113
314
  }
315
+ if (caughtError !== undefined)
316
+ throw caughtError;
317
+ this.updateOverlay(this.stats(), this.statsHistory());
114
318
  };
115
319
  World.prototype.flush = function () {
116
- var e_2, _a;
320
+ var e_8, _a;
117
321
  this._ensureNotIterating("flush");
118
- // Apply commands until queue is empty. This allows spawn(init) to enqueue add/remove
322
+ // Apply commands until the queue is empty. This allows spawn(init) to enqueue add/remove
119
323
  // operations that will be applied during the same flush.
120
324
  while (true) {
121
325
  var ops = this.commands.drain();
122
326
  if (ops.length === 0)
123
327
  break;
124
328
  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()) {
329
+ 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
330
  var op = ops_1_1.value;
127
331
  this._apply(op);
128
332
  }
129
333
  }
130
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
334
+ catch (e_8_1) { e_8 = { error: e_8_1 }; }
131
335
  finally {
132
336
  try {
133
337
  if (ops_1_1 && !ops_1_1.done && (_a = ops_1.return)) _a.call(ops_1);
134
338
  }
135
- finally { if (e_2) throw e_2.error; }
339
+ finally { if (e_8) throw e_8.error; }
136
340
  }
137
341
  }
138
342
  };
343
+ //#region ---------- Snapshot / Restore ----------
344
+ World.prototype.registerComponentSnapshot = function (key, codec) {
345
+ this.snapshotStore.registerComponentSnapshot(this._snapshotRuntime(), key, codec);
346
+ return this;
347
+ };
348
+ World.prototype.unregisterComponentSnapshot = function (key) {
349
+ return this.snapshotStore.unregisterComponentSnapshot(key);
350
+ };
351
+ World.prototype.registerResourceSnapshot = function (key, codec) {
352
+ this.snapshotStore.registerResourceSnapshot(this._snapshotRuntime(), key, codec);
353
+ return this;
354
+ };
355
+ World.prototype.unregisterResourceSnapshot = function (key) {
356
+ return this.snapshotStore.unregisterResourceSnapshot(key);
357
+ };
358
+ World.prototype.snapshot = function () {
359
+ return this.snapshotStore.snapshot(this._snapshotRuntime());
360
+ };
361
+ World.prototype.restore = function (snapshot) {
362
+ this.snapshotStore.restore(this._snapshotRuntime(), snapshot);
363
+ this.updateOverlay(this.stats(), this.statsHistory());
364
+ };
365
+ //#endregion
139
366
  //#region ---------- Resources (singletons) ----------
140
367
  World.prototype.setResource = function (key, value) {
141
368
  this.resources.set(key, value);
@@ -147,9 +374,9 @@ var World = /** @class */ (function () {
147
374
  };
148
375
  World.prototype.requireResource = function (key) {
149
376
  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)."));
377
+ var name_2 = this._formatCtor(key);
378
+ throw new Error("requireResource(".concat(name_2, ") failed: resource missing. ") +
379
+ "Insert it via world.setResource(".concat(name_2, ", value) or world.initResource(").concat(name_2, ", () => value)."));
153
380
  }
154
381
  return this.resources.get(key);
155
382
  };
@@ -181,7 +408,7 @@ var World = /** @class */ (function () {
181
408
  ch.drain(fn);
182
409
  };
183
410
  World.prototype.clearEvents = function (key) {
184
- var e_3, _a;
411
+ var e_9, _a;
185
412
  if (key) {
186
413
  var ch = this.eventChannels.get(key);
187
414
  if (!ch)
@@ -196,34 +423,35 @@ var World = /** @class */ (function () {
196
423
  ch.clear();
197
424
  }
198
425
  }
199
- catch (e_3_1) { e_3 = { error: e_3_1 }; }
426
+ catch (e_9_1) { e_9 = { error: e_9_1 }; }
200
427
  finally {
201
428
  try {
202
429
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
203
430
  }
204
- finally { if (e_3) throw e_3.error; }
431
+ finally { if (e_9) throw e_9.error; }
205
432
  }
206
433
  };
207
434
  /** @internal Called by Schedule at phase boundaries */
208
435
  World.prototype.swapEvents = function () {
209
- var e_4, _a;
436
+ var e_10, _a;
210
437
  try {
211
438
  for (var _b = __values(this.eventChannels.values()), _c = _b.next(); !_c.done; _c = _b.next()) {
212
439
  var ch = _c.value;
213
440
  ch.swapBuffers();
214
441
  }
215
442
  }
216
- catch (e_4_1) { e_4 = { error: e_4_1 }; }
443
+ catch (e_10_1) { e_10 = { error: e_10_1 }; }
217
444
  finally {
218
445
  try {
219
446
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
220
447
  }
221
- finally { if (e_4) throw e_4.error; }
448
+ finally { if (e_10) throw e_10.error; }
222
449
  }
223
450
  };
224
451
  //#endregion
225
452
  //#region ---------- Entity lifecycle ----------
226
453
  World.prototype.spawn = function () {
454
+ this._ensureNotIterating("spawn");
227
455
  var entity = this.entities.create();
228
456
  // place in archetype 0
229
457
  var archetype0 = this.archetypes[0];
@@ -234,7 +462,7 @@ var World = /** @class */ (function () {
234
462
  return entity;
235
463
  };
236
464
  World.prototype.spawnMany = function () {
237
- var e_5, _a;
465
+ var e_11, _a;
238
466
  var items = [];
239
467
  for (var _i = 0; _i < arguments.length; _i++) {
240
468
  items[_i] = arguments[_i];
@@ -246,12 +474,12 @@ var World = /** @class */ (function () {
246
474
  this.add(e, ctor, value);
247
475
  }
248
476
  }
249
- catch (e_5_1) { e_5 = { error: e_5_1 }; }
477
+ catch (e_11_1) { e_11 = { error: e_11_1 }; }
250
478
  finally {
251
479
  try {
252
480
  if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
253
481
  }
254
- finally { if (e_5) throw e_5.error; }
482
+ finally { if (e_11) throw e_11.error; }
255
483
  }
256
484
  return e;
257
485
  };
@@ -265,19 +493,19 @@ var World = /** @class */ (function () {
265
493
  this.entities.kill(e);
266
494
  };
267
495
  World.prototype.despawnMany = function (entities) {
268
- var e_6, _a;
496
+ var e_12, _a;
269
497
  try {
270
498
  for (var entities_1 = __values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) {
271
499
  var e = entities_1_1.value;
272
500
  this.despawn(e);
273
501
  }
274
502
  }
275
- catch (e_6_1) { e_6 = { error: e_6_1 }; }
503
+ catch (e_12_1) { e_12 = { error: e_12_1 }; }
276
504
  finally {
277
505
  try {
278
506
  if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1);
279
507
  }
280
- finally { if (e_6) throw e_6.error; }
508
+ finally { if (e_12) throw e_12.error; }
281
509
  }
282
510
  };
283
511
  //#endregion
@@ -300,7 +528,8 @@ var World = /** @class */ (function () {
300
528
  return a.column(tid)[meta.row];
301
529
  };
302
530
  World.prototype.set = function (e, ctor, value) {
303
- var meta = this._assertAlive(e, "set(".concat(this._formatCtor(ctor), ")"));
531
+ var op = "set(".concat(this._formatCtor(ctor), ")");
532
+ var meta = this._assertAlive(e, op);
304
533
  var tid = (0, TypeRegistry_1.typeId)(ctor);
305
534
  var a = this.archetypes[meta.arch];
306
535
  if (!a.has(tid))
@@ -308,8 +537,9 @@ var World = /** @class */ (function () {
308
537
  a.column(tid)[meta.row] = value;
309
538
  };
310
539
  World.prototype.add = function (e, ctor, value) {
311
- this._assertAlive(e, "add(".concat(this._formatCtor(ctor), ")"));
312
- this._ensureNotIterating("add");
540
+ var op = "add(".concat(this._formatCtor(ctor), ")");
541
+ this._assertAlive(e, op);
542
+ this._ensureNotIterating(op);
313
543
  var tid = (0, TypeRegistry_1.typeId)(ctor);
314
544
  var srcMeta = this.entities.meta[e.id];
315
545
  var src = this.archetypes[srcMeta.arch];
@@ -327,28 +557,75 @@ var World = /** @class */ (function () {
327
557
  });
328
558
  };
329
559
  World.prototype.addMany = function (e) {
330
- var e_7, _a;
560
+ var e_13, _a, e_14, _b;
331
561
  var items = [];
332
562
  for (var _i = 1; _i < arguments.length; _i++) {
333
563
  items[_i - 1] = arguments[_i];
334
564
  }
565
+ if (items.length === 0)
566
+ return;
567
+ this._assertAlive(e, "addMany");
568
+ this._ensureNotIterating("addMany");
569
+ var srcMeta = this.entities.meta[e.id];
570
+ var src = this.archetypes[srcMeta.arch];
571
+ // Build component map: TypeId -> value
572
+ var newComps = new Map();
335
573
  try {
336
574
  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);
575
+ var _c = __read(items_2_1.value, 2), ctor = _c[0], value = _c[1];
576
+ var tid = (0, TypeRegistry_1.typeId)(ctor);
577
+ newComps.set(tid, value);
339
578
  }
340
579
  }
341
- catch (e_7_1) { e_7 = { error: e_7_1 }; }
580
+ catch (e_13_1) { e_13 = { error: e_13_1 }; }
342
581
  finally {
343
582
  try {
344
583
  if (items_2_1 && !items_2_1.done && (_a = items_2.return)) _a.call(items_2);
345
584
  }
346
- finally { if (e_7) throw e_7.error; }
585
+ finally { if (e_13) throw e_13.error; }
347
586
  }
587
+ // Compute final signature: src.sig + new components
588
+ var dstSig = src.sig.slice();
589
+ try {
590
+ for (var _d = __values(newComps.keys()), _e = _d.next(); !_e.done; _e = _d.next()) {
591
+ var tid = _e.value;
592
+ if (!src.has(tid)) {
593
+ // Insert in sorted order
594
+ var i = 0;
595
+ while (i < dstSig.length && dstSig[i] < tid)
596
+ i++;
597
+ if (dstSig[i] !== tid) {
598
+ dstSig.splice(i, 0, tid);
599
+ }
600
+ }
601
+ else {
602
+ // Component already exists, update in-place
603
+ src.column(tid)[srcMeta.row] = newComps.get(tid);
604
+ newComps.delete(tid);
605
+ }
606
+ }
607
+ }
608
+ catch (e_14_1) { e_14 = { error: e_14_1 }; }
609
+ finally {
610
+ try {
611
+ if (_e && !_e.done && (_b = _d.return)) _b.call(_d);
612
+ }
613
+ finally { if (e_14) throw e_14.error; }
614
+ }
615
+ // If all components were in-place updates, no move needed
616
+ if (newComps.size === 0)
617
+ return;
618
+ // Single move to final archetype
619
+ var dst = this._getOrCreateArchetype(dstSig);
620
+ this._moveEntity(e, src, srcMeta.row, dst, function (t) {
621
+ // Use new value if adding this component, otherwise copy from src
622
+ return newComps.has(t) ? newComps.get(t) : src.column(t)[srcMeta.row];
623
+ });
348
624
  };
349
625
  World.prototype.remove = function (e, ctor) {
350
- this._assertAlive(e, "remove(".concat(this._formatCtor(ctor), ")"));
351
- this._ensureNotIterating("remove");
626
+ var op = "remove(".concat(this._formatCtor(ctor), ")");
627
+ this._assertAlive(e, op);
628
+ this._ensureNotIterating(op);
352
629
  var tid = (0, TypeRegistry_1.typeId)(ctor);
353
630
  var srcMeta = this.entities.meta[e.id];
354
631
  var src = this.archetypes[srcMeta.arch];
@@ -362,47 +639,57 @@ var World = /** @class */ (function () {
362
639
  });
363
640
  };
364
641
  World.prototype.removeMany = function (e) {
365
- var e_8, _a;
642
+ var e_15, _a;
366
643
  var ctors = [];
367
644
  for (var _i = 1; _i < arguments.length; _i++) {
368
645
  ctors[_i - 1] = arguments[_i];
369
646
  }
647
+ if (ctors.length === 0)
648
+ return;
649
+ this._assertAlive(e, "removeMany");
650
+ this._ensureNotIterating("removeMany");
651
+ var srcMeta = this.entities.meta[e.id];
652
+ var src = this.archetypes[srcMeta.arch];
653
+ // Collect TypeIds to remove
654
+ var toRemove = new Set();
370
655
  try {
371
656
  for (var ctors_1 = __values(ctors), ctors_1_1 = ctors_1.next(); !ctors_1_1.done; ctors_1_1 = ctors_1.next()) {
372
657
  var ctor = ctors_1_1.value;
373
- this.remove(e, ctor);
658
+ var tid = (0, TypeRegistry_1.typeId)(ctor);
659
+ if (src.has(tid)) {
660
+ toRemove.add(tid);
661
+ }
374
662
  }
375
663
  }
376
- catch (e_8_1) { e_8 = { error: e_8_1 }; }
664
+ catch (e_15_1) { e_15 = { error: e_15_1 }; }
377
665
  finally {
378
666
  try {
379
667
  if (ctors_1_1 && !ctors_1_1.done && (_a = ctors_1.return)) _a.call(ctors_1);
380
668
  }
381
- finally { if (e_8) throw e_8.error; }
669
+ finally { if (e_15) throw e_15.error; }
382
670
  }
671
+ // If nothing to remove, no-op
672
+ if (toRemove.size === 0)
673
+ return;
674
+ // Compute final signature: src.sig - removed components
675
+ var dstSig = src.sig.filter(function (tid) { return !toRemove.has(tid); });
676
+ // Single move to final archetype
677
+ var dst = this._getOrCreateArchetype(dstSig);
678
+ this._moveEntity(e, src, srcMeta.row, dst, function (t) {
679
+ // Copy all components except removed ones (dstSig guarantees t is not removed)
680
+ return src.column(t)[srcMeta.row];
681
+ });
383
682
  };
384
683
  World.prototype.query = function () {
684
+ var _a;
385
685
  var ctors = [];
386
686
  for (var _i = 0; _i < arguments.length; _i++) {
387
687
  ctors[_i] = arguments[_i];
388
688
  }
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;
689
+ var _b = World._buildQueryTypeIds(ctors), requested = _b.requested, needSorted = _b.needSorted;
403
690
  function gen(world) {
404
- var _a, _b, a, cols, i, row, e, out, i, e_9_1;
405
- var e_9, _c;
691
+ var _a, _b, a, cols, i, row, e, out, i, e_16_1;
692
+ var e_16, _c;
406
693
  return __generator(this, function (_d) {
407
694
  switch (_d.label) {
408
695
  case 0:
@@ -445,14 +732,14 @@ var World = /** @class */ (function () {
445
732
  return [3 /*break*/, 3];
446
733
  case 8: return [3 /*break*/, 11];
447
734
  case 9:
448
- e_9_1 = _d.sent();
449
- e_9 = { error: e_9_1 };
735
+ e_16_1 = _d.sent();
736
+ e_16 = { error: e_16_1 };
450
737
  return [3 /*break*/, 11];
451
738
  case 10:
452
739
  try {
453
740
  if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
454
741
  }
455
- finally { if (e_9) throw e_9.error; }
742
+ finally { if (e_16) throw e_16.error; }
456
743
  return [7 /*endfinally*/];
457
744
  case 11: return [3 /*break*/, 13];
458
745
  case 12:
@@ -462,10 +749,179 @@ var World = /** @class */ (function () {
462
749
  }
463
750
  });
464
751
  }
465
- return gen(this);
752
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
753
+ var self = this;
754
+ return _a = {}, _a[Symbol.iterator] = function () { return gen(self); }, _a;
755
+ };
756
+ World.prototype.queryTables = function () {
757
+ var _a;
758
+ var ctors = [];
759
+ for (var _i = 0; _i < arguments.length; _i++) {
760
+ ctors[_i] = arguments[_i];
761
+ }
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) {
768
+ case 0:
769
+ world._iterateDepth++;
770
+ _d.label = 1;
771
+ case 1:
772
+ _d.trys.push([1, , 10, 11]);
773
+ _d.label = 2;
774
+ case 2:
775
+ _d.trys.push([2, 7, 8, 9]);
776
+ _a = __values(world.archetypes), _b = _a.next();
777
+ _d.label = 3;
778
+ 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))
784
+ return [3 /*break*/, 5];
785
+ out = { entities: a.entities };
786
+ for (i = 0; i < requested.length; i++) {
787
+ out["c".concat(i + 1)] = a.column(requested[i]);
788
+ }
789
+ return [4 /*yield*/, out];
790
+ case 4:
791
+ _d.sent();
792
+ _d.label = 5;
793
+ case 5:
794
+ _b = _a.next();
795
+ return [3 /*break*/, 3];
796
+ case 6: return [3 /*break*/, 9];
797
+ case 7:
798
+ e_17_1 = _d.sent();
799
+ e_17 = { error: e_17_1 };
800
+ return [3 /*break*/, 9];
801
+ case 8:
802
+ try {
803
+ if (_b && !_b.done && (_c = _a.return)) _c.call(_a);
804
+ }
805
+ finally { if (e_17) throw e_17.error; }
806
+ return [7 /*endfinally*/];
807
+ case 9: return [3 /*break*/, 11];
808
+ case 10:
809
+ world._iterateDepth--;
810
+ return [7 /*endfinally*/];
811
+ case 11: return [2 /*return*/];
812
+ }
813
+ });
814
+ }
815
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
816
+ var self = this;
817
+ return _a = {}, _a[Symbol.iterator] = function () { return gen(self); }, _a;
818
+ };
819
+ World.prototype.queryEach = function () {
820
+ var e_18, _a;
821
+ var args = [];
822
+ for (var _i = 0; _i < arguments.length; _i++) {
823
+ args[_i] = arguments[_i];
824
+ }
825
+ 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;
828
+ this._iterateDepth++;
829
+ try {
830
+ 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))
836
+ continue;
837
+ var cols = new Array(requested.length);
838
+ for (var i = 0; i < requested.length; i++)
839
+ 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;
864
+ }
865
+ };
866
+ for (var row = 0; row < a.entities.length; row++) {
867
+ _loop_1(row);
868
+ }
869
+ }
870
+ }
871
+ catch (e_18_1) { e_18 = { error: e_18_1 }; }
872
+ finally {
873
+ try {
874
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
875
+ }
876
+ finally { if (e_18) throw e_18.error; }
877
+ }
878
+ }
879
+ finally {
880
+ this._iterateDepth--;
881
+ }
466
882
  };
467
883
  //#endregion
468
884
  //#region ---------- Internals ----------
885
+ World.prototype._snapshotRuntime = function () {
886
+ var _this = this;
887
+ return {
888
+ ensureNotIterating: function (op) { return _this._ensureNotIterating(op); },
889
+ formatCtor: function (ctor) { return _this._formatCtor(ctor); },
890
+ flush: function () { return _this.flush(); },
891
+ resetArchetypes: function () { return _this._resetArchetypes(); },
892
+ getOrCreateArchetype: function (sig) { return _this._getOrCreateArchetype(sig); },
893
+ commands: this.commands,
894
+ entities: this.entities,
895
+ archetypes: this.archetypes,
896
+ resources: this.resources,
897
+ eventChannels: this.eventChannels
898
+ };
899
+ };
900
+ World.prototype._resetArchetypes = function () {
901
+ this.archetypes.length = 0;
902
+ this.archByKey.clear();
903
+ // Archetype 0: empty signature
904
+ var archetype0 = new Archetype_1.Archetype(0, []);
905
+ this.archetypes[0] = archetype0;
906
+ this.archByKey.set("", archetype0);
907
+ };
908
+ World._buildQueryTypeIds = function (ctors) {
909
+ // Preserve caller order for (c1,c2,c3,...) mapping.
910
+ var requested = new Array(ctors.length);
911
+ for (var i = 0; i < ctors.length; i++)
912
+ 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; });
916
+ var w = 0;
917
+ for (var i = 0; i < needSorted.length; i++) {
918
+ var v = needSorted[i];
919
+ if (i === 0 || v !== needSorted[w - 1])
920
+ needSorted[w++] = v;
921
+ }
922
+ needSorted.length = w;
923
+ return { requested: requested, needSorted: needSorted };
924
+ };
469
925
  World.prototype._ensureNotIterating = function (op) {
470
926
  if (this._iterateDepth > 0) {
471
927
  throw new Error("Cannot do structural change (".concat(op, ") while iterating. Use world.cmd() and flush at end of frame."));
@@ -497,7 +953,7 @@ var World = /** @class */ (function () {
497
953
  * Then swap-remove from src.
498
954
  */
499
955
  World.prototype._moveEntity = function (e, src, srcRow, dst, pick) {
500
- var e_10, _a;
956
+ var e_19, _a;
501
957
  // add row in dst
502
958
  var dstRow = dst.addRow(e);
503
959
  try {
@@ -506,12 +962,12 @@ var World = /** @class */ (function () {
506
962
  dst.column(t).push(pick(t));
507
963
  }
508
964
  }
509
- catch (e_10_1) { e_10 = { error: e_10_1 }; }
965
+ catch (e_19_1) { e_19 = { error: e_19_1 }; }
510
966
  finally {
511
967
  try {
512
968
  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
513
969
  }
514
- finally { if (e_10) throw e_10.error; }
970
+ finally { if (e_19) throw e_19.error; }
515
971
  }
516
972
  // update meta to dst before removing from src (in case src==dst should never happen here)
517
973
  var m = this.entities.meta[e.id];
@@ -552,10 +1008,10 @@ var World = /** @class */ (function () {
552
1008
  * Throws an error if the entity is not alive
553
1009
  */
554
1010
  World.prototype._assertAlive = function (e, op) {
555
- var _a, _b;
556
1011
  var meta = this.entities.meta[e.id];
557
1012
  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", ")"));
1013
+ var status_1 = meta ? "alive=".concat(meta.alive, ", gen=").concat(meta.gen) : "not found";
1014
+ throw new Error("".concat(op, " failed: stale entity ").concat(this._formatEntity(e), " (").concat(status_1, ")"));
559
1015
  }
560
1016
  return meta;
561
1017
  };
@@ -567,6 +1023,30 @@ var World = /** @class */ (function () {
567
1023
  }
568
1024
  return ch;
569
1025
  };
1026
+ /**
1027
+ * @internal Warns about lifecycle method conflicts in development mode
1028
+ */
1029
+ World.prototype._warnAboutLifecycleConflict = function (method) {
1030
+ var hint = method === "World.update"
1031
+ ? "You have systems registered via schedule.add() but are calling world.update()."
1032
+ : "You have systems registered via world.addSystem() but are calling schedule.run().";
1033
+ throw new Error("\u26A0\uFE0F ECS Lifecycle Conflict Detected!\n" +
1034
+ "".concat(hint, "\n") +
1035
+ "This can cause:\n" +
1036
+ "- Double command flushes\n" +
1037
+ "- Confusing event visibility\n" +
1038
+ "- Unclear lifecycle semantics\n\n" +
1039
+ "Recommended fix:\n" +
1040
+ "- Use World.update() for simple single-phase applications (with world.addSystem())\n" +
1041
+ "- Use Schedule.run() for multi-phase applications (with schedule.add())\n\n" +
1042
+ "Choose ONE approach and stick with it.");
1043
+ };
1044
+ /**
1045
+ * @internal Returns the number of systems registered via addSystem()
1046
+ */
1047
+ World.prototype._getSystemCount = function () {
1048
+ return this.systems.length;
1049
+ };
570
1050
  return World;
571
- }());
1051
+ }(StatsOverlay_1.StatsOverlay));
572
1052
  exports.World = World;