@oasys/oecs 0.4.0 → 0.5.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/CHANGELOG.md +553 -0
- package/README.md +42 -31
- package/dist/core/ecs/access_check.d.cts +77 -0
- package/dist/core/ecs/access_check.d.ts +8 -8
- package/dist/core/ecs/access_check.d.ts.map +1 -1
- package/dist/core/ecs/archetype.d.cts +486 -0
- package/dist/core/ecs/archetype.d.ts +23 -11
- package/dist/core/ecs/archetype.d.ts.map +1 -1
- package/dist/core/ecs/archetype_graph.d.cts +96 -0
- package/dist/core/ecs/archetype_graph.d.ts +96 -0
- package/dist/core/ecs/archetype_graph.d.ts.map +1 -0
- package/dist/core/ecs/builtin_relations.d.cts +43 -0
- package/dist/core/ecs/builtin_relations.d.ts +8 -8
- package/dist/core/ecs/builtin_relations.d.ts.map +1 -1
- package/dist/core/ecs/command_log.d.cts +116 -0
- package/dist/core/ecs/command_log.d.ts +13 -10
- package/dist/core/ecs/command_log.d.ts.map +1 -1
- package/dist/core/ecs/component.d.cts +176 -0
- package/dist/core/ecs/component.d.ts +75 -5
- package/dist/core/ecs/component.d.ts.map +1 -1
- package/dist/core/ecs/compute_backend.d.cts +37 -0
- package/dist/core/ecs/compute_backend.d.ts +2 -2
- package/dist/core/ecs/debug_names.d.cts +7 -0
- package/dist/core/ecs/debug_names.d.ts +7 -0
- package/dist/core/ecs/debug_names.d.ts.map +1 -0
- package/dist/core/ecs/deferred_commands.d.cts +55 -0
- package/dist/core/ecs/deferred_commands.d.ts +55 -0
- package/dist/core/ecs/deferred_commands.d.ts.map +1 -0
- package/dist/core/ecs/dispatch_trace.d.cts +93 -0
- package/dist/core/ecs/dispatch_trace.d.ts +3 -3
- package/dist/core/ecs/ecs.d.cts +562 -0
- package/dist/core/ecs/ecs.d.ts +314 -285
- package/dist/core/ecs/ecs.d.ts.map +1 -1
- package/dist/core/ecs/ecs_memory.d.cts +179 -0
- package/dist/core/ecs/ecs_memory.d.ts +1 -1
- package/dist/core/ecs/entity.d.cts +34 -0
- package/dist/core/ecs/entity.d.ts +7 -1
- package/dist/core/ecs/entity.d.ts.map +1 -1
- package/dist/core/ecs/entity_allocator.d.cts +59 -0
- package/dist/core/ecs/entity_allocator.d.ts +59 -0
- package/dist/core/ecs/entity_allocator.d.ts.map +1 -0
- package/dist/core/ecs/event.d.cts +79 -0
- package/dist/core/ecs/event.d.ts +36 -11
- package/dist/core/ecs/event.d.ts.map +1 -1
- package/dist/core/ecs/event_registry.d.cts +22 -0
- package/dist/core/ecs/event_registry.d.ts +22 -0
- package/dist/core/ecs/event_registry.d.ts.map +1 -0
- package/dist/core/ecs/facades.d.cts +158 -0
- package/dist/core/ecs/facades.d.ts +158 -0
- package/dist/core/ecs/facades.d.ts.map +1 -0
- package/dist/core/ecs/frame_stepper.d.cts +58 -0
- package/dist/core/ecs/frame_stepper.d.ts +58 -0
- package/dist/core/ecs/frame_stepper.d.ts.map +1 -0
- package/dist/core/ecs/frame_trace.d.cts +133 -0
- package/dist/core/ecs/frame_trace.d.ts +5 -5
- package/dist/core/ecs/frame_trace.d.ts.map +1 -1
- package/dist/core/ecs/host_commands.d.cts +274 -0
- package/dist/core/ecs/host_commands.d.ts +53 -31
- package/dist/core/ecs/host_commands.d.ts.map +1 -1
- package/dist/core/ecs/index.d.cts +46 -0
- package/dist/core/ecs/index.d.ts +45 -40
- package/dist/core/ecs/index.d.ts.map +1 -1
- package/dist/core/ecs/observer.d.cts +173 -0
- package/dist/core/ecs/observer.d.ts +43 -12
- package/dist/core/ecs/observer.d.ts.map +1 -1
- package/dist/core/ecs/query.d.cts +718 -0
- package/dist/core/ecs/query.d.ts +251 -90
- package/dist/core/ecs/query.d.ts.map +1 -1
- package/dist/core/ecs/ref.d.cts +31 -0
- package/dist/core/ecs/ref.d.ts +3 -3
- package/dist/core/ecs/relation.d.cts +243 -0
- package/dist/core/ecs/relation.d.ts +26 -14
- package/dist/core/ecs/relation.d.ts.map +1 -1
- package/dist/core/ecs/relation_service.d.cts +235 -0
- package/dist/core/ecs/relation_service.d.ts +235 -0
- package/dist/core/ecs/relation_service.d.ts.map +1 -0
- package/dist/core/ecs/resource.d.cts +53 -0
- package/dist/core/ecs/resource.d.ts +25 -5
- package/dist/core/ecs/resource.d.ts.map +1 -1
- package/dist/core/ecs/resource_registry.d.cts +20 -0
- package/dist/core/ecs/resource_registry.d.ts +20 -0
- package/dist/core/ecs/resource_registry.d.ts.map +1 -0
- package/dist/core/ecs/resume.d.cts +85 -0
- package/dist/core/ecs/resume.d.ts +6 -6
- package/dist/core/ecs/resume.d.ts.map +1 -1
- package/dist/core/ecs/run_condition.d.cts +81 -0
- package/dist/core/ecs/run_condition.d.ts +15 -9
- package/dist/core/ecs/run_condition.d.ts.map +1 -1
- package/dist/core/ecs/schedule.d.cts +142 -0
- package/dist/core/ecs/schedule.d.ts +15 -6
- package/dist/core/ecs/schedule.d.ts.map +1 -1
- package/dist/core/ecs/snapshot_service.d.cts +94 -0
- package/dist/core/ecs/snapshot_service.d.ts +94 -0
- package/dist/core/ecs/snapshot_service.d.ts.map +1 -0
- package/dist/core/ecs/sparse_store.d.cts +110 -0
- package/dist/core/ecs/sparse_store.d.ts +5 -2
- package/dist/core/ecs/sparse_store.d.ts.map +1 -1
- package/dist/core/ecs/store.d.cts +950 -0
- package/dist/core/ecs/store.d.ts +143 -342
- package/dist/core/ecs/store.d.ts.map +1 -1
- package/dist/core/ecs/store_layout_listener.d.cts +23 -0
- package/dist/core/ecs/system.d.cts +250 -0
- package/dist/core/ecs/system.d.ts +138 -22
- package/dist/core/ecs/system.d.ts.map +1 -1
- package/dist/{utils/arrays.d.ts → core/ecs/utils/arrays.d.cts} +1 -0
- package/dist/core/ecs/utils/error.d.cts +61 -0
- package/dist/core/ecs/utils/error.d.ts +12 -2
- package/dist/core/ecs/utils/error.d.ts.map +1 -1
- package/dist/core/store/action_ring.d.cts +136 -0
- package/dist/core/store/allocator.d.cts +238 -0
- package/dist/core/store/allocator.d.ts.map +1 -1
- package/dist/core/store/buffer_backed_column.d.cts +69 -0
- package/dist/core/store/buffer_backed_column.d.ts +2 -2
- package/dist/core/store/column_store.d.cts +273 -0
- package/dist/core/store/column_store.d.ts +18 -10
- package/dist/core/store/column_store.d.ts.map +1 -1
- package/dist/core/store/command_dispatch.d.cts +52 -0
- package/dist/core/store/command_ring.d.cts +107 -0
- package/dist/core/store/descriptor.d.cts +80 -0
- package/dist/core/store/descriptor.d.ts +1 -1
- package/dist/core/store/descriptor.d.ts.map +1 -1
- package/dist/core/store/entity_index.d.cts +108 -0
- package/dist/core/store/event_ring.d.cts +95 -0
- package/dist/core/store/extend.d.cts +39 -0
- package/dist/core/store/extend.d.ts +3 -73
- package/dist/core/store/extend.d.ts.map +1 -1
- package/dist/core/store/grow.d.cts +34 -0
- package/dist/core/store/grow.d.ts +4 -9
- package/dist/core/store/grow.d.ts.map +1 -1
- package/dist/core/store/header.d.cts +64 -0
- package/dist/core/store/header.d.ts +1 -1
- package/dist/core/store/header.d.ts.map +1 -1
- package/dist/core/store/index.d.cts +17 -0
- package/dist/core/store/index.d.ts +16 -15
- package/dist/core/store/index.d.ts.map +1 -1
- package/dist/core/store/layout_ops.d.cts +152 -0
- package/dist/core/store/layout_ops.d.ts +152 -0
- package/dist/core/store/layout_ops.d.ts.map +1 -0
- package/dist/core/store/region_table.d.cts +74 -0
- package/dist/core/store/region_table.d.ts +1 -1
- package/dist/core/store/region_table.d.ts.map +1 -1
- package/dist/core/store/snapshot.d.cts +43 -0
- package/dist/core/store/snapshot.d.ts +2 -2
- package/dist/core/store/state_hash.d.cts +38 -0
- package/dist/core/store/state_hash.d.ts +1 -1
- package/dist/core/store/store_regions.d.cts +38 -0
- package/dist/core/store/store_regions.d.ts +1 -1
- package/dist/core/store/vendored_abi/abi.d.ts +43 -0
- package/dist/core/store/{__generated__ → vendored_abi}/abi.d.ts.map +1 -1
- package/dist/dev_flag.d.cts +17 -0
- package/dist/dev_flag.d.ts +17 -0
- package/dist/dev_flag.d.ts.map +1 -0
- package/dist/extensions/editor/editor.d.cts +170 -0
- package/dist/extensions/editor/editor.d.ts +43 -22
- package/dist/extensions/editor/editor.d.ts.map +1 -1
- package/dist/extensions/editor/field_handle.d.cts +40 -0
- package/dist/extensions/editor/field_handle.d.ts +8 -3
- package/dist/extensions/editor/field_handle.d.ts.map +1 -1
- package/dist/extensions/editor/index.cjs +1 -1
- package/dist/extensions/editor/index.d.cts +21 -0
- package/dist/extensions/editor/index.d.ts +2 -2
- package/dist/extensions/editor/index.js +116 -88
- package/dist/extensions/reactive/ecs_sync.d.cts +214 -0
- package/dist/extensions/reactive/ecs_sync.d.ts +26 -22
- package/dist/extensions/reactive/ecs_sync.d.ts.map +1 -1
- package/dist/extensions/reactive/index.cjs +1 -1
- package/dist/extensions/reactive/index.d.cts +23 -0
- package/dist/extensions/reactive/index.d.ts +1 -1
- package/dist/extensions/reactive/index.js +106 -116
- package/dist/extensions/solid/index.cjs +1 -1
- package/dist/extensions/solid/index.d.cts +6 -0
- package/dist/extensions/solid/index.d.ts +2 -2
- package/dist/extensions/solid/index.js +8 -4
- package/dist/extensions/solid/kernel_solid.d.cts +45 -0
- package/dist/extensions/solid/kernel_solid.d.ts +6 -3
- package/dist/extensions/solid/kernel_solid.d.ts.map +1 -1
- package/dist/host_commands-BF8QMi3c.cjs +1 -0
- package/dist/host_commands-i4cAeyL5.js +2318 -0
- package/dist/index.cjs +1 -2
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +50 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4469 -5933
- package/dist/internal.cjs +2 -0
- package/dist/internal.d.cts +20 -0
- package/dist/internal.d.ts +20 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +141 -0
- package/dist/primitives.cjs +1 -1
- package/dist/primitives.d.cts +18 -0
- package/dist/primitives.d.ts +6 -6
- package/dist/primitives.js +24 -23
- package/dist/reactive/array.d.ts +24 -0
- package/dist/reactive/array.d.ts.map +1 -0
- package/dist/reactive/index.cjs +1 -0
- package/dist/reactive/index.d.cts +11 -0
- package/dist/reactive/index.d.ts +11 -0
- package/dist/reactive/index.d.ts.map +1 -0
- package/dist/reactive/index.js +18 -0
- package/dist/reactive/interop.d.cts +19 -0
- package/dist/{core/reactive → reactive}/interop.d.ts +1 -1
- package/dist/reactive/interop.d.ts.map +1 -0
- package/dist/reactive/kernel.d.cts +73 -0
- package/dist/{core/reactive → reactive}/kernel.d.ts +5 -3
- package/dist/reactive/kernel.d.ts.map +1 -0
- package/dist/reactive/map.d.ts +16 -0
- package/dist/reactive/map.d.ts.map +1 -0
- package/dist/reactive/shallow.d.cts +11 -0
- package/dist/reactive/shallow.d.ts +11 -0
- package/dist/reactive/shallow.d.ts.map +1 -0
- package/dist/reactive/struct.d.cts +10 -0
- package/dist/{core/reactive → reactive}/struct.d.ts +1 -1
- package/dist/reactive/struct.d.ts.map +1 -0
- package/dist/shallow-DQ8aG51_.cjs +1 -0
- package/dist/shallow-DfnAm9vG.js +150 -0
- package/dist/shared-BU1Cd40h.js +103 -0
- package/dist/shared-BymrGTyR.cjs +1 -0
- package/dist/shared.cjs +1 -1
- package/dist/shared.d.cts +22 -0
- package/dist/shared.d.ts +2 -2
- package/dist/shared.js +1 -1
- package/dist/topological_sort-BDvEyb9W.cjs +1 -0
- package/dist/topological_sort-DK6EjpWa.js +135 -0
- package/dist/type_primitives/assertions.d.cts +32 -0
- package/dist/type_primitives/assertions.d.ts +13 -1
- package/dist/type_primitives/assertions.d.ts.map +1 -1
- package/dist/type_primitives/binary_heap/binary_heap.d.cts +37 -0
- package/dist/type_primitives/bitset/bitset.d.cts +54 -0
- package/dist/type_primitives/brand.d.cts +23 -0
- package/dist/type_primitives/error.d.cts +16 -0
- package/dist/type_primitives/error.d.ts +4 -3
- package/dist/type_primitives/error.d.ts.map +1 -1
- package/dist/type_primitives/index.d.cts +13 -0
- package/dist/type_primitives/index.d.ts +9 -9
- package/dist/type_primitives/sparse_map/sparse_map.d.cts +30 -0
- package/dist/type_primitives/sparse_set/sparse_set.d.cts +28 -0
- package/dist/type_primitives/topological_sort/topological_sort.d.cts +29 -0
- package/dist/type_primitives/typed_arrays/typed_arrays.d.cts +127 -0
- package/dist/typed_arrays-BcXcnzB4.js +258 -0
- package/dist/typed_arrays-D3NmwtCG.cjs +1 -0
- package/dist/utils/error.d.cts +6 -0
- package/dist/version.d.cts +10 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.d.ts.map +1 -0
- package/package.json +102 -19
- package/dist/array-CxbPyiHO.cjs +0 -1
- package/dist/array-uFR7Q8fU.js +0 -132
- package/dist/core/reactive/array.d.ts.map +0 -1
- package/dist/core/reactive/index.cjs +0 -1
- package/dist/core/reactive/index.d.ts +0 -10
- package/dist/core/reactive/index.d.ts.map +0 -1
- package/dist/core/reactive/index.js +0 -17
- package/dist/core/reactive/interop.d.ts.map +0 -1
- package/dist/core/reactive/kernel.d.ts.map +0 -1
- package/dist/core/reactive/map.d.ts.map +0 -1
- package/dist/core/reactive/struct.d.ts.map +0 -1
- package/dist/log/console_sink.d.ts +0 -4
- package/dist/log/console_sink.d.ts.map +0 -1
- package/dist/log/index.d.ts +0 -3
- package/dist/log/index.d.ts.map +0 -1
- package/dist/log/logger.d.ts +0 -27
- package/dist/log/logger.d.ts.map +0 -1
- package/dist/shared-BXSZnxx4.cjs +0 -1
- package/dist/shared-C678TAPY.js +0 -99
- package/dist/topological_sort-DlRpSrxu.js +0 -391
- package/dist/topological_sort-WAT-VHb-.cjs +0 -1
- package/dist/utils/arrays.d.ts.map +0 -1
- package/dist/utils/constants.d.ts.map +0 -1
- /package/dist/{utils/constants.d.ts → core/ecs/utils/constants.d.cts} +0 -0
- /package/dist/core/store/{__generated__/abi.d.ts → vendored_abi/abi.d.cts} +0 -0
- /package/dist/{core/reactive/array.d.ts → reactive/array.d.cts} +0 -0
- /package/dist/{core/reactive/map.d.ts → reactive/map.d.cts} +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { BitSet, TypedArrayTag } from '../../type_primitives/index.js';
|
|
2
|
+
import { Archetype } from './archetype.js';
|
|
3
|
+
import { EntityID } from './entity.js';
|
|
4
|
+
import { RelationDef, RelationOptions, RelationStore } from './relation.js';
|
|
5
|
+
import { SparseComponentDef, SparseComponentID, SparseComponentStore } from './sparse_store.js';
|
|
6
|
+
/** What the relation service needs from `Store` — nothing more. The accessor
|
|
7
|
+
* members re-read the live field on every call, so capacity growth that
|
|
8
|
+
* reallocates `entityGenerations` / `entityArchetype` / `entityRow` is always
|
|
9
|
+
* observed; never cache their return values across mutations. */
|
|
10
|
+
export interface RelationServiceHost {
|
|
11
|
+
isAlive(id: EntityID): boolean;
|
|
12
|
+
hasSparse(entityId: EntityID, def: SparseComponentDef): boolean;
|
|
13
|
+
pushSparseStore(fieldNames: string[], fieldTypes: TypedArrayTag[]): SparseComponentDef;
|
|
14
|
+
sparseStoreOf(def: SparseComponentDef): SparseComponentStore;
|
|
15
|
+
sparseStores(): readonly SparseComponentStore[];
|
|
16
|
+
entityGenerations(): Int32Array;
|
|
17
|
+
entityArchetype(): Int32Array;
|
|
18
|
+
entityRow(): Int32Array;
|
|
19
|
+
archetypes(): readonly Archetype[];
|
|
20
|
+
forEachSparseMatch(include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, sparseInclude: readonly SparseComponentID[], sparseExclude: readonly SparseComponentID[], denseArchetypes: readonly Archetype[], cb: (entityId: EntityID) => void, includeDisabled: boolean): void;
|
|
21
|
+
}
|
|
22
|
+
export declare class RelationService {
|
|
23
|
+
private readonly host;
|
|
24
|
+
private readonly relations;
|
|
25
|
+
private _hasTargetCleanup;
|
|
26
|
+
private readonly _hierarchyRadixOut;
|
|
27
|
+
private readonly _hierarchyRadixC0;
|
|
28
|
+
private readonly _hierarchyRadixC1;
|
|
29
|
+
constructor(host: RelationServiceHost);
|
|
30
|
+
/** The registry, indexed by RelationID — the read-only view the Store's
|
|
31
|
+
* `stateHash` / snapshot / restore paths iterate. */
|
|
32
|
+
get stores(): readonly RelationStore[];
|
|
33
|
+
/** Number of registered relations. */
|
|
34
|
+
get count(): number;
|
|
35
|
+
/** Whether any relation registered a non-`orphan` `onDeleteTarget` policy —
|
|
36
|
+
* the destroy paths' gate for `cleanupTarget` (#473). */
|
|
37
|
+
get hasTargetCleanup(): boolean;
|
|
38
|
+
/** Register a relation kind. `exclusive` (the default) → one target per
|
|
39
|
+
* source, stored in a backing `{ target: f64 }` sparse component, so the
|
|
40
|
+
* forward index rides the sparse store and inherits query membership (#469),
|
|
41
|
+
* `stateHash` + snapshot/restore (#470) for free. `multi` → a set of
|
|
42
|
+
* targets per source, backed by a sparse tag for membership plus a side
|
|
43
|
+
* forward index the relation owns. The two cardinalities are mutually
|
|
44
|
+
* exclusive. `onDeleteTarget` selects the cleanup policy run when a target
|
|
45
|
+
* is destroyed — `delete` (cascade-destroy sources), `clear` (drop the link,
|
|
46
|
+
* sources survive), or `orphan` (default: leave it dangling, #473). See
|
|
47
|
+
* ADR-0011 and `relation.ts`.
|
|
48
|
+
*
|
|
49
|
+
* The backing sparse store is resolved and handed to the relation so it can
|
|
50
|
+
* drive forward/membership rows directly — the cardinality-specific
|
|
51
|
+
* interaction is `ExclusiveRelationStore` / `MultiRelationStore`'s, not a
|
|
52
|
+
* branch here (#498). */
|
|
53
|
+
registerRelation(opts?: RelationOptions): RelationDef;
|
|
54
|
+
private relationOf;
|
|
55
|
+
/** Add a `(R, tgt)` pair to `src`. No archetype transition. Exclusive:
|
|
56
|
+
* replaces any existing target (engine-enforced one-per-source), a no-op if
|
|
57
|
+
* `tgt` is already the target. Multi: adds `tgt` to the set, a no-op if
|
|
58
|
+
* already present. A dead `src` *or* `tgt` is caller error: it throws in
|
|
59
|
+
* `DEV` and is a no-op in production — symmetric, so a production build
|
|
60
|
+
* never links a reverse-index entry keyed by a destroyed handle (#495). */
|
|
61
|
+
addRelation(src: EntityID, def: RelationDef, tgt: EntityID): void;
|
|
62
|
+
/** Remove a `(R, tgt)` pair from `src`. No archetype transition. Exclusive:
|
|
63
|
+
* `tgt` is optional and the removal is a no-op when it names a target other
|
|
64
|
+
* than the current one. Multi: omitting `tgt` removes *all* of `src`'s
|
|
65
|
+
* targets; passing one removes just that pair (dropping membership when the
|
|
66
|
+
* set empties). */
|
|
67
|
+
removeRelation(src: EntityID, def: RelationDef, tgt?: EntityID): void;
|
|
68
|
+
/** The single target of `src` under an exclusive relation, or `undefined`.
|
|
69
|
+
* Throws in `DEV` on a multi-target relation (use `targetsOf`). */
|
|
70
|
+
targetOf(src: EntityID, def: RelationDef): EntityID | undefined;
|
|
71
|
+
/** All targets of `src` under `R` — one or zero for exclusive, the full set
|
|
72
|
+
* for multi — ascending by id. */
|
|
73
|
+
targetsOf(src: EntityID, def: RelationDef): EntityID[];
|
|
74
|
+
/** Sources that point at `tgt` under `R` (the reverse index), ascending by
|
|
75
|
+
* id; empty when none. */
|
|
76
|
+
sourcesOf(tgt: EntityID, def: RelationDef): EntityID[];
|
|
77
|
+
/** Whether `src` holds any pair under `R`. */
|
|
78
|
+
hasRelation(src: EntityID, def: RelationDef): boolean;
|
|
79
|
+
/** All `(source, target)` pairs of relation `R` — the `(R, *)` wildcard
|
|
80
|
+
* (#472). Sources are emitted in **canonical entity-index order** (the #470
|
|
81
|
+
* determinism convention): exclusive relations ride the backing sparse
|
|
82
|
+
* store's `canonicalIndices`, multi relations ride the same
|
|
83
|
+
* `forEachCanonicalTargetSet` traversal `stateHash` / `snapshotRelations`
|
|
84
|
+
* use; a multi source's targets follow ascending by id. Empty when the
|
|
85
|
+
* relation holds no pairs. Cold path — allocates the result array (and, for
|
|
86
|
+
* multi, sorts each source's target set); not for per-tick use. The
|
|
87
|
+
* point-query forms are `targetOf` / `targetsOf`. */
|
|
88
|
+
pairsOf(def: RelationDef): readonly (readonly [EntityID, EntityID])[];
|
|
89
|
+
/** Every `(relation, source)` pointing at `tgt`, across **all** registered
|
|
90
|
+
* relation kinds — the `(*, T)` wildcard (#472). Walks the relation registry
|
|
91
|
+
* in id order (each relation's reverse index already returns sources
|
|
92
|
+
* ascending by id), so the result is ordered by relation id then source id.
|
|
93
|
+
* Empty when nothing targets `tgt`. The single-relation form is
|
|
94
|
+
* `sourcesOf(tgt, def)`. */
|
|
95
|
+
sourcesOfAny(tgt: EntityID): readonly (readonly [RelationDef, EntityID])[];
|
|
96
|
+
/** The backing sparse component id of relation `R` — the membership store a
|
|
97
|
+
* `(R, *)` wildcard term (`Query.withRelation`) drives through the shared
|
|
98
|
+
* sparse-match path. Exclusive relations back a `{ target: f64 }` sparse
|
|
99
|
+
* component, multi a tag; both carry per-source membership, so "has any
|
|
100
|
+
* `(R, *)` pair" is exactly membership in this store (including an
|
|
101
|
+
* orphan-dangling source, whose membership row persists — consistent with
|
|
102
|
+
* `pairsOf`). The def is engine-owned and never handed to callers; this hands
|
|
103
|
+
* the query builder only its erased id for the `sparseInclude` list. */
|
|
104
|
+
relationBackingSparseId(def: RelationDef): SparseComponentID;
|
|
105
|
+
/** Drive a `(*, T)` wildcard query (`Query.forEachRelatedTo`): every source
|
|
106
|
+
* related to `target` under **any** relation, intersected with the query's
|
|
107
|
+
* dense mask + sparse require/exclude terms + the default enabled-row filter,
|
|
108
|
+
* each source yielded once. Unions `sourcesOf(target, R)` across every
|
|
109
|
+
* relation into a `Set` (dedup by full `EntityID` — a source related to `T`
|
|
110
|
+
* via two relations is yielded once), then sorts ascending: the cross-relation
|
|
111
|
+
* union has no inherent order, so one cold sort gives a deterministic,
|
|
112
|
+
* canonical `(*, T)` order matching `sourcesOf` / `sourcesOfAny` (which sort
|
|
113
|
+
* the same way). Cold/structural — not a per-tick hot loop over many targets.
|
|
114
|
+
* `sparseInclude`/`sparseExclude` carry both raw sparse terms and the backing
|
|
115
|
+
* stores of any composed `(R, *)` terms, so it intersects with them uniformly. */
|
|
116
|
+
forEachRelationTargetMatch(target: EntityID, include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, sparseInclude: readonly SparseComponentID[], sparseExclude: readonly SparseComponentID[], includeDisabled: boolean, cb: (entityId: EntityID) => void): void;
|
|
117
|
+
/** Reclaim reverse-index memory: drop every relation's reverse entries whose
|
|
118
|
+
* **target** has been destroyed, returning the total dropped across all
|
|
119
|
+
* relations (#491). Under the default `orphan` policy a destroyed target
|
|
120
|
+
* leaves its reverse entry intact until each source re-targets or dies, so a
|
|
121
|
+
* long-lived source that orphan-points at a churn of short-lived targets and
|
|
122
|
+
* never re-targets accumulates dead-target keys without bound. This cold-path
|
|
123
|
+
* hook drops them on demand — call it at scene or snapshot boundaries.
|
|
124
|
+
*
|
|
125
|
+
* Purely a memory reclaim with no observable state change: forward links stay
|
|
126
|
+
* dangling (so `orphan`'s `targetOf`-returns-the-dead-handle contract is
|
|
127
|
+
* unchanged), `stateHash` is unaffected (the reverse index is derived, never
|
|
128
|
+
* folded), and the dropped entries are faithfully rebuilt by snapshot/restore
|
|
129
|
+
* from the surviving forward links (`Store._rebuildRelationIndices`). The only
|
|
130
|
+
* difference a caller can see is `sourcesOf(deadHandle, R)` going from the
|
|
131
|
+
* dangling sources to `[]` — both meaningless once the target is gone.
|
|
132
|
+
* No-op (returns 0) when no relations are registered. */
|
|
133
|
+
compactRelations(): number;
|
|
134
|
+
/** Walk exclusive relation `R` from `src` toward the root, returning the
|
|
135
|
+
* **up**-chain `[src, parent, grandparent, …, root]` (nearest-ancestor-first,
|
|
136
|
+
* inclusive of both endpoints). A source with no target returns `[src]`. The
|
|
137
|
+
* root is the first entity in the chain with no `R`-target, **or** a dangling
|
|
138
|
+
* dead target handle (see below). Throws `RELATION_MODE_MISMATCH` on a multi
|
|
139
|
+
* relation and `RELATION_CYCLE` on a cycle (both `DEV`-only; in production
|
|
140
|
+
* a cycle stops at the repeated node).
|
|
141
|
+
*
|
|
142
|
+
* **Dangling links terminate the chain.** Under the `orphan` policy a source
|
|
143
|
+
* keeps pointing at a destroyed target (a dead handle). The walk must *not*
|
|
144
|
+
* advance through such a handle: the backing store is keyed by entity
|
|
145
|
+
* **index**, so reading the dead handle's index would return whatever now
|
|
146
|
+
* occupies that recycled slot — splicing the chain onto an unrelated entity
|
|
147
|
+
* (the ABA the `EntityID`-keyed reverse index avoids for `cascadeOf`). So a
|
|
148
|
+
* dead next-hop is appended as the chain's (dangling) terminus and the walk
|
|
149
|
+
* stops; the caller can detect it with `isAlive`, exactly as `targetOf`
|
|
150
|
+
* returns a dead handle. */
|
|
151
|
+
ancestorsOf(src: EntityID, def: RelationDef): EntityID[];
|
|
152
|
+
/** The root of `src`'s exclusive-relation chain — the last entity of
|
|
153
|
+
* `ancestorsOf` (the one with no `R`-target). `src` itself when it has no
|
|
154
|
+
* target. If the chain ends in a dangling dead target handle (orphan policy),
|
|
155
|
+
* that handle is the root — `isAlive`-check the result if dangling links are
|
|
156
|
+
* possible. Same `DEV` guards as `ancestorsOf`. */
|
|
157
|
+
rootOf(src: EntityID, def: RelationDef): EntityID;
|
|
158
|
+
/** Walk exclusive relation `R` **down** from `root` over the reverse index,
|
|
159
|
+
* returning the subtree (including `root`) breadth-first — **parents before
|
|
160
|
+
* children** (the `cascade` order). Children of each node come from
|
|
161
|
+
* `sourcesOf` (ascending by id), so the traversal is deterministic. Throws
|
|
162
|
+
* `RELATION_MODE_MISMATCH` on a multi relation and `RELATION_CYCLE` on a cycle
|
|
163
|
+
* (both `DEV`-only; in production an already-visited node is skipped, so
|
|
164
|
+
* it never hangs). */
|
|
165
|
+
cascadeOf(root: EntityID, def: RelationDef): EntityID[];
|
|
166
|
+
/** Purge a destroyed entity from the relation indices — its **source** role
|
|
167
|
+
* only: drop its forward target(s) and unlink it from every target's reverse
|
|
168
|
+
* set. The backing sparse membership row is dropped separately by the Store's
|
|
169
|
+
* `_purgeSparse`, so this must run *before* it (it reads the sparse target
|
|
170
|
+
* field for exclusive relations). The entity's **target** role is left
|
|
171
|
+
* intact: the reverse index is keyed by full `EntityID`, so a recycled slot
|
|
172
|
+
* never aliases the dead target's sources. The destroyed entity's **target**
|
|
173
|
+
* role is handled separately by `cleanupTarget` per each relation's
|
|
174
|
+
* `OnDeleteTarget` policy (#473). Gated by the caller on `count > 0`. */
|
|
175
|
+
purgeSource(entityId: EntityID): void;
|
|
176
|
+
/** Apply each relation's `OnDeleteTarget` policy for a destroyed **target**
|
|
177
|
+
* `targetId` (#473). Walks the registry; for every relation whose reverse
|
|
178
|
+
* index has sources pointing at the dead target:
|
|
179
|
+
*
|
|
180
|
+
* - **`delete`** — append each source to `cascade`; the caller destroys them
|
|
181
|
+
* through the same path, so chains/trees cascade recursively. The sources'
|
|
182
|
+
* own source-role purge (`purgeSource`) drops their forward link and
|
|
183
|
+
* unlinks them from `targetId`'s reverse set, so no reverse entry leaks.
|
|
184
|
+
* - **`clear`** — drop each source's link to the dead target in place via
|
|
185
|
+
* `rs.unlink(src, targetId)` (exclusive drops the row; multi removes the
|
|
186
|
+
* target from the set, dropping membership when it empties). Sources
|
|
187
|
+
* survive — the cardinality bookkeeping is the relation's, not branched here.
|
|
188
|
+
* - **`orphan`** — skipped (the link is left dangling; safe because the
|
|
189
|
+
* reverse key carries the generation).
|
|
190
|
+
*
|
|
191
|
+
* `sourcesOf` returns a fresh snapshot, so mutating the reverse index while
|
|
192
|
+
* iterating is safe. Gated by the caller on `hasTargetCleanup`, so the
|
|
193
|
+
* whole walk is skipped when no relation opts into a non-`orphan` policy. */
|
|
194
|
+
cleanupTarget(targetId: EntityID, cascade: EntityID[]): void;
|
|
195
|
+
/** Fourth query-match path (#581): yield the matched entities — the exact
|
|
196
|
+
* sparse-match intersection (dense mask + sparse require/exclude + the
|
|
197
|
+
* default enabled-row filter) — in canonical **hierarchy depth order** over
|
|
198
|
+
* exclusive relation `R`: depth ascending (parents before children), **entity
|
|
199
|
+
* index ascending within each depth band**. Entities deeper than `maxDepth`
|
|
200
|
+
* are skipped (`Infinity` = unbounded). Only reached via `Query.forEachEntity`
|
|
201
|
+
* on a query carrying a `.hierarchy(R)` term.
|
|
202
|
+
*
|
|
203
|
+
* `.hierarchy(R)` does not narrow the matched set — it reorders it — so an
|
|
204
|
+
* entity with no `R`-parent is a root (depth 0) and is yielded first. Depth is a
|
|
205
|
+
* structural property of the *full* tree (an ancestor outside the matched set
|
|
206
|
+
* still counts toward depth), computed by a memoised upward walk shared across
|
|
207
|
+
* the whole batch, so a shared/deep chain costs O(nodes), not O(nodes²).
|
|
208
|
+
*
|
|
209
|
+
* The canonical order is produced without a comparator sort (which the observer
|
|
210
|
+
* bench measured at 2–4× — `observer.ts`): (1) collect the matched ids via
|
|
211
|
+
* the host's `forEachSparseMatch`; (2) `radixSortByIndex` → entity-index
|
|
212
|
+
* ascending; (3) stable-bucket by depth — since the input is index-ascending
|
|
213
|
+
* and the bucket append is stable, each depth band stays index-ascending.
|
|
214
|
+
* Tuned for the motivating per-tick case (transform propagation): the radix
|
|
215
|
+
* scratch is reused instance state (`_hierarchyRadix*`), so a per-tick pass
|
|
216
|
+
* churns no histograms; the working set (`matched` / `buckets` / `depthMemo` /
|
|
217
|
+
* `visiting`) is still allocated per call, as it must stay call-local for
|
|
218
|
+
* re-entrancy.
|
|
219
|
+
*
|
|
220
|
+
* Exclusive-only — a multi relation throws `RELATION_MODE_MISMATCH` in `DEV`
|
|
221
|
+
* (mirrors `cascadeOf` / `ancestorsOf`); a cycle is a loud `RELATION_CYCLE` in
|
|
222
|
+
* `DEV` and a safe break in production. */
|
|
223
|
+
forEachHierarchyMatch(include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, sparseInclude: readonly SparseComponentID[], sparseExclude: readonly SparseComponentID[], denseArchetypes: readonly Archetype[], relation: RelationDef, maxDepth: number, includeDisabled: boolean, cb: (entityId: EntityID) => void): void;
|
|
224
|
+
/** Depth of entity index `idx` in the exclusive-relation tree backing `store`
|
|
225
|
+
* (root = 0), for `forEachHierarchyMatch`. Memoised across the batch (`memo`),
|
|
226
|
+
* so a shared ancestor chain is walked once. Walks upward via the backing store's
|
|
227
|
+
* target field 0 (mirroring `ancestorsOf`), stopping at: a root (no target), an
|
|
228
|
+
* already-memoised node, or a dangling/dead parent (the child is then treated as
|
|
229
|
+
* a root — never advance through a recycled slot, the ABA `ancestorsOf` guards).
|
|
230
|
+
* `visiting` flags the nodes on the current upward path to catch a cycle —
|
|
231
|
+
* `RELATION_CYCLE` in `DEV`, treated as a root in production — and is emptied
|
|
232
|
+
* on the way back down so it can be reused for the next entity. */
|
|
233
|
+
private _hierarchyDepthOf;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=relation_service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation_service.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/relation_service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/***
|
|
2
|
+
* Resource — Typed singleton key-value storage.
|
|
3
|
+
*
|
|
4
|
+
* Resources are global singletons that don't belong to any entity.
|
|
5
|
+
* Think: time, input state, camera config, game settings.
|
|
6
|
+
*
|
|
7
|
+
* Each resource is identified by a unique symbol (ResourceKey<T>) and
|
|
8
|
+
* stores an arbitrary typed value. The key carries the value type as a
|
|
9
|
+
* phantom type parameter, so reads are type-safe at compile time.
|
|
10
|
+
*
|
|
11
|
+
* Lifecycle (#798). A resource is register-once *until removed*: a second
|
|
12
|
+
* `registerResource` for a live key throws RESOURCE_ALREADY_REGISTERED, but
|
|
13
|
+
* `removeResource(key)` drops it (failing closed on a missing key) and frees the
|
|
14
|
+
* key to be registered again — the present → absent → present axis. Removal is
|
|
15
|
+
* access-checked as a *write* (a system must declare the key in `resourceWrites`),
|
|
16
|
+
* and resources stay out of `stateHash` and snapshot/resume regardless, so a
|
|
17
|
+
* lifecycle change never perturbs the determinism hash.
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
*
|
|
21
|
+
* const TimeRes = resourceKey<{ delta: number; elapsed: number }>("Time");
|
|
22
|
+
* ecs.resources.register(TimeRes, { delta: 0, elapsed: 0 });
|
|
23
|
+
* const time = ecs.resources.get(TimeRes);
|
|
24
|
+
* // time.delta → number, time.elapsed → number
|
|
25
|
+
* ecs.resources.remove(TimeRes); // present → absent
|
|
26
|
+
* ecs.resources.register(TimeRes, { delta: 0, elapsed: 0 }); // → present again
|
|
27
|
+
*
|
|
28
|
+
***/
|
|
29
|
+
/**
|
|
30
|
+
* `T` is deliberately INVARIANT (via the function-typed phantom — an `in out`
|
|
31
|
+
* annotation is not legal on an intersection alias, so the structure carries
|
|
32
|
+
* the variance): a key is used for both reads (`resource`, covariant) and
|
|
33
|
+
* writes (`setResource`, contravariant), so either one-sided variance is a
|
|
34
|
+
* soundness hole — a covariant `T` let `ResourceKey<Cat>` widen to
|
|
35
|
+
* `ResourceKey<Animal>` and `setResource` then stored a `Dog` behind a `Cat`
|
|
36
|
+
* key. Invariance also makes keys with different `T` mutually unassignable,
|
|
37
|
+
* which is what the typed system seam's `resourceReads`/`resourceWrites`
|
|
38
|
+
* narrowing keys on (§typestate). Schema-erased positions (access checks,
|
|
39
|
+
* declaration lists) must use `ResourceKey<any>` — `ResourceKey<unknown>` no
|
|
40
|
+
* longer erases.
|
|
41
|
+
*/
|
|
42
|
+
declare const __resourceValue: unique symbol;
|
|
43
|
+
export type ResourceKey<T> = symbol & {
|
|
44
|
+
readonly [__resourceValue]: (value: T) => T;
|
|
45
|
+
};
|
|
46
|
+
export declare function resourceKey<T>(name: string): ResourceKey<T>;
|
|
47
|
+
/** Recover a key's value type: `ResourceValueOf<typeof TimeRes>` is the Time
|
|
48
|
+
* shape. The typed `SystemContext` resource surface infers the KEY type (to
|
|
49
|
+
* check it against the declared-access union) and recovers `T` through this,
|
|
50
|
+
* instead of taking `ResourceKey<T>` directly. */
|
|
51
|
+
export type ResourceValueOf<K> = K extends ResourceKey<infer T> ? T : never;
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -19,15 +19,35 @@
|
|
|
19
19
|
* Usage:
|
|
20
20
|
*
|
|
21
21
|
* const TimeRes = resourceKey<{ delta: number; elapsed: number }>("Time");
|
|
22
|
-
*
|
|
23
|
-
* const time =
|
|
22
|
+
* ecs.resources.register(TimeRes, { delta: 0, elapsed: 0 });
|
|
23
|
+
* const time = ecs.resources.get(TimeRes);
|
|
24
24
|
* // time.delta → number, time.elapsed → number
|
|
25
|
-
*
|
|
26
|
-
*
|
|
25
|
+
* ecs.resources.remove(TimeRes); // present → absent
|
|
26
|
+
* ecs.resources.register(TimeRes, { delta: 0, elapsed: 0 }); // → present again
|
|
27
27
|
*
|
|
28
28
|
***/
|
|
29
|
+
/**
|
|
30
|
+
* `T` is deliberately INVARIANT (via the function-typed phantom — an `in out`
|
|
31
|
+
* annotation is not legal on an intersection alias, so the structure carries
|
|
32
|
+
* the variance): a key is used for both reads (`resource`, covariant) and
|
|
33
|
+
* writes (`setResource`, contravariant), so either one-sided variance is a
|
|
34
|
+
* soundness hole — a covariant `T` let `ResourceKey<Cat>` widen to
|
|
35
|
+
* `ResourceKey<Animal>` and `setResource` then stored a `Dog` behind a `Cat`
|
|
36
|
+
* key. Invariance also makes keys with different `T` mutually unassignable,
|
|
37
|
+
* which is what the typed system seam's `resourceReads`/`resourceWrites`
|
|
38
|
+
* narrowing keys on (§typestate). Schema-erased positions (access checks,
|
|
39
|
+
* declaration lists) must use `ResourceKey<any>` — `ResourceKey<unknown>` no
|
|
40
|
+
* longer erases.
|
|
41
|
+
*/
|
|
42
|
+
declare const __resourceValue: unique symbol;
|
|
29
43
|
export type ResourceKey<T> = symbol & {
|
|
30
|
-
readonly
|
|
44
|
+
readonly [__resourceValue]: (value: T) => T;
|
|
31
45
|
};
|
|
32
46
|
export declare function resourceKey<T>(name: string): ResourceKey<T>;
|
|
47
|
+
/** Recover a key's value type: `ResourceValueOf<typeof TimeRes>` is the Time
|
|
48
|
+
* shape. The typed `SystemContext` resource surface infers the KEY type (to
|
|
49
|
+
* check it against the declared-access union) and recovers `T` through this,
|
|
50
|
+
* instead of taking `ResourceKey<T>` directly. */
|
|
51
|
+
export type ResourceValueOf<K> = K extends ResourceKey<infer T> ? T : never;
|
|
52
|
+
export {};
|
|
33
53
|
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2BK;AAIL,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;KA2BK;AAIL;;;;;;;;;;;;GAYG;AACH,OAAO,CAAC,MAAM,eAAe,EAAE,OAAO,MAAM,CAAC;AAE7C,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAA;CAAE,CAAC;AAEtF,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAE3D;AAED;;;kDAGkD;AAClD,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,WAAW,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/***
|
|
2
|
+
* ResourceRegistry — the world's symbol-keyed resource dictionary (H1 step 2).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `Store`, which keeps one-line delegations; fully
|
|
5
|
+
* self-contained (no Store reach-back). Resources stay out of `stateHash` and
|
|
6
|
+
* out of snapshot/resume — this is host-side state, not simulation state.
|
|
7
|
+
***/
|
|
8
|
+
export declare class ResourceRegistry {
|
|
9
|
+
private readonly map;
|
|
10
|
+
register(key: symbol, value: unknown): void;
|
|
11
|
+
get(key: symbol): unknown;
|
|
12
|
+
set(key: symbol, value: unknown): void;
|
|
13
|
+
/** Drop a resource from the world. Fails closed on a missing key (mirrors
|
|
14
|
+
* `get` / `set`); afterwards the key is free to `register` again — the
|
|
15
|
+
* present → absent → present lifecycle (#798). Purely a host-side
|
|
16
|
+
* dictionary delete with no determinism-hash effect. */
|
|
17
|
+
remove(key: symbol): void;
|
|
18
|
+
has(key: symbol): boolean;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=resource_registry.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/***
|
|
2
|
+
* ResourceRegistry — the world's symbol-keyed resource dictionary (H1 step 2).
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `Store`, which keeps one-line delegations; fully
|
|
5
|
+
* self-contained (no Store reach-back). Resources stay out of `stateHash` and
|
|
6
|
+
* out of snapshot/resume — this is host-side state, not simulation state.
|
|
7
|
+
***/
|
|
8
|
+
export declare class ResourceRegistry {
|
|
9
|
+
private readonly map;
|
|
10
|
+
register(key: symbol, value: unknown): void;
|
|
11
|
+
get(key: symbol): unknown;
|
|
12
|
+
set(key: symbol, value: unknown): void;
|
|
13
|
+
/** Drop a resource from the world. Fails closed on a missing key (mirrors
|
|
14
|
+
* `get` / `set`); afterwards the key is free to `register` again — the
|
|
15
|
+
* present → absent → present lifecycle (#798). Purely a host-side
|
|
16
|
+
* dictionary delete with no determinism-hash effect. */
|
|
17
|
+
remove(key: symbol): void;
|
|
18
|
+
has(key: symbol): boolean;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=resource_registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource_registry.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/resource_registry.ts"],"names":[],"mappings":"AAAA;;;;;;KAMK;AAIL,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAmC;IAEhD,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAW3C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAWzB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAW7C;;;4DAGwD;IACjD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAWzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;CAGhC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { ArchetypeViews } from '../store/index.cjs';
|
|
2
|
+
/** Magic for the combined world-snapshot frame (`"WRS0"` little-endian). Distinct
|
|
3
|
+
* from the SAB `STORE_MAGIC` so a bare dense snapshot fed to `restoreInto` is
|
|
4
|
+
* rejected with a clear error instead of being mis-parsed as a combined frame. */
|
|
5
|
+
export declare const WORLD_SNAPSHOT_MAGIC = 810766935;
|
|
6
|
+
/** Combined-frame format version. Bumped if the section framing or host-state
|
|
7
|
+
* layout changes. Independent of `SIM_ABI_VERSION` (which gates the dense bytes). */
|
|
8
|
+
export declare const ECS_SNAPSHOT_VERSION = 1;
|
|
9
|
+
/** Thrown by `Store.restoreInto` (and the helpers here) when a combined snapshot
|
|
10
|
+
* is malformed, carries the wrong magic/version, or targets a world whose
|
|
11
|
+
* archetype/component registration doesn't match the snapshot. Mirrors
|
|
12
|
+
* `StoreRestoreError` / `SparseRestoreError` so callers see one error class per
|
|
13
|
+
* restore failure mode. */
|
|
14
|
+
export declare class ECSRestoreError extends Error {
|
|
15
|
+
constructor(message: string);
|
|
16
|
+
}
|
|
17
|
+
/** Per-archetype host-side row bookkeeping the SAB doesn't carry authoritatively. */
|
|
18
|
+
export interface ArchetypeRowState {
|
|
19
|
+
readonly archetypeId: number;
|
|
20
|
+
/** Total live rows (enabled + disabled). */
|
|
21
|
+
readonly length: number;
|
|
22
|
+
/** Enabled-row partition boundary (#577): rows `[0, enabledCount)` enabled. */
|
|
23
|
+
readonly enabledCount: number;
|
|
24
|
+
}
|
|
25
|
+
/** The host-side state a snapshot captures alongside the dense + sparse bytes. */
|
|
26
|
+
export interface HostState {
|
|
27
|
+
/** World tick at snapshot time (`Store._tick`). */
|
|
28
|
+
readonly tick: number;
|
|
29
|
+
/** Entity-index high-water (count of slots ever issued). Also mirrored in the
|
|
30
|
+
* SAB region's `length` header; carried here for a cross-check on restore. */
|
|
31
|
+
readonly entityHighWater: number;
|
|
32
|
+
/** Live entity count. */
|
|
33
|
+
readonly entityAliveCount: number;
|
|
34
|
+
/** Recycle free-list in live order (LIFO: the last element is the next slot
|
|
35
|
+
* `createEntity` hands out). */
|
|
36
|
+
readonly freeIndices: readonly number[];
|
|
37
|
+
/** Per-archetype row state, one entry per SAB-backed archetype. */
|
|
38
|
+
readonly archetypeRows: readonly ArchetypeRowState[];
|
|
39
|
+
}
|
|
40
|
+
/** Serialize host-state to a self-contained little-endian byte buffer. Layout:
|
|
41
|
+
*
|
|
42
|
+
* [u32 tick][u32 highWater][u32 alive_count]
|
|
43
|
+
* [u32 freeCount][u32 free_index × freeCount]
|
|
44
|
+
* [u32 archCount][(u32 id, u32 length, u32 enabled_count) × archCount]
|
|
45
|
+
*/
|
|
46
|
+
export declare function serializeHostState(hs: HostState): Uint8Array;
|
|
47
|
+
/** Parse host-state bytes produced by `serializeHostState`. Throws
|
|
48
|
+
* `ECSRestoreError` on truncation or a trailing-byte (non-canonical) buffer. */
|
|
49
|
+
export declare function parseHostState(bytes: Uint8Array): HostState;
|
|
50
|
+
/** Assemble the combined world-snapshot frame from its three sections. Layout:
|
|
51
|
+
*
|
|
52
|
+
* [u32 magic][u32 version][u32 denseLen][u32 sparseLen][u32 hostLen]
|
|
53
|
+
* [dense][sparse][host]
|
|
54
|
+
*/
|
|
55
|
+
export declare function frameWorldSnapshot(dense: Uint8Array, sparse: Uint8Array, host: Uint8Array): Uint8Array;
|
|
56
|
+
/** The three sections of a combined frame, as zero-copy subviews over `bytes`. */
|
|
57
|
+
export interface WorldSnapshotSections {
|
|
58
|
+
readonly dense: Uint8Array;
|
|
59
|
+
readonly sparse: Uint8Array;
|
|
60
|
+
readonly host: Uint8Array;
|
|
61
|
+
}
|
|
62
|
+
/** Split a combined frame back into its sections. Validates magic, version, and
|
|
63
|
+
* an exact (no trailing bytes) frame; throws `ECSRestoreError` otherwise. */
|
|
64
|
+
export declare function unframeWorldSnapshot(bytes: Uint8Array): WorldSnapshotSections;
|
|
65
|
+
/**
|
|
66
|
+
* Fail-closed registration guard, read **directly from the snapshot's dense
|
|
67
|
+
* bytes** so it can run BEFORE the dense backing is touched. `restoreInto`
|
|
68
|
+
* builds the restored store through the live world's in-place allocator
|
|
69
|
+
* (ADR-0008), which reuses the live backing buffer — so validating a
|
|
70
|
+
* *materialised* `ColumnStore` would already have overwritten live column data
|
|
71
|
+
* (the buffer is overwritten inside `restoreColumnStore`, before any post-build
|
|
72
|
+
* check could run). Parsing the descriptors off the raw `dense` `Uint8Array`
|
|
73
|
+
* keeps the check non-mutating, so a mismatch leaves the live world untouched.
|
|
74
|
+
*
|
|
75
|
+
* Asserts: the dense section's SAB magic + ABI, that its archetype set +
|
|
76
|
+
* per-archetype `componentMask` + per-column `(componentId, fieldId, typeTag)`
|
|
77
|
+
* match the live store's exactly (so every live `Archetype.refreshViews` finds
|
|
78
|
+
* its region and no snapshot archetype is orphaned), and that the entity-index
|
|
79
|
+
* capacity matches (the region is sized once at construction). The archetype
|
|
80
|
+
* graph is rebuilt from registration code, not the snapshot (mirroring
|
|
81
|
+
* `restoreSparse`'s "registered in the same order" contract). Throws
|
|
82
|
+
* `ECSRestoreError` on any mismatch / malformed section.
|
|
83
|
+
*/
|
|
84
|
+
export declare function assertDenseLayoutMatchesLive(dense: Uint8Array, live: ReadonlyMap<number, ArchetypeViews>, liveEntityIndexCapacity: number): void;
|
|
85
|
+
//# sourceMappingURL=resume.d.ts.map
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { ArchetypeViews } from '../store';
|
|
1
|
+
import { ArchetypeViews } from '../store/index.js';
|
|
2
2
|
/** Magic for the combined world-snapshot frame (`"WRS0"` little-endian). Distinct
|
|
3
3
|
* from the SAB `STORE_MAGIC` so a bare dense snapshot fed to `restoreInto` is
|
|
4
4
|
* rejected with a clear error instead of being mis-parsed as a combined frame. */
|
|
5
5
|
export declare const WORLD_SNAPSHOT_MAGIC = 810766935;
|
|
6
6
|
/** Combined-frame format version. Bumped if the section framing or host-state
|
|
7
7
|
* layout changes. Independent of `SIM_ABI_VERSION` (which gates the dense bytes). */
|
|
8
|
-
export declare const
|
|
8
|
+
export declare const ECS_SNAPSHOT_VERSION = 1;
|
|
9
9
|
/** Thrown by `Store.restoreInto` (and the helpers here) when a combined snapshot
|
|
10
10
|
* is malformed, carries the wrong magic/version, or targets a world whose
|
|
11
11
|
* archetype/component registration doesn't match the snapshot. Mirrors
|
|
12
12
|
* `StoreRestoreError` / `SparseRestoreError` so callers see one error class per
|
|
13
13
|
* restore failure mode. */
|
|
14
|
-
export declare class
|
|
14
|
+
export declare class ECSRestoreError extends Error {
|
|
15
15
|
constructor(message: string);
|
|
16
16
|
}
|
|
17
17
|
/** Per-archetype host-side row bookkeeping the SAB doesn't carry authoritatively. */
|
|
@@ -45,7 +45,7 @@ export interface HostState {
|
|
|
45
45
|
*/
|
|
46
46
|
export declare function serializeHostState(hs: HostState): Uint8Array;
|
|
47
47
|
/** Parse host-state bytes produced by `serializeHostState`. Throws
|
|
48
|
-
* `
|
|
48
|
+
* `ECSRestoreError` on truncation or a trailing-byte (non-canonical) buffer. */
|
|
49
49
|
export declare function parseHostState(bytes: Uint8Array): HostState;
|
|
50
50
|
/** Assemble the combined world-snapshot frame from its three sections. Layout:
|
|
51
51
|
*
|
|
@@ -60,7 +60,7 @@ export interface WorldSnapshotSections {
|
|
|
60
60
|
readonly host: Uint8Array;
|
|
61
61
|
}
|
|
62
62
|
/** Split a combined frame back into its sections. Validates magic, version, and
|
|
63
|
-
* an exact (no trailing bytes) frame; throws `
|
|
63
|
+
* an exact (no trailing bytes) frame; throws `ECSRestoreError` otherwise. */
|
|
64
64
|
export declare function unframeWorldSnapshot(bytes: Uint8Array): WorldSnapshotSections;
|
|
65
65
|
/**
|
|
66
66
|
* Fail-closed registration guard, read **directly from the snapshot's dense
|
|
@@ -79,7 +79,7 @@ export declare function unframeWorldSnapshot(bytes: Uint8Array): WorldSnapshotSe
|
|
|
79
79
|
* capacity matches (the region is sized once at construction). The archetype
|
|
80
80
|
* graph is rebuilt from registration code, not the snapshot (mirroring
|
|
81
81
|
* `restoreSparse`'s "registered in the same order" contract). Throws
|
|
82
|
-
* `
|
|
82
|
+
* `ECSRestoreError` on any mismatch / malformed section.
|
|
83
83
|
*/
|
|
84
84
|
export declare function assertDenseLayoutMatchesLive(dense: Uint8Array, live: ReadonlyMap<number, ArchetypeViews>, liveEntityIndexCapacity: number): void;
|
|
85
85
|
//# sourceMappingURL=resume.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/resume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAUN,KAAK,cAAc,EACnB,MAAM,UAAU,CAAC;AAElB;;kFAEkF;AAClF,eAAO,MAAM,oBAAoB,YAAa,CAAC;AAE/C;qFACqF;AACrF,eAAO,MAAM,
|
|
1
|
+
{"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/resume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAUN,KAAK,cAAc,EACnB,MAAM,UAAU,CAAC;AAElB;;kFAEkF;AAClF,eAAO,MAAM,oBAAoB,YAAa,CAAC;AAE/C;qFACqF;AACrF,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC;;;;2BAI2B;AAC3B,qBAAa,eAAgB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI3B;AAED,qFAAqF;AACrF,MAAM,WAAW,iBAAiB;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAED,kFAAkF;AAClF,MAAM,WAAW,SAAS;IACzB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;kFAC8E;IAC9E,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,yBAAyB;IACzB,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;oCACgC;IAChC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,SAAS,iBAAiB,EAAE,CAAC;CACrD;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,SAAS,GAAG,UAAU,CAwB5D;AAED;gFACgF;AAChF,wBAAgB,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,CAqC3D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CACjC,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,UAAU,GACd,UAAU,CAaZ;AAED,kFAAkF;AAClF,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC1B;AAED;6EAC6E;AAC7E,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,qBAAqB,CAuC7E;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAC3C,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,EACzC,uBAAuB,EAAE,MAAM,GAC7B,IAAI,CAoGN"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { ComponentDef } from './component.cjs';
|
|
2
|
+
import { ResourceKey } from './resource.cjs';
|
|
3
|
+
import { Query } from './query.cjs';
|
|
4
|
+
/**
|
|
5
|
+
* The read-only slice of `SystemContext` a run condition is handed. Deliberately
|
|
6
|
+
* narrow — a condition can read the ECS tick and resources, but has no path to
|
|
7
|
+
* mutate the world or read component columns directly (component reads go
|
|
8
|
+
* through a captured `Query`). `SystemContext` structurally satisfies this, so
|
|
9
|
+
* the schedule passes the live context unchanged.
|
|
10
|
+
*/
|
|
11
|
+
export interface ConditionContext {
|
|
12
|
+
/** Current ECS tick — the deterministic clock built-ins key off. */
|
|
13
|
+
readonly ecsTick: number;
|
|
14
|
+
/** Read a resource value (checked against `resourceReads` in `DEV`). */
|
|
15
|
+
resource<T>(key: ResourceKey<T>): T;
|
|
16
|
+
/** Membership probe for a resource — unchecked, mirrors `hasComponent`. */
|
|
17
|
+
hasResource<T>(key: ResourceKey<T>): boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A per-tick gate. `evaluate` returns `true` to run the gated system(s), `false`
|
|
21
|
+
* to skip them this tick. `reads` / `resourceReads` declare the predicate's
|
|
22
|
+
* read surface for `accessCheck` (dev) and the future parallel scheduler's
|
|
23
|
+
* read-edge graph; both are optional (absent reads as empty).
|
|
24
|
+
*/
|
|
25
|
+
export interface RunCondition {
|
|
26
|
+
/** Human-readable label, surfaced in dev access-violation diagnostics. */
|
|
27
|
+
readonly name: string;
|
|
28
|
+
/** Pure, deterministic predicate. See the two hard rules in the file header. */
|
|
29
|
+
readonly evaluate: (ctx: ConditionContext) => boolean;
|
|
30
|
+
/** Components the predicate reads (via a captured query). Forward-looking
|
|
31
|
+
* metadata — `ConditionContext` exposes no direct column read, so this is
|
|
32
|
+
* not runtime-checkable today, but it is the read-edge a parallel scheduler
|
|
33
|
+
* will consume. */
|
|
34
|
+
readonly reads?: readonly ComponentDef[];
|
|
35
|
+
/** Resources the predicate reads. Validated at runtime in `DEV` when the
|
|
36
|
+
* condition evaluates inside `accessCheck.enterCondition`. */
|
|
37
|
+
readonly resourceReads?: readonly ResourceKey<any>[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Run the gated system(s) only while a resource equals `expected` (strict `===`,
|
|
41
|
+
* so reference identity for objects). The canonical "feature flag / game phase"
|
|
42
|
+
* gate — flip `ecs.resources.set(key, …)` and the whole group toggles.
|
|
43
|
+
*/
|
|
44
|
+
export declare function runIfResourceEq<T>(key: ResourceKey<T>, expected: T): RunCondition;
|
|
45
|
+
/**
|
|
46
|
+
* Run the gated system(s) once every `n` ticks — on ticks congruent to `offset`
|
|
47
|
+
* mod `n`. Keyed off the deterministic ECS tick, so it carries no private
|
|
48
|
+
* state and replays bit-identically. `offset` is a **phase shift taken mod `n`**
|
|
49
|
+
* (default `0` ⇒ fires on tick 0, n, 2n, …; `offset` and `offset + n` are the
|
|
50
|
+
* same phase, and a negative offset wraps), normalized once here into `[0, n)`.
|
|
51
|
+
* `n` must be a positive integer and `offset` an integer.
|
|
52
|
+
*
|
|
53
|
+
* The normalization isn't cosmetic: `(ecsTick - offset) % n` with a raw
|
|
54
|
+
* `offset ≥ n` (or negative) leans on JS's signed `%` yielding `-0` for a
|
|
55
|
+
* negative multiple (and `-0 === 0`) to stay congruent — correct, but load-
|
|
56
|
+
* bearing on a quirk. Folding `offset` into `[0, n)` up front makes the phase
|
|
57
|
+
* explicit: the per-tick dividend `ecsTick - phase` may still be negative
|
|
58
|
+
* (when `ecsTick < phase`), but with `phase ∈ [0, n)` it is never a *negative
|
|
59
|
+
* multiple* of `n` — it lands in `[-(n-1), -1]`, which holds no multiple of `n`
|
|
60
|
+
* — so `%` never produces `-0` and the check needs no signed-zero reliance.
|
|
61
|
+
*/
|
|
62
|
+
export declare function runEveryNTicks(n: number, offset?: number): RunCondition;
|
|
63
|
+
/**
|
|
64
|
+
* Run the gated system(s) only when a query matches at least one (enabled)
|
|
65
|
+
* entity — flecs `runIf` over a query. The query is captured at config time;
|
|
66
|
+
* its component defs are declared as `reads`. `count()` is a membership sum over
|
|
67
|
+
* matching archetypes (no column reads), so it trips no `accessCheck` read.
|
|
68
|
+
*
|
|
69
|
+
* The query must be **dense-only**: `entityCount` asserts this in `DEV`
|
|
70
|
+
* (`_assertDenseOnly`), so pass a plain `ecs.query(...)` — a query carrying
|
|
71
|
+
* `.optional(...)` or sparse terms throws. Gate on sparse membership with a
|
|
72
|
+
* custom predicate over `forEachEntity` instead.
|
|
73
|
+
*/
|
|
74
|
+
export declare function runIfAnyMatch(query: Query<readonly ComponentDef[]>): RunCondition;
|
|
75
|
+
/** Invert a condition: run exactly when `cond` would skip. */
|
|
76
|
+
export declare function not(cond: RunCondition): RunCondition;
|
|
77
|
+
/** Run only when EVERY condition passes (`&&`, short-circuit). */
|
|
78
|
+
export declare function allOf(...conds: RunCondition[]): RunCondition;
|
|
79
|
+
/** Run when ANY condition passes (`||`, short-circuit). */
|
|
80
|
+
export declare function anyOf(...conds: RunCondition[]): RunCondition;
|
|
81
|
+
//# sourceMappingURL=run_condition.d.ts.map
|