@vworlds/vecs 1.0.54 → 1.0.55

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.
Files changed (54) hide show
  1. package/README.md +9 -0
  2. package/dist/entity/entity.components.js +4 -2
  3. package/dist/entity/entity.components.js.map +1 -1
  4. package/dist/entity/entity.lifecycle.d.ts +2 -0
  5. package/dist/entity/entity.lifecycle.js +9 -4
  6. package/dist/entity/entity.lifecycle.js.map +1 -1
  7. package/dist/geometry.d.ts +5 -0
  8. package/dist/geometry.js +2 -0
  9. package/dist/geometry.js.map +1 -0
  10. package/dist/index.d.ts +3 -1
  11. package/dist/index.js +2 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/package.json +1 -1
  14. package/dist/system.d.ts +10 -0
  15. package/dist/system.js +19 -2
  16. package/dist/system.js.map +1 -1
  17. package/dist/timer.d.ts +15 -1
  18. package/dist/timer.js +24 -4
  19. package/dist/timer.js.map +1 -1
  20. package/dist/world/destroy-order.d.ts +8 -0
  21. package/dist/world/destroy-order.js +60 -0
  22. package/dist/world/destroy-order.js.map +1 -0
  23. package/dist/world/stats.d.ts +51 -0
  24. package/dist/world/stats.js +88 -0
  25. package/dist/world/stats.js.map +1 -0
  26. package/dist/world/world.base.js +2 -0
  27. package/dist/world/world.base.js.map +1 -1
  28. package/dist/world/world.components.d.ts +2 -0
  29. package/dist/world/world.components.js +4 -0
  30. package/dist/world/world.components.js.map +1 -1
  31. package/dist/world/world.disposal.d.ts +34 -36
  32. package/dist/world/world.disposal.js +89 -94
  33. package/dist/world/world.disposal.js.map +1 -1
  34. package/dist/world/world.entities.d.ts +21 -1
  35. package/dist/world/world.entities.js +54 -7
  36. package/dist/world/world.entities.js.map +1 -1
  37. package/dist/world/world.js +17 -11
  38. package/dist/world/world.js.map +1 -1
  39. package/dist/world/world.modules.d.ts +8 -0
  40. package/dist/world/world.modules.js +22 -1
  41. package/dist/world/world.modules.js.map +1 -1
  42. package/dist/world/world.pipeline.d.ts +15 -1
  43. package/dist/world/world.pipeline.js +40 -4
  44. package/dist/world/world.pipeline.js.map +1 -1
  45. package/docs/README.md +21 -19
  46. package/docs/components.md +9 -0
  47. package/docs/concepts.md +4 -0
  48. package/docs/design-guide.md +7 -3
  49. package/docs/entities.md +36 -25
  50. package/docs/glossary.md +3 -3
  51. package/docs/modules.md +62 -0
  52. package/docs/systems.md +108 -2
  53. package/package.json +1 -1
  54. package/tsconfig.types.json +6 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"destroy-order.js","sourceRoot":"","sources":["../../src/world/destroy-order.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,QAAkB,EAAE,UAAgB;IAChE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAClC,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;YAC/C,IAAI,OAAO,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC7C,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;gBACzD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,OAAO,UAAU,EAAE,CAAC;QAClB,UAAU,GAAG,KAAK,CAAC;QACnB,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,CAAC,SAAiB,EAAW,EAAE,CAC5C,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACnD,IACE,CAAC,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC;gBACvB,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,CAAC,EACxE,CAAC;gBACD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBAClB,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACpB,UAAU,GAAG,IAAI,CAAC;YACpB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,SAAuC,GAAG,EAAE,CAAC,IAAI;IACtF,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;QAC/C,IAAI,OAAO,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,yEAAyE;AACzE,SAAS,gBAAgB,CACvB,MAAc,EACd,UAAe,EACf,MAAkC;IAElC,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;IACnD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;YAClB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { System } from "../system.js";
2
+ /** Accumulated synchronous execution time, in milliseconds, since the last reset. */
3
+ export interface Timing {
4
+ /** Number of admitted executions (not entity callbacks). */
5
+ runs: number;
6
+ /** Sum of execution durations in milliseconds. */
7
+ totalMs: number;
8
+ /** Longest execution duration in milliseconds. */
9
+ maxMs: number;
10
+ }
11
+ /** A system's timing; read its identity and cadence from the original system. */
12
+ export interface SystemTiming extends Timing {
13
+ /** The original system, not a descriptor copy. */
14
+ system: System;
15
+ }
16
+ /** A named timing breakdown supplied by a world extension. */
17
+ export interface OperationTiming extends Timing {
18
+ /** Stable operation name, for example `replication:encode`. */
19
+ name: string;
20
+ }
21
+ /** Opt-in world timing. Reading allocates snapshots; ticking reuses slots. */
22
+ export declare class WorldStats {
23
+ private readonly _pipeline;
24
+ private _enabled;
25
+ private readonly _systems;
26
+ private readonly _operations;
27
+ private _generation;
28
+ /** Whether system and extension timing is enabled. Off by default. */
29
+ get systemsEnabled(): boolean;
30
+ /** Stop measuring without discarding accumulated results. */
31
+ disableSystems(): void;
32
+ /** Start measuring systems and extension operations on subsequent ticks. */
33
+ enableSystems(): void;
34
+ /** Read extension-operation snapshots in first-seen order; empty before measurement. */
35
+ operations(): OperationTiming[];
36
+ /**
37
+ * Record one completed extension operation, reusing its first-seen named slot.
38
+ * World extensions select their instrumented path with `systemsEnabled` once
39
+ * per frame, then call this method with a finite, non-negative duration in ms.
40
+ * This method neither reads a clock nor checks the switch again, so an in-flight
41
+ * measured frame can finish even if a callback changes the switch.
42
+ * Throws `RangeError` for a negative or non-finite duration.
43
+ */
44
+ recordOperation(name: string, elapsedMs: number): void;
45
+ /** Clear all system and operation totals without changing the enabled state. */
46
+ resetSystems(): void;
47
+ /** Read system snapshots in current pipeline order, including zero-run systems. */
48
+ systems(): SystemTiming[];
49
+ private _clear;
50
+ private _operationSlot;
51
+ }
@@ -0,0 +1,88 @@
1
+ /** Opt-in world timing. Reading allocates snapshots; ticking reuses slots. */
2
+ export class WorldStats {
3
+ /** @internal Constructed by the owning world. */
4
+ constructor(_pipeline) {
5
+ this._pipeline = _pipeline;
6
+ this._enabled = false;
7
+ this._systems = new WeakMap();
8
+ this._operations = new Map();
9
+ this._generation = 0;
10
+ }
11
+ /** Whether system and extension timing is enabled. Off by default. */
12
+ get systemsEnabled() {
13
+ return this._enabled;
14
+ }
15
+ /** Stop measuring without discarding accumulated results. */
16
+ disableSystems() {
17
+ this._enabled = false;
18
+ }
19
+ /** Start measuring systems and extension operations on subsequent ticks. */
20
+ enableSystems() {
21
+ this._enabled = true;
22
+ }
23
+ /** Read extension-operation snapshots in first-seen order; empty before measurement. */
24
+ operations() {
25
+ return Array.from(this._operations.values(), (slot) => ({ ...slot }));
26
+ }
27
+ /**
28
+ * Record one completed extension operation, reusing its first-seen named slot.
29
+ * World extensions select their instrumented path with `systemsEnabled` once
30
+ * per frame, then call this method with a finite, non-negative duration in ms.
31
+ * This method neither reads a clock nor checks the switch again, so an in-flight
32
+ * measured frame can finish even if a callback changes the switch.
33
+ * Throws `RangeError` for a negative or non-finite duration.
34
+ */
35
+ recordOperation(name, elapsedMs) {
36
+ if (!Number.isFinite(elapsedMs) || elapsedMs < 0) {
37
+ throw new RangeError("operation duration must be finite and non-negative");
38
+ }
39
+ this._record(this._operationSlot(name), elapsedMs);
40
+ }
41
+ /** Clear all system and operation totals without changing the enabled state. */
42
+ resetSystems() {
43
+ this._generation++;
44
+ for (const slot of this._operations.values()) {
45
+ this._clear(slot);
46
+ }
47
+ }
48
+ /** Read system snapshots in current pipeline order, including zero-run systems. */
49
+ systems() {
50
+ return this._pipeline().map((system) => {
51
+ const { runs, totalMs, maxMs } = this._systemSlot(system);
52
+ return { system, runs, totalMs, maxMs };
53
+ });
54
+ }
55
+ /** @internal Reuse a slot after the system's first measured or read appearance. */
56
+ _systemSlot(system) {
57
+ let slot = this._systems.get(system);
58
+ if (!slot) {
59
+ slot = { system, runs: 0, totalMs: 0, maxMs: 0, generation: this._generation };
60
+ this._systems.set(system, slot);
61
+ }
62
+ if (slot.generation !== this._generation) {
63
+ this._clear(slot);
64
+ slot.generation = this._generation;
65
+ }
66
+ return slot;
67
+ }
68
+ /** @internal Aggregate a completed execution without allocating. */
69
+ _record(slot, elapsedMs) {
70
+ slot.runs++;
71
+ slot.totalMs += elapsedMs;
72
+ slot.maxMs = Math.max(slot.maxMs, elapsedMs);
73
+ }
74
+ _clear(slot) {
75
+ slot.runs = 0;
76
+ slot.totalMs = 0;
77
+ slot.maxMs = 0;
78
+ }
79
+ _operationSlot(name) {
80
+ let slot = this._operations.get(name);
81
+ if (!slot) {
82
+ slot = { name, runs: 0, totalMs: 0, maxMs: 0 };
83
+ this._operations.set(name, slot);
84
+ }
85
+ return slot;
86
+ }
87
+ }
88
+ //# sourceMappingURL=stats.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.js","sourceRoot":"","sources":["../../src/world/stats.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  export class Base {
2
2
  constructor() {
3
+ /** @internal Suspend cleanup policies only during dispose's infrastructure pass. */
4
+ this._tearingDownInfrastructure = false;
3
5
  /**
4
6
  * @internal Per-world registry mapping a component class to its
5
7
  * component-entity. The registry is per-world so the lookup stays off the
@@ -1 +1 @@
1
- {"version":3,"file":"world.base.js","sourceRoot":"","sources":["../../src/world/world.base.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,IAAI;IAAjB;QACE;;;;WAIG;QACa,mBAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE3D;;;WAGG;QACa,uBAAkB,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE/D;;;WAGG;QACa,0BAAqB,GAAG,IAAI,OAAO,EAAkB,CAAC;QAEtE;;;;WAIG;QACI,mBAAc,GAAuB,SAAS,CAAC;QAEtD;;;;WAIG;QACI,mBAAc,GAAuB,SAAS,CAAC;IACxD,CAAC;CAAA"}
1
+ {"version":3,"file":"world.base.js","sourceRoot":"","sources":["../../src/world/world.base.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,IAAI;IAAjB;QACE,oFAAoF;QAC7E,+BAA0B,GAAG,KAAK,CAAC;QAE1C;;;;WAIG;QACa,mBAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE3D;;;WAGG;QACa,uBAAkB,GAAG,IAAI,GAAG,EAAkB,CAAC;QAE/D;;;WAGG;QACa,0BAAqB,GAAG,IAAI,OAAO,EAAkB,CAAC;QAEtE;;;;WAIG;QACI,mBAAc,GAAuB,SAAS,CAAC;QAEtD;;;;WAIG;QACI,mBAAc,GAAuB,SAAS,CAAC;IACxD,CAAC;CAAA"}
@@ -11,6 +11,8 @@ export declare class Components extends Entities {
11
11
  * and its id is drawn from the `"component"` id pool (1–899 by default).
12
12
  * Subsequent calls with the same class return the same entity. The same class
13
13
  * may be registered independently in multiple worlds.
14
+ * A newly registered definition inherits the current scope parent immediately.
15
+ * Scopes must be entered outside deferred mode. Existing lookups do not reparent it.
14
16
  *
15
17
  * Pass an explicit numeric `ceid` to pin the component to a server-assigned
16
18
  * stable id instead of auto-allocating one.
@@ -71,6 +71,10 @@ export class Components extends Entities {
71
71
  const component = new Constructor(this, ceid, ComponentClass);
72
72
  this._entities.set(ceid, component);
73
73
  this._classRegistry.set(ComponentClass, component);
74
+ const parent = this._scope;
75
+ if (parent !== undefined) {
76
+ component.childOf(parent);
77
+ }
74
78
  // Component-definition entities are first-class entities: notify so any
75
79
  // live world-rooted query/term picks them up reactively (reflection),
76
80
  // exactly like an ordinary `world.entity()`.
@@ -1 +1 @@
1
- {"version":3,"file":"world.components.js","sourceRoot":"","sources":["../../src/world/world.components.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"world.components.js","sourceRoot":"","sources":["../../src/world/world.components.ts"],"names":[],"mappings":"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"}
@@ -6,61 +6,59 @@ import type { World } from "./world.js";
6
6
  * to `Pipeline`.
7
7
  */
8
8
  export declare class Disposal extends Transfers {
9
- /** `true` once {@link dispose} has started: what makes it idempotent and safe to re-enter. */
9
+ /** `true` once {@link dispose} has started: repeated top-level calls are idempotent. */
10
10
  private _disposeStarted;
11
11
  /**
12
- * End the world's life: destroy every entity the application and its modules created, so the
12
+ * End the world's life: destroy application entities, then all world infrastructure, so the
13
13
  * `remove` hooks of the components they carry release what those components own (render
14
14
  * objects, handles, timers). Call it when the world is finished — a scene or route change, a
15
15
  * hot reload — and drop the reference afterwards.
16
16
  *
17
- * Each entity goes through the ordinary destroy path, as if it had received
18
- * {@link Entity.destroy}: every component's `remove` hook fires exactly once, cleanup policies
19
- * and relationship cascades (`ChildOf`, `OwnedTarget`, `CleanupPolicy.Delete`) run as they
20
- * always do, and an entity a cascade or a hook has already destroyed is skipped, never
21
- * destroyed twice.
22
- *
23
- * ## What stays
24
- *
25
- * The infrastructure the world needs to stay coherent is not destroyed:
26
- *
27
- * - component and module entities (every {@link Component}),
28
- * - the entities behind queries and systems (they carry the {@link Query} component), and
29
- * - phase entities (they carry the {@link Phase} component).
30
- *
31
- * What those entities hold stays too: data stored on a component entity (a singleton set with
32
- * {@link set}) and any entity they still carry as a component key, which cannot be destroyed
33
- * while it is in use. Release such data yourself. {@link clearAllEntities} is the full wipe,
34
- * infrastructure included.
17
+ * Both passes use the existing entity destroy path and fire every component's
18
+ * `remove` hook exactly once. Application cleanup retains ordinary cleanup policies
19
+ * and cascades. Infrastructure teardown suspends cleanup policies: it removes a
20
+ * component from remaining carriers instead of throwing or deleting them, and
21
+ * destroys children explicitly rather than through ChildOf/OwnedTarget cascades.
35
22
  *
36
23
  * ## Order
37
24
  *
38
- * No order is specified among unrelated entities, so do not make a hook depend on one. The one
39
- * guarantee is that an entity used as a component key is destroyed after every entity that
40
- * carries it, because destroying it earlier would throw under the default cleanup policy.
25
+ * Queued application cleanup finishes before infrastructure teardown starts. Systems and
26
+ * queries go first in the infrastructure pass, then modules in reverse registration
27
+ * order, then the rest. Beyond these priorities the order is unspecified, not a
28
+ * resource dependency contract. Infrastructure carrier cycles are stripped, not
29
+ * reported as survivors. Nothing remains after successful disposal.
30
+ * {@link clearAllEntities} retains its own throwing behavior.
41
31
  *
42
- * ## Pending commands
32
+ * ## Call site
43
33
  *
44
- * Commands queued by an enclosing deferred scope are neither flushed early nor discarded: they
45
- * run first, in order, and the destruction follows them in the same queue, so a hook still fires
46
- * once per component instance however it was removed. An entity whose creation is still queued
47
- * is created, then destroyed. At top level nothing is queued and `dispose()` returns with the
48
- * application's entities gone. Called from a system, a hook or a {@link defer} block, it queues
49
- * the destruction like any `destroy()` in that situation, and the destruction completes when
50
- * the queue next drains: right after the running system, or at the end of the `defer` block.
34
+ * Call synchronously at top level: not inside {@link progress}, {@link defer}, a command drain
35
+ * or an entity's destroy. A refused call throws before teardown starts, leaving the world
36
+ * usable; call again after the enclosing operation finishes. Ordinary synchronous top-level
37
+ * `add`, `set` and `remove` hooks may call it. Cleanup and commands created by teardown hooks
38
+ * settle before this call returns.
51
39
  *
52
40
  * ## After disposal
53
41
  *
54
42
  * A disposed world must not be used again. Nothing enforces it — no other method checks, so
55
- * `entity()` and `progress()` stay as cheap as ever — but the world has no application state
56
- * left to run on. Calling `dispose()` again, from a hook included, does nothing. A hook that
57
- * creates entities while the world is being disposed leaves them behind. A system's `exit`
58
- * callback only queues its event, and a disposed world never runs the system again, so release
59
- * resources in `remove` hooks, which fire during the call. A hook that throws propagates out of
43
+ * mutations have no disposal guard — but the world has no state left to run on. Calling
44
+ * `dispose()` again at top level does nothing. The call-site rule still applies during teardown.
45
+ * Do not create entities indefinitely from teardown hooks: the final pass repeats while it can
46
+ * destroy more entities. Release resources in `remove` hooks, not system `exit` callbacks,
47
+ * which will not run after teardown. A hook that throws propagates out of
60
48
  * the call and leaves the world partial, as it does from any destroy: treat it as a programming
61
49
  * error.
50
+ *
51
+ * @throws An `Error` for a forbidden call site before any disposal mutation,
52
+ * an internal-invariant error if infrastructure teardown makes no progress,
53
+ * or an error propagated by a teardown hook. The latter errors leave the world partial.
62
54
  */
63
55
  dispose(this: World): void;
56
+ /**
57
+ * Validate the disposal call site before teardown changes any state.
58
+ * Subclasses overriding {@link dispose} must call this before starting their own cleanup.
59
+ * @throws An `Error` inside a frame, deferred scope, command drain or entity destroy.
60
+ */
61
+ protected _assertDisposalAllowed(): void;
64
62
  /**
65
63
  * The entities {@link dispose} destroys, in an order that lets every destroy succeed: those the
66
64
  * application created, with each one used as a component key after its carriers.
@@ -1,6 +1,8 @@
1
1
  import { Component } from "../component.js";
2
2
  import { Phase } from "../modules/pipeline.js";
3
3
  import { Query } from "../query/index.js";
4
+ import { Module } from "../module.js";
5
+ import { carriersFirst } from "./destroy-order.js";
4
6
  import { Transfers } from "./world.transfers.js";
5
7
  /**
6
8
  * World teardown. It is a layer of its own rather than a method of `World`, because the layers
@@ -10,73 +12,117 @@ import { Transfers } from "./world.transfers.js";
10
12
  export class Disposal extends Transfers {
11
13
  constructor() {
12
14
  super(...arguments);
13
- /** `true` once {@link dispose} has started: what makes it idempotent and safe to re-enter. */
15
+ /** `true` once {@link dispose} has started: repeated top-level calls are idempotent. */
14
16
  this._disposeStarted = false;
15
17
  }
16
18
  /**
17
- * End the world's life: destroy every entity the application and its modules created, so the
19
+ * End the world's life: destroy application entities, then all world infrastructure, so the
18
20
  * `remove` hooks of the components they carry release what those components own (render
19
21
  * objects, handles, timers). Call it when the world is finished — a scene or route change, a
20
22
  * hot reload — and drop the reference afterwards.
21
23
  *
22
- * Each entity goes through the ordinary destroy path, as if it had received
23
- * {@link Entity.destroy}: every component's `remove` hook fires exactly once, cleanup policies
24
- * and relationship cascades (`ChildOf`, `OwnedTarget`, `CleanupPolicy.Delete`) run as they
25
- * always do, and an entity a cascade or a hook has already destroyed is skipped, never
26
- * destroyed twice.
27
- *
28
- * ## What stays
29
- *
30
- * The infrastructure the world needs to stay coherent is not destroyed:
31
- *
32
- * - component and module entities (every {@link Component}),
33
- * - the entities behind queries and systems (they carry the {@link Query} component), and
34
- * - phase entities (they carry the {@link Phase} component).
35
- *
36
- * What those entities hold stays too: data stored on a component entity (a singleton set with
37
- * {@link set}) and any entity they still carry as a component key, which cannot be destroyed
38
- * while it is in use. Release such data yourself. {@link clearAllEntities} is the full wipe,
39
- * infrastructure included.
24
+ * Both passes use the existing entity destroy path and fire every component's
25
+ * `remove` hook exactly once. Application cleanup retains ordinary cleanup policies
26
+ * and cascades. Infrastructure teardown suspends cleanup policies: it removes a
27
+ * component from remaining carriers instead of throwing or deleting them, and
28
+ * destroys children explicitly rather than through ChildOf/OwnedTarget cascades.
40
29
  *
41
30
  * ## Order
42
31
  *
43
- * No order is specified among unrelated entities, so do not make a hook depend on one. The one
44
- * guarantee is that an entity used as a component key is destroyed after every entity that
45
- * carries it, because destroying it earlier would throw under the default cleanup policy.
32
+ * Queued application cleanup finishes before infrastructure teardown starts. Systems and
33
+ * queries go first in the infrastructure pass, then modules in reverse registration
34
+ * order, then the rest. Beyond these priorities the order is unspecified, not a
35
+ * resource dependency contract. Infrastructure carrier cycles are stripped, not
36
+ * reported as survivors. Nothing remains after successful disposal.
37
+ * {@link clearAllEntities} retains its own throwing behavior.
46
38
  *
47
- * ## Pending commands
39
+ * ## Call site
48
40
  *
49
- * Commands queued by an enclosing deferred scope are neither flushed early nor discarded: they
50
- * run first, in order, and the destruction follows them in the same queue, so a hook still fires
51
- * once per component instance however it was removed. An entity whose creation is still queued
52
- * is created, then destroyed. At top level nothing is queued and `dispose()` returns with the
53
- * application's entities gone. Called from a system, a hook or a {@link defer} block, it queues
54
- * the destruction like any `destroy()` in that situation, and the destruction completes when
55
- * the queue next drains: right after the running system, or at the end of the `defer` block.
41
+ * Call synchronously at top level: not inside {@link progress}, {@link defer}, a command drain
42
+ * or an entity's destroy. A refused call throws before teardown starts, leaving the world
43
+ * usable; call again after the enclosing operation finishes. Ordinary synchronous top-level
44
+ * `add`, `set` and `remove` hooks may call it. Cleanup and commands created by teardown hooks
45
+ * settle before this call returns.
56
46
  *
57
47
  * ## After disposal
58
48
  *
59
49
  * A disposed world must not be used again. Nothing enforces it — no other method checks, so
60
- * `entity()` and `progress()` stay as cheap as ever — but the world has no application state
61
- * left to run on. Calling `dispose()` again, from a hook included, does nothing. A hook that
62
- * creates entities while the world is being disposed leaves them behind. A system's `exit`
63
- * callback only queues its event, and a disposed world never runs the system again, so release
64
- * resources in `remove` hooks, which fire during the call. A hook that throws propagates out of
50
+ * mutations have no disposal guard — but the world has no state left to run on. Calling
51
+ * `dispose()` again at top level does nothing. The call-site rule still applies during teardown.
52
+ * Do not create entities indefinitely from teardown hooks: the final pass repeats while it can
53
+ * destroy more entities. Release resources in `remove` hooks, not system `exit` callbacks,
54
+ * which will not run after teardown. A hook that throws propagates out of
65
55
  * the call and leaves the world partial, as it does from any destroy: treat it as a programming
66
56
  * error.
57
+ *
58
+ * @throws An `Error` for a forbidden call site before any disposal mutation,
59
+ * an internal-invariant error if infrastructure teardown makes no progress,
60
+ * or an error propagated by a teardown hook. The latter errors leave the world partial.
67
61
  */
68
62
  dispose() {
63
+ this._assertDisposalAllowed();
69
64
  if (this._disposeStarted) {
70
65
  return;
71
66
  }
72
67
  this._disposeStarted = true;
73
- // The destroys are queued commands, as `Entity.destroy()` makes them in a deferred scope, so a
74
- // queue that is already filling runs ahead of them. At top level this scope drains them now.
75
- this.defer(() => {
76
- for (const entity of this._destroyOrder()) {
77
- this._enqueue({ kind: 4 /* CommandKind.Destroy */, entity });
68
+ // Settle application hooks and any entities they create before removing infrastructure.
69
+ let applications = this._destroyOrder();
70
+ while (applications.length > 0) {
71
+ this.defer(() => {
72
+ for (const entity of applications) {
73
+ this._enqueue({ kind: 4 /* CommandKind.Destroy */, entity });
74
+ }
75
+ });
76
+ applications = this._destroyOrder();
77
+ }
78
+ this._tearingDownInfrastructure = true;
79
+ try {
80
+ while (this._entities.size > 0) {
81
+ const entities = this._entities.values().filter((entity) => !entity.destroyed);
82
+ // The class registry is insertion-ordered; the dense entity map swaps entries on deletion.
83
+ const modules = [...this._classRegistry.values()]
84
+ .filter((entity) => entity instanceof Module && !entity.destroyed)
85
+ .reverse();
86
+ const queryEid = this.getEntity(Query)?.eid;
87
+ const queries = entities.filter((entity) => queryEid !== undefined && entity.has(queryEid));
88
+ const priority = new Set([...queries, ...modules]);
89
+ const ordered = [
90
+ ...queries,
91
+ ...modules,
92
+ ...entities.filter((entity) => !priority.has(entity)),
93
+ ];
94
+ let destroyed = 0;
95
+ for (const entity of ordered) {
96
+ if (!entity.destroyed) {
97
+ entity._destroy();
98
+ if (entity.destroyed) {
99
+ destroyed++;
100
+ }
101
+ this.flush();
102
+ }
103
+ }
104
+ this.flush();
105
+ if (destroyed === 0 && this._entities.size > 0) {
106
+ throw new Error(`Internal invariant: infrastructure teardown made no progress; registered entities: ${this._entities
107
+ .values()
108
+ .map((entity) => `${entity.eid} (${entity.meta.Class.name}, destroyed=${entity.destroyed})`)
109
+ .join(", ")}`);
110
+ }
78
111
  }
79
- });
112
+ }
113
+ finally {
114
+ this._tearingDownInfrastructure = false;
115
+ }
116
+ }
117
+ /**
118
+ * Validate the disposal call site before teardown changes any state.
119
+ * Subclasses overriding {@link dispose} must call this before starting their own cleanup.
120
+ * @throws An `Error` inside a frame, deferred scope, command drain or entity destroy.
121
+ */
122
+ _assertDisposalAllowed() {
123
+ if (this.deferred || this._frameInProgress || this._deletionScope) {
124
+ throw new Error("dispose() must be called outside progress(), defer, a command drain or an entity's destroy");
125
+ }
80
126
  }
81
127
  /**
82
128
  * The entities {@link dispose} destroys, in an order that lets every destroy succeed: those the
@@ -89,58 +135,7 @@ export class Disposal extends Transfers {
89
135
  const isInfrastructure = (entity) => entity instanceof Component ||
90
136
  (queryEid !== undefined && entity.has(queryEid)) ||
91
137
  (phaseEid !== undefined && entity.has(phaseEid));
92
- const entities = this._entities.values();
93
- // In a deferred scope an entity created earlier is only queued, not registered yet. Its
94
- // creation runs ahead of the destroy that is queued after it.
95
- for (const command of this._commandQueue) {
96
- if (command.kind === 0 /* CommandKind.CreateEntity */ &&
97
- !command.entity.destroyed &&
98
- this._entities.get(command.entity.eid) !== command.entity) {
99
- entities.push(command.entity);
100
- }
101
- }
102
- return carriersFirst(entities.filter((entity) => !isInfrastructure(entity)), isInfrastructure);
103
- }
104
- }
105
- /**
106
- * Order `entities` so that an entity used as a component key follows every entity that carries it.
107
- *
108
- * A key destroyed while still carried throws under the default cleanup policy, so carriers go
109
- * first. A key that something outside `entities` carries, which is infrastructure in practice, or
110
- * that is caught in a cycle, cannot be ordered and is left out: destroying it would throw or strip
111
- * the infrastructure.
112
- */
113
- function carriersFirst(entities, isInfrastructure) {
114
- const ordered = [];
115
- const keys = new Set();
116
- for (const entity of entities) {
117
- if (entity.instanceCount() === 0) {
118
- ordered.push(entity);
119
- }
120
- else {
121
- keys.add(entity);
122
- }
123
- }
124
- let progressed = keys.size > 0;
125
- while (progressed) {
126
- progressed = false;
127
- for (const key of keys) {
128
- if (!isCarried(key, keys, isInfrastructure)) {
129
- ordered.push(key);
130
- keys.delete(key);
131
- progressed = true;
132
- }
133
- }
134
- }
135
- return ordered;
136
- }
137
- /** Whether `key` is still carried by another key that waits to be ordered, or by infrastructure. */
138
- function isCarried(key, waiting, isInfrastructure) {
139
- for (const carrier of key.meta._carriers ?? []) {
140
- if (carrier !== key && (waiting.has(carrier) || isInfrastructure(carrier))) {
141
- return true;
142
- }
138
+ return carriersFirst(this._entities.values().filter((entity) => !entity.destroyed && !isInfrastructure(entity)));
143
139
  }
144
- return false;
145
140
  }
