@vworlds/vecs 1.0.8 → 1.0.10

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/dist/system.js CHANGED
@@ -1,7 +1,4 @@
1
- import { ArrayMap } from "./util/array_map.js";
2
- import { Bitset } from "./util/bitset.js";
3
1
  import { Query } from "./query.js";
4
- import { HAS } from "./dsl.js";
5
2
  import { Phase } from "./phase.js";
6
3
  /**
7
4
  * A reactive processor that operates on a filtered subset of world entities.
@@ -21,6 +18,11 @@ import { Phase } from "./phase.js";
21
18
  * once all systems are registered; after that, drive the loop with
22
19
  * {@link World.runPhase}.
23
20
  *
21
+ * Internally each system holds a single ordered **inbox** of routed events
22
+ * (`enter`, `exit`, `update`). The world appends to it during command-queue
23
+ * routing; the system replays the inbox at the top of every `_run` so
24
+ * callbacks observe events in arrival order.
25
+ *
24
26
  * ### Component injection and type inference
25
27
  *
26
28
  * `enter`, `exit`, `update`, `each`, and `sort` all accept an array of
@@ -36,9 +38,7 @@ import { Phase } from "./phase.js";
36
38
  export class System extends Query {
37
39
  constructor(name, world) {
38
40
  super(name, world, false);
39
- this.componentUpdateCallbacks = new ArrayMap();
40
- this.updateQueue = [];
41
- this.watchlistBitmask = new Bitset();
41
+ this.inbox = [];
42
42
  }
43
43
  /**
44
44
  * Assign this system to a pipeline phase.
@@ -63,49 +63,83 @@ export class System extends Query {
63
63
  this._phase = p;
64
64
  return this;
65
65
  }
66
- /** @internal Delivers a component-modified notification to this system. */
67
- notifyModified(c) {
68
- if (!this.watchlistBitmask.hasBit(c.bitPtr)) {
69
- return;
70
- }
71
- this.updateQueue.push(c);
72
- }
73
- /** @internal Fires enter callbacks, adds entity to tracked set, queues component updates. */
66
+ /** @internal Routing entry: register membership and enqueue an enter event. */
74
67
  _enter(e) {
75
- super._enter(e);
76
- e.forEachComponent((c) => this.notifyModified(c));
77
- }
78
- /** @internal Fires exit callbacks, removes entity from tracked set, drains update queue. */
79
- _exit(e) {
80
- super._exit(e);
81
- this.updateQueue.forEach((c, i) => {
82
- if (!c) {
83
- return;
84
- }
85
- if (c.entity === e) {
86
- this.updateQueue[i] = undefined;
68
+ this._entities?.add(e);
69
+ e._addQueryMembership(this);
70
+ if (this._enterCallback !== undefined) {
71
+ this.inbox.push({ kind: 0 /* InboxCommand.Enter */, entity: e });
72
+ }
73
+ // Bridge: surface watched components on entry through `notifyModified`,
74
+ // which on System pushes inbox `update` events.
75
+ e.forEachComponent((c) => {
76
+ if (this.watchlistBitmask.hasBit(c.bitPtr)) {
77
+ this.notifyModified(c);
87
78
  }
88
79
  });
89
80
  }
90
- /** @internal Execute one tick: run `run`, fire `each`, then drain the update queue. */
91
- _run(now, delta) {
92
- if (this._runCallback) {
93
- this._runCallback(now, delta);
81
+ /** @internal Routing entry: deregister membership and enqueue an exit event. */
82
+ _exit(e) {
83
+ this._entities?.delete(e);
84
+ e._removeQueryMembership(this);
85
+ if (this._exitCallback !== undefined) {
86
+ // Only snapshot the types the callback injects, and only direct (non-parent)
87
+ // ones — resolved at registration time. Parent refs are read from e.parent
88
+ // at callback time. Undefined when the callback takes no injection.
89
+ let snapshot;
90
+ if (this._exitSnapshotTypes && this._exitSnapshotTypes.length > 0) {
91
+ snapshot = new Map();
92
+ for (const type of this._exitSnapshotTypes) {
93
+ const c = e._get(type);
94
+ if (c) {
95
+ snapshot.set(type, c);
96
+ }
97
+ }
98
+ }
99
+ this.inbox.push({ kind: 1 /* InboxCommand.Exit */, entity: e, snapshot });
94
100
  }
95
- if (this.eachCallback) {
96
- const cb = this.eachCallback;
97
- this.forEach((e) => cb(e));
101
+ }
102
+ /** @internal Routing entry: enqueue an update event if the watchlist matches. */
103
+ notifyModified(c) {
104
+ if (!this.watchlistBitmask.hasBit(c.bitPtr)) {
105
+ return;
98
106
  }
99
- this.updateQueue.forEach((c) => {
100
- if (!c) {
101
- return;
107
+ this.inbox.push({ kind: 2 /* InboxCommand.Update */, component: c });
108
+ }
109
+ /**
110
+ * @internal Execute one tick: drain the inbox in arrival order, then run
111
+ * `runCallback` and `eachCallback`. The whole body runs in a deferred
112
+ * scope; any mutations made by callbacks land in the world queue and are
113
+ * processed by the world after `_run` returns.
114
+ */
115
+ _run(now, delta) {
116
+ this.world.defer(() => {
117
+ for (let i = 0; i < this.inbox.length; i++) {
118
+ const event = this.inbox[i];
119
+ switch (event.kind) {
120
+ case 0 /* InboxCommand.Enter */:
121
+ this._enterCallback(event.entity);
122
+ break;
123
+ case 1 /* InboxCommand.Exit */:
124
+ this._exitCallback(event.entity, event.snapshot);
125
+ break;
126
+ case 2 /* InboxCommand.Update */:
127
+ const callback = this.componentUpdateCallbacks.get(event.component.type);
128
+ if (callback) {
129
+ callback(event.component);
130
+ }
131
+ break;
132
+ }
102
133
  }
103
- const callback = this.componentUpdateCallbacks.get(c.type);
104
- if (callback) {
105
- callback(c);
134
+ this.inbox.length = 0;
135
+ if (this._runCallback) {
136
+ this._runCallback(now, delta);
137
+ }
138
+ if (this.eachCallback) {
139
+ const cb = this.eachCallback;
140
+ this.forEach((e) => cb(e));
106
141
  }
107
142
  });
108
- this.updateQueue.length = 0;
109
143
  }
110
144
  /**
111
145
  * Register a per-tick callback that runs every time this system's phase
@@ -122,33 +156,6 @@ export class System extends Query {
122
156
  this._runCallback = callback;
123
157
  return this;
124
158
  }
125
- update(ComponentClass, injectOrCallback, callback) {
126
- const type = this.world.getComponentType(ComponentClass);
127
- if (typeof injectOrCallback === "function") {
128
- callback = injectOrCallback;
129
- this.componentUpdateCallbacks.set(type, callback);
130
- }
131
- else {
132
- const inject = injectOrCallback;
133
- const injectedComponentTypes = inject.map((C) => this.world.getComponentType(C));
134
- const cb = (c) => {
135
- const injected = [];
136
- injectedComponentTypes.forEach((InjectedComponentType) => {
137
- injected.push(c.entity.get(InjectedComponentType));
138
- });
139
- if (callback) {
140
- callback(c, injected);
141
- }
142
- };
143
- this.componentUpdateCallbacks.set(type, cb);
144
- }
145
- this.watchlistBitmask.add(type);
146
- if (!this.hasQuery) {
147
- const watchlist = this.watchlistBitmask.indices();
148
- this._belongs = HAS(this.world, ...watchlist);
149
- }
150
- return this;
151
- }
152
159
  /**
153
160
  * Register a callback that fires **every tick** for every entity currently
154
161
  * tracked by this system, with the listed components resolved from each
@@ -1 +1 @@
1
- {"version":3,"file":"system.js","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../src/system.ts"],"names":[],"mappings":"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"}
package/dist/world.d.ts CHANGED
@@ -5,6 +5,7 @@ import { System } from "./system.js";
5
5
  import { Filter } from "./filter.js";
6
6
  import { type QueryDSL, type ExtractRequired } from "./dsl.js";
7
7
  import { IPhase, Phase } from "./phase.js";
8
+ import { type Command } from "./command.js";
8
9
  /**
9
10
  * The central ECS container.
10
11
  *
@@ -36,20 +37,29 @@ import { IPhase, Phase } from "./phase.js";
36
37
  * ```
37
38
  */
38
39
  export declare class World {
39
- private entities;
40
+ private _entities;
40
41
  private componentNameTypeMap;
41
- private archChangeQueue;
42
- private destroyedEntities;
43
- private allQueries;
42
+ private _queries;
44
43
  private Class2Meta;
45
44
  private Type2Meta;
46
- private updatedComponents;
47
45
  private localComponentCounter;
48
46
  private componentRegistrationDisabled;
47
+ /** @internal Single ordered command queue used in deferred mode. */
48
+ private commandQueue;
49
+ /** @internal Nested `beginDefer` / `endDefer` count. */
50
+ private deferredDepth;
51
+ /** @internal True while `processCommandQueue` is iterating, to avoid re-entrant drains. */
52
+ private draining;
53
+ /** `true` when the world is in deferred mode — mutations are queued rather than applied immediately. */
54
+ get deferred(): boolean;
49
55
  /** @internal */
50
56
  _pipeline: Map<string, Phase>;
51
57
  private eidCounter;
52
58
  constructor();
59
+ /** @readonly */
60
+ get entities(): Omit<Map<number, Entity>, "set" | "delete" | "clear">;
61
+ /** @readonly */
62
+ get queries(): ReadonlyArray<Query>;
53
63
  /**
54
64
  * Return the entity with id `eid`, creating it if it does not yet exist.
55
65
  *
@@ -112,25 +122,48 @@ export declare class World {
112
122
  */
113
123
  getComponentType(typeOrClass: ComponentClassOrType): number;
114
124
  /**
115
- * Mark an entity's archetype as changed, queuing it for re-evaluation
116
- * against all system queries at the end of the current system run.
125
+ * Enter deferred mode. Mutations made until the matching {@link endDefer}
126
+ * are queued instead of executing inline.
117
127
  *
118
- * Also recursively marks all children as changed so that `{ PARENT: ... }`
119
- * queries are re-evaluated.
128
+ * Nested begin/end pairs are allowed; only the outermost `endDefer`
129
+ * triggers a drain.
120
130
  *
121
- * @internal Called automatically by {@link Entity.add} and
122
- * {@link Entity.remove}.
123
131
  */
124
- archetypeChanged(e: Entity): void;
125
- /** @internal */
126
- _notifyComponentAdded(e: Entity, c: Component): void;
127
- /** @internal */
128
- _notifyComponentRemoved(e: Entity, c: Component): void;
129
- /** @internal */
130
- _notifyEntityDestroyed(e: Entity): void;
131
- private updateArchetypes;
132
- /** @internal Queues a component for onSet / update delivery. */
133
- _queueUpdatedComponent(c: Component): void;
132
+ beginDefer(): void;
133
+ /**
134
+ * Leave deferred mode. When the depth returns to zero, the world processes
135
+ * the command queue (firing hooks and routing enter / exit / update events).
136
+ *
137
+ */
138
+ endDefer(): void;
139
+ /**
140
+ *
141
+ * @param fn callback to invoke in deferred mode.
142
+ */
143
+ defer(fn: () => void): void;
144
+ /**
145
+ * Drain any pending commands queued at the top level (depth 0).
146
+ *
147
+ * Useful between phases or after batch-loading network snapshots, to make
148
+ * accumulated mutations visible (fire hooks, route enter/exit/update) before
149
+ * the next read or system run.
150
+ */
151
+ flush(): void;
152
+ /** @internal Append a command to the queue. */
153
+ _enqueue(cmd: Command): void;
154
+ /**
155
+ * @internal Walk the command queue in insertion order, executing each
156
+ * command. Callbacks may push more commands, which are processed in the
157
+ * same pass via index iteration.
158
+ */
159
+ private processCommandQueue;
160
+ /**
161
+ * @internal Run a single command's side effects: data-layer mutation, hook
162
+ * firing, and routing to all registered queries / systems.
163
+ */
164
+ private executeCommand;
165
+ /** @internal Remove an entity from the world's entity map. Called by Entity._destroy. */
166
+ _unregisterEntity(entity: Entity): void;
134
167
  /**
135
168
  * Register a component class with the world.
136
169
  *
@@ -170,8 +203,6 @@ export declare class World {
170
203
  _addQuery(q: Query): void;
171
204
  /** @internal Called by {@link Query.destroy} to unregister a query and remove it from all entities. */
172
205
  _removeQuery(q: Query): void;
173
- /** @internal Iterate over all entities currently in the world. */
174
- _forEachEntity(callback: (e: Entity) => void): void;
175
206
  /**
176
207
  * Create a new {@link System}, register it, and return it for configuration.
177
208
  *
@@ -294,9 +325,11 @@ export declare class World {
294
325
  /**
295
326
  * Execute all systems in the given phase for one tick.
296
327
  *
297
- * After each system runs, pending archetype changes (entity add/remove
298
- * component events) are flushed so that `enter` / `exit` callbacks are
299
- * delivered before the next system in the same phase executes.
328
+ * Pending top-level mutations are drained at the start of the phase so the
329
+ * first system observes a consistent world. Each system's body runs in a
330
+ * deferred scope; mutations made by callbacks are appended to the world
331
+ * queue and processed by the world after the system returns, before the
332
+ * next system runs.
300
333
  *
301
334
  * @param phase - The {@link IPhase} to run (returned by {@link addPhase}).
302
335
  * @param now - Absolute timestamp in milliseconds (e.g. `Date.now()`).