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.
@@ -74,7 +74,7 @@ var Archetype = /** @class */ (function () {
74
74
  this.entities.pop();
75
75
  try {
76
76
  for (var _c = __values(this.cols), _d = _c.next(); !_d.done; _d = _c.next()) {
77
- var _e = __read(_d.value, 2), _ = _e[0], col = _e[1];
77
+ var _e = __read(_d.value, 2), col = _e[1];
78
78
  col[row] = col[last];
79
79
  col.pop();
80
80
  }
@@ -1,4 +1,4 @@
1
- import type { Entity, EntityMeta } from "./Types";
1
+ import type { Entity, EntityMeta, WorldSnapshotAllocator } from "./Types";
2
2
  export declare class EntityManager {
3
3
  private _nextId;
4
4
  private _free;
@@ -6,4 +6,6 @@ export declare class EntityManager {
6
6
  create(): Entity;
7
7
  isAlive(e: Entity): boolean;
8
8
  kill(e: Entity): void;
9
+ snapshotAllocator(): WorldSnapshotAllocator;
10
+ restoreAllocator(snapshot: WorldSnapshotAllocator): void;
9
11
  }
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __values = (this && this.__values) || function(o) {
3
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
4
+ if (m) return m.call(o);
5
+ if (o && typeof o.length === "number") return {
6
+ next: function () {
7
+ if (o && i >= o.length) o = void 0;
8
+ return { value: o && o[i++], done: !o };
9
+ }
10
+ };
11
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
12
+ };
2
13
  Object.defineProperty(exports, "__esModule", { value: true });
3
14
  exports.EntityManager = void 0;
4
15
  var EntityManager = /** @class */ (function () {
@@ -33,6 +44,84 @@ var EntityManager = /** @class */ (function () {
33
44
  m.alive = false;
34
45
  this._free.push(e.id);
35
46
  };
47
+ EntityManager.prototype.snapshotAllocator = function () {
48
+ var generations = [];
49
+ for (var id = 1; id < this.meta.length; id++) {
50
+ var m = this.meta[id];
51
+ if (!m)
52
+ continue;
53
+ generations.push([id, m.gen]);
54
+ }
55
+ return {
56
+ nextId: this._nextId,
57
+ free: this._free.slice(),
58
+ generations: generations
59
+ };
60
+ };
61
+ EntityManager.prototype.restoreAllocator = function (snapshot) {
62
+ var e_1, _a, e_2, _b;
63
+ if (!Number.isInteger(snapshot.nextId) || snapshot.nextId < 1) {
64
+ throw new Error("Invalid snapshot allocator.nextId: ".concat(snapshot.nextId));
65
+ }
66
+ var seenGenerations = new Set();
67
+ this.meta.length = 0;
68
+ try {
69
+ for (var _c = __values(snapshot.generations), _d = _c.next(); !_d.done; _d = _c.next()) {
70
+ var entry = _d.value;
71
+ var id = entry[0];
72
+ var gen = entry[1];
73
+ if (!Number.isInteger(id) || id <= 0) {
74
+ throw new Error("Invalid snapshot allocator generations id: ".concat(id));
75
+ }
76
+ if (!Number.isInteger(gen) || gen <= 0) {
77
+ throw new Error("Invalid snapshot allocator generation for id ".concat(id, ": ").concat(gen));
78
+ }
79
+ if (id >= snapshot.nextId) {
80
+ throw new Error("Invalid snapshot allocator generation id ".concat(id, ": must be < nextId (").concat(snapshot.nextId, ")"));
81
+ }
82
+ if (seenGenerations.has(id)) {
83
+ throw new Error("Duplicate snapshot allocator generation entry for id ".concat(id));
84
+ }
85
+ seenGenerations.add(id);
86
+ this.meta[id] = { gen: gen, alive: false, arch: 0, row: 0 };
87
+ }
88
+ }
89
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
90
+ finally {
91
+ try {
92
+ if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
93
+ }
94
+ finally { if (e_1) throw e_1.error; }
95
+ }
96
+ var seenFree = new Set();
97
+ try {
98
+ for (var _e = __values(snapshot.free), _f = _e.next(); !_f.done; _f = _e.next()) {
99
+ var id = _f.value;
100
+ if (!Number.isInteger(id) || id <= 0) {
101
+ throw new Error("Invalid snapshot allocator free id: ".concat(id));
102
+ }
103
+ if (id >= snapshot.nextId) {
104
+ throw new Error("Invalid snapshot allocator free id ".concat(id, ": must be < nextId (").concat(snapshot.nextId, ")"));
105
+ }
106
+ if (!seenGenerations.has(id)) {
107
+ throw new Error("Invalid snapshot allocator free id ".concat(id, ": missing generation entry"));
108
+ }
109
+ if (seenFree.has(id)) {
110
+ throw new Error("Duplicate snapshot allocator free id ".concat(id));
111
+ }
112
+ seenFree.add(id);
113
+ }
114
+ }
115
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
116
+ finally {
117
+ try {
118
+ if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
119
+ }
120
+ finally { if (e_2) throw e_2.error; }
121
+ }
122
+ this._nextId = snapshot.nextId;
123
+ this._free = snapshot.free.slice();
124
+ };
36
125
  return EntityManager;
37
126
  }());
38
127
  exports.EntityManager = EntityManager;
@@ -1,10 +1,133 @@
1
1
  import type { SystemFn, WorldApi } from "./Types";
2
2
  /**
3
- * Minimal scheduler that supports phases, without borrow-checking.
4
- * (Add conflict detection later if you want parallelism.)
3
+ * Minimal multiphase scheduler for running ECS systems in named "phases".
4
+ *
5
+ * ## What it does
6
+ * - Group systems by phase name (e.g. `"input"`, `"update"`, `"render"`).
7
+ * - Executes phases in a chosen order.
8
+ * - Optionally performs **phase-boundary work** (flush commands and swap events).
9
+ * - Supports **ordering constraints** between phases via `.after()` / `.before()`.
10
+ *
11
+ * ## Phase boundaries
12
+ * By default (`boundaryMode = "auto"`), after each phase:
13
+ * - if `world.cmd().hasPending()` → `world.flush()`
14
+ * - `world.swapEvents()` so events emitted in this phase become visible to the next phase
15
+ *
16
+ * Use `"manual"` boundary mode if you want to control flush/event delivery yourself.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const schedule = new Schedule();
21
+ *
22
+ * schedule
23
+ * .add(world, "input", inputSystem)
24
+ * .add(world, "sim", simSystem).after("input")
25
+ * .add(world, "render", renderSystem).after("sim");
26
+ *
27
+ * // Either pass an explicit phase list...
28
+ * schedule.run(world, dt, ["input", "sim", "render"]);
29
+ *
30
+ * // ...or omit it and let constraints drive the order:
31
+ * // schedule.run(world, dt);
32
+ * ```
5
33
  */
6
34
  export declare class Schedule {
7
- private readonly phases;
8
- add(phase: string, fn: SystemFn): this;
9
- run(world: WorldApi, dt: number, phaseOrder: string[]): void;
35
+ private phaseOrder;
36
+ private boundaryMode;
37
+ /**
38
+ * Phase ordering constraints stored as edges `before -> after`.
39
+ * Example: calling `after("input")` for phase `"sim"` records `input -> sim`.
40
+ */
41
+ private readonly phaseEdges;
42
+ /**
43
+ * Tracks the most recently modified phase (via add()) so `.after()`/`.before()` can be chained.
44
+ */
45
+ private _lastPhase;
46
+ /**
47
+ * Set a default phase order used when calling `run(world, dt)` without passing `phaseOrder`.
48
+ *
49
+ * @param phases Ordered list of phase names to execute.
50
+ */
51
+ setOrder(phases: string[]): this;
52
+ /**
53
+ * Control what happens at phase boundaries.
54
+ *
55
+ * - `"auto"` (default): flush deferred commands (if pending) and swap event buffers after each phase.
56
+ * - `"manual"`: do nothing automatically; the caller is responsible for `world.flush()` / `world.swapEvents()`.
57
+ *
58
+ * @param mode Boundary behavior.
59
+ */
60
+ setBoundaryMode(mode: "auto" | "manual"): this;
61
+ /**
62
+ * Add a system function to a phase in the given world.
63
+ *
64
+ * The returned object allows you to attach **phase ordering constraints**:
65
+ * - `.after("input")` means this phase must run after `"input"`.
66
+ * - `.before("render")` means this phase must run before `"render"`.
67
+ *
68
+ * Constraints are **phase-level**, not system-level: they affect the relative order of phases, not
69
+ * the order of systems within the same phase.
70
+ *
71
+ * @param world World instance to add the system to.
72
+ * @param phase Phase name.
73
+ * @param fn System function `(world, dt) => void`.
74
+ */
75
+ add(world: WorldApi, phase: string, fn: SystemFn): {
76
+ after: (otherPhase: string) => Schedule;
77
+ before: (otherPhase: string) => Schedule;
78
+ };
79
+ /**
80
+ * Constrain the last added phase to run after `otherPhase`.
81
+ *
82
+ * Must be called after `add(...)`.
83
+ */
84
+ after(otherPhase: string): this;
85
+ /**
86
+ * Constrain the last added phase to run before `otherPhase`.
87
+ *
88
+ * Must be called after `add(...)`.
89
+ */
90
+ before(otherPhase: string): this;
91
+ /**
92
+ * Execute all scheduled systems for the given frame.
93
+ *
94
+ * Phase order selection:
95
+ * 1) If `phaseOrder` is provided, it is used as-is.
96
+ * 2) Else if `setOrder()` was called, the stored order is used.
97
+ * 3) Else an order is computed from `.after()`/`.before()` constraints.
98
+ *
99
+ * @param world World instance (API) passed to each system.
100
+ * @param dt Delta time in seconds.
101
+ * @param phaseOrder Optional explicit phase order for this run.
102
+ *
103
+ * @throws If both `Schedule.run()` and `World.update()` are used on the same World instance.
104
+ * @throws If phase constraints contain a cycle and no explicit order is provided.
105
+ * @throws Re-throws system errors, wrapped with `[phase=... system=...]` context.
106
+ *
107
+ * @note When using `Schedule`, avoid calling `world.update()` on the same World instance.
108
+ */
109
+ run(world: WorldApi, dt: number, phaseOrder?: string[]): void;
110
+ /**
111
+ * Record a phase ordering constraint `before -> after`.
112
+ *
113
+ * @param before Phase that must execute first.
114
+ * @param after Phase that must execute later.
115
+ *
116
+ * @throws If `before === after`.
117
+ * @internal
118
+ */
119
+ private _addPhaseConstraint;
120
+ /**
121
+ * Compute a phase order from the currently registered constraints.
122
+ *
123
+ * Uses a stable topological sort:
124
+ * - phases explicitly registered via `add()` keep insertion order when unconstrained
125
+ * - remaining ties fall back to lexicographic order
126
+ *
127
+ * @param world World instance to get phases from.
128
+ * @returns A valid phase order that satisfies all constraints.
129
+ * @throws If constraints contain a cycle.
130
+ * @internal
131
+ */
132
+ private _computePhaseOrder;
10
133
  }