146
141
  //# sourceMappingURL=world.disposal.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"world.disposal.js","sourceRoot":"","sources":["../../src/world/world.disposal.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"world.disposal.js","sourceRoot":"","sources":["../../src/world/world.disposal.ts"],"names":[],"mappings":"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"}
@@ -31,6 +31,26 @@ export declare class Entities extends Deferred {
31
31
  * A foreign `Entity` ref throws (see {@link getEntity}).
32
32
  */
33
33
  entity(this: World, ref: ComponentRef): Entity;
34
- /** Destroy every entity currently tracked by the world. */
34
+ /**
35
+ * Destroy every tracked entity, ordering both carriers and ChildOf children
36
+ * before their keys/parents. Retains ordinary cleanup policies; throws when
37
+ * dependency cycles prevent safe destruction (unlike dispose's final pass).
38
+ */
35
39
  clearAllEntities(): void;
40
+ /**
41
+ * Parent entities created synchronously in `fn`'s body to `parent` through ChildOf.
42
+ * Includes components, modules, queries, systems and ordinary entities. Existing
43
+ * lookups are not reparented; an explicit childOf call replaces the scope parent.
44
+ * Nested scopes restore their caller in finally, even when fn throws.
45
+ * Entities created by deferred callbacks inside the body are not parented:
46
+ * query.forEach, enter backfill during query build, or an explicit world.defer.
47
+ * These are run-time data; the same callback outside the scope would not parent
48
+ * them either. For example, health bars created per existing ship by an enter
49
+ * or forEach callback remain roots, unlike definitions created directly in init.
50
+ * Throws while deferred: call outside progress() and defer, not during a command
51
+ * drain. Schedule lazy module loading after progress() returns.
52
+ * The parent must be a live entity of this world. This is a synchronous scope,
53
+ * not an async context; it does not change deferral or catch callback errors.
54
+ */
55
+ scope(this: World, parent: Entity, fn: () => void): void;
36
56
  }