@oasys/oecs 0.3.1 → 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 +207 -267
- package/dist/core/ecs/access_check.d.cts +77 -0
- package/dist/core/ecs/access_check.d.ts +77 -0
- package/dist/core/ecs/access_check.d.ts.map +1 -0
- package/dist/core/ecs/archetype.d.cts +486 -0
- package/dist/core/ecs/archetype.d.ts +486 -0
- package/dist/core/ecs/archetype.d.ts.map +1 -0
- 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 +43 -0
- package/dist/core/ecs/builtin_relations.d.ts.map +1 -0
- package/dist/core/ecs/command_log.d.cts +116 -0
- package/dist/core/ecs/command_log.d.ts +116 -0
- package/dist/core/ecs/command_log.d.ts.map +1 -0
- package/dist/core/ecs/component.d.cts +176 -0
- package/dist/core/ecs/component.d.ts +176 -0
- package/dist/core/ecs/component.d.ts.map +1 -0
- package/dist/core/ecs/compute_backend.d.cts +37 -0
- package/dist/core/ecs/compute_backend.d.ts +37 -0
- package/dist/core/ecs/compute_backend.d.ts.map +1 -0
- 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 +93 -0
- package/dist/core/ecs/dispatch_trace.d.ts.map +1 -0
- package/dist/core/ecs/ecs.d.cts +562 -0
- package/dist/core/ecs/ecs.d.ts +562 -0
- package/dist/core/ecs/ecs.d.ts.map +1 -0
- package/dist/core/ecs/ecs_memory.d.cts +179 -0
- package/dist/core/ecs/ecs_memory.d.ts +179 -0
- package/dist/core/ecs/ecs_memory.d.ts.map +1 -0
- package/dist/core/ecs/entity.d.cts +34 -0
- package/dist/core/ecs/entity.d.ts +34 -0
- package/dist/core/ecs/entity.d.ts.map +1 -0
- 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 +79 -0
- package/dist/core/ecs/event.d.ts.map +1 -0
- 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 +133 -0
- package/dist/core/ecs/frame_trace.d.ts.map +1 -0
- package/dist/core/ecs/host_commands.d.cts +274 -0
- package/dist/core/ecs/host_commands.d.ts +274 -0
- package/dist/core/ecs/host_commands.d.ts.map +1 -0
- package/dist/core/ecs/index.d.cts +46 -0
- package/dist/core/ecs/index.d.ts +46 -0
- package/dist/core/ecs/index.d.ts.map +1 -0
- package/dist/core/ecs/observer.d.cts +173 -0
- package/dist/core/ecs/observer.d.ts +173 -0
- package/dist/core/ecs/observer.d.ts.map +1 -0
- package/dist/core/ecs/query.d.cts +718 -0
- package/dist/core/ecs/query.d.ts +718 -0
- package/dist/core/ecs/query.d.ts.map +1 -0
- package/dist/core/ecs/ref.d.cts +31 -0
- package/dist/core/ecs/ref.d.ts +31 -0
- package/dist/core/ecs/ref.d.ts.map +1 -0
- package/dist/core/ecs/relation.d.cts +243 -0
- package/dist/core/ecs/relation.d.ts +243 -0
- package/dist/core/ecs/relation.d.ts.map +1 -0
- 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 +53 -0
- package/dist/core/ecs/resource.d.ts.map +1 -0
- 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 +85 -0
- package/dist/core/ecs/resume.d.ts.map +1 -0
- package/dist/core/ecs/run_condition.d.cts +81 -0
- package/dist/core/ecs/run_condition.d.ts +81 -0
- package/dist/core/ecs/run_condition.d.ts.map +1 -0
- package/dist/core/ecs/schedule.d.cts +142 -0
- package/dist/core/ecs/schedule.d.ts +142 -0
- package/dist/core/ecs/schedule.d.ts.map +1 -0
- 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 +110 -0
- package/dist/core/ecs/sparse_store.d.ts.map +1 -0
- package/dist/core/ecs/store.d.cts +950 -0
- package/dist/core/ecs/store.d.ts +950 -0
- package/dist/core/ecs/store.d.ts.map +1 -0
- package/dist/core/ecs/store_layout_listener.d.cts +23 -0
- package/dist/core/ecs/store_layout_listener.d.ts +23 -0
- package/dist/core/ecs/store_layout_listener.d.ts.map +1 -0
- package/dist/core/ecs/system.d.cts +250 -0
- package/dist/core/ecs/system.d.ts +250 -0
- package/dist/core/ecs/system.d.ts.map +1 -0
- package/dist/core/ecs/utils/arrays.d.cts +7 -0
- package/dist/{utils → core/ecs/utils}/arrays.d.ts +2 -1
- package/dist/core/ecs/utils/arrays.d.ts.map +1 -0
- package/dist/core/ecs/utils/constants.d.ts +12 -0
- package/dist/core/ecs/utils/constants.d.ts.map +1 -0
- package/dist/core/ecs/utils/error.d.cts +61 -0
- package/dist/core/ecs/utils/error.d.ts +61 -0
- package/dist/core/ecs/utils/error.d.ts.map +1 -0
- package/dist/core/store/action_ring.d.cts +136 -0
- package/dist/core/store/action_ring.d.ts +136 -0
- package/dist/core/store/action_ring.d.ts.map +1 -0
- package/dist/core/store/allocator.d.cts +238 -0
- package/dist/core/store/allocator.d.ts +238 -0
- package/dist/core/store/allocator.d.ts.map +1 -0
- package/dist/core/store/buffer_backed_column.d.cts +69 -0
- package/dist/core/store/buffer_backed_column.d.ts +69 -0
- package/dist/core/store/buffer_backed_column.d.ts.map +1 -0
- package/dist/core/store/column_store.d.cts +273 -0
- package/dist/core/store/column_store.d.ts +273 -0
- package/dist/core/store/column_store.d.ts.map +1 -0
- package/dist/core/store/command_dispatch.d.cts +52 -0
- package/dist/core/store/command_dispatch.d.ts +52 -0
- package/dist/core/store/command_dispatch.d.ts.map +1 -0
- package/dist/core/store/command_ring.d.cts +107 -0
- package/dist/core/store/command_ring.d.ts +107 -0
- package/dist/core/store/command_ring.d.ts.map +1 -0
- package/dist/core/store/descriptor.d.cts +80 -0
- package/dist/core/store/descriptor.d.ts +80 -0
- package/dist/core/store/descriptor.d.ts.map +1 -0
- package/dist/core/store/entity_index.d.cts +108 -0
- package/dist/core/store/entity_index.d.ts +108 -0
- package/dist/core/store/entity_index.d.ts.map +1 -0
- package/dist/core/store/event_ring.d.cts +95 -0
- package/dist/core/store/event_ring.d.ts +95 -0
- package/dist/core/store/event_ring.d.ts.map +1 -0
- package/dist/core/store/extend.d.cts +39 -0
- package/dist/core/store/extend.d.ts +39 -0
- package/dist/core/store/extend.d.ts.map +1 -0
- package/dist/core/store/grow.d.cts +34 -0
- package/dist/core/store/grow.d.ts +34 -0
- package/dist/core/store/grow.d.ts.map +1 -0
- package/dist/core/store/header.d.cts +64 -0
- package/dist/core/store/header.d.ts +64 -0
- package/dist/core/store/header.d.ts.map +1 -0
- package/dist/core/store/index.d.cts +17 -0
- package/dist/core/store/index.d.ts +17 -0
- package/dist/core/store/index.d.ts.map +1 -0
- 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 +74 -0
- package/dist/core/store/region_table.d.ts.map +1 -0
- package/dist/core/store/snapshot.d.cts +43 -0
- package/dist/core/store/snapshot.d.ts +43 -0
- package/dist/core/store/snapshot.d.ts.map +1 -0
- package/dist/core/store/state_hash.d.cts +38 -0
- package/dist/core/store/state_hash.d.ts +38 -0
- package/dist/core/store/state_hash.d.ts.map +1 -0
- package/dist/core/store/store_regions.d.cts +38 -0
- package/dist/core/store/store_regions.d.ts +38 -0
- package/dist/core/store/store_regions.d.ts.map +1 -0
- package/dist/core/store/vendored_abi/abi.d.cts +43 -0
- package/dist/core/store/vendored_abi/abi.d.ts +43 -0
- package/dist/core/store/vendored_abi/abi.d.ts.map +1 -0
- 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 +170 -0
- package/dist/extensions/editor/editor.d.ts.map +1 -0
- package/dist/extensions/editor/field_handle.d.cts +40 -0
- package/dist/extensions/editor/field_handle.d.ts +40 -0
- package/dist/extensions/editor/field_handle.d.ts.map +1 -0
- package/dist/extensions/editor/index.cjs +1 -0
- package/dist/extensions/editor/index.d.cts +21 -0
- package/dist/extensions/editor/index.d.ts +21 -0
- package/dist/extensions/editor/index.d.ts.map +1 -0
- package/dist/extensions/editor/index.js +237 -0
- package/dist/extensions/reactive/ecs_sync.d.cts +214 -0
- package/dist/extensions/reactive/ecs_sync.d.ts +214 -0
- package/dist/extensions/reactive/ecs_sync.d.ts.map +1 -0
- package/dist/extensions/reactive/index.cjs +1 -0
- package/dist/extensions/reactive/index.d.cts +23 -0
- package/dist/extensions/reactive/index.d.ts +23 -0
- package/dist/extensions/reactive/index.d.ts.map +1 -0
- package/dist/extensions/reactive/index.js +215 -0
- package/dist/extensions/solid/index.cjs +1 -0
- package/dist/extensions/solid/index.d.cts +6 -0
- package/dist/extensions/solid/index.d.ts +6 -0
- package/dist/extensions/solid/index.d.ts.map +1 -0
- package/dist/extensions/solid/index.js +36 -0
- package/dist/extensions/solid/kernel_solid.d.cts +45 -0
- package/dist/extensions/solid/kernel_solid.d.ts +45 -0
- package/dist/extensions/solid/kernel_solid.d.ts.map +1 -0
- package/dist/host_commands-BF8QMi3c.cjs +1 -0
- package/dist/host_commands-i4cAeyL5.js +2318 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +65 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7080 -1289
- 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/interop-CT-REx0W.cjs +1 -0
- package/dist/interop-CcY6ASQc.js +18 -0
- package/dist/kernel-DgyrLFjW.js +227 -0
- package/dist/kernel-yWV3XnAb.cjs +1 -0
- package/dist/primitives.cjs +1 -0
- package/dist/primitives.d.cts +18 -0
- package/dist/primitives.d.ts +18 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +45 -0
- package/dist/reactive/array.d.cts +24 -0
- 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/reactive/interop.d.ts +19 -0
- package/dist/reactive/interop.d.ts.map +1 -0
- package/dist/reactive/kernel.d.cts +73 -0
- package/dist/reactive/kernel.d.ts +73 -0
- package/dist/reactive/kernel.d.ts.map +1 -0
- package/dist/reactive/map.d.cts +16 -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/reactive/struct.d.ts +10 -0
- 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 -0
- package/dist/shared.d.cts +22 -0
- package/dist/shared.d.ts +22 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +7 -0
- 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 +25 -9
- 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/binary_heap/binary_heap.d.ts +6 -2
- package/dist/type_primitives/binary_heap/binary_heap.d.ts.map +1 -1
- package/dist/type_primitives/bitset/bitset.d.cts +54 -0
- package/dist/type_primitives/bitset/bitset.d.ts +16 -4
- package/dist/type_primitives/bitset/bitset.d.ts.map +1 -1
- package/dist/type_primitives/brand.d.cts +23 -0
- package/dist/type_primitives/brand.d.ts +6 -1
- package/dist/type_primitives/brand.d.ts.map +1 -1
- package/dist/type_primitives/error.d.cts +16 -0
- package/dist/type_primitives/error.d.ts +8 -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 +12 -9
- package/dist/type_primitives/index.d.ts.map +1 -1
- package/dist/type_primitives/sparse_map/sparse_map.d.cts +30 -0
- package/dist/type_primitives/sparse_map/sparse_map.d.ts +7 -3
- package/dist/type_primitives/sparse_map/sparse_map.d.ts.map +1 -1
- package/dist/type_primitives/sparse_set/sparse_set.d.cts +28 -0
- package/dist/type_primitives/sparse_set/sparse_set.d.ts +4 -0
- package/dist/type_primitives/sparse_set/sparse_set.d.ts.map +1 -1
- package/dist/type_primitives/topological_sort/topological_sort.d.cts +29 -0
- package/dist/type_primitives/topological_sort/topological_sort.d.ts +7 -3
- package/dist/type_primitives/topological_sort/topological_sort.d.ts.map +1 -1
- package/dist/type_primitives/typed_arrays/typed_arrays.d.cts +127 -0
- package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +53 -16
- package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -1
- 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/utils/error.d.ts +2 -20
- package/dist/utils/error.d.ts.map +1 -1
- 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 +115 -5
- package/dist/archetype.d.ts +0 -108
- package/dist/archetype.d.ts.map +0 -1
- package/dist/component.d.ts +0 -45
- package/dist/component.d.ts.map +0 -1
- package/dist/ecs.d.ts +0 -104
- package/dist/ecs.d.ts.map +0 -1
- package/dist/entity.d.ts +0 -11
- package/dist/entity.d.ts.map +0 -1
- package/dist/event.d.ts +0 -30
- package/dist/event.d.ts.map +0 -1
- package/dist/query.d.ts +0 -94
- package/dist/query.d.ts.map +0 -1
- package/dist/ref.d.ts +0 -23
- package/dist/ref.d.ts.map +0 -1
- package/dist/resource.d.ts +0 -23
- package/dist/resource.d.ts.map +0 -1
- package/dist/schedule.d.ts +0 -45
- package/dist/schedule.d.ts.map +0 -1
- package/dist/store.d.ts +0 -118
- package/dist/store.d.ts.map +0 -1
- package/dist/system.d.ts +0 -16
- package/dist/system.d.ts.map +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
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { EntityID } from './entity.cjs';
|
|
2
|
+
import { SystemDescriptor } from './system.cjs';
|
|
3
|
+
import { SCHEDULE } from './schedule.cjs';
|
|
4
|
+
/** A deferred structural command issued through `ctx.commands.*` (`query.ts`).
|
|
5
|
+
* `spawn`/`despawn`/`enable`/`disable` carry a `null` component; `add`/`remove`
|
|
6
|
+
* carry the affected component's id. */
|
|
7
|
+
export type StructuralOp = "spawn" | "despawn" | "add" | "remove" | "enable" | "disable";
|
|
8
|
+
/** An observer edge that fired at a flush (`add`/`remove`/`enable`/`disable`) or
|
|
9
|
+
* at the post-update onSet detection point (`set`). */
|
|
10
|
+
export type ObserverOp = "add" | "remove" | "set" | "enable" | "disable";
|
|
11
|
+
/**
|
|
12
|
+
* The push sink. All callers are `DEV`-gated, so the methods receive raw ids
|
|
13
|
+
* and descriptor references (no string-building on the hot path); the recorder
|
|
14
|
+
* decides what to retain. Implementations must be side-effect-free with respect
|
|
15
|
+
* to the ECS — they observe, never mutate.
|
|
16
|
+
*/
|
|
17
|
+
export interface FrameTraceSink {
|
|
18
|
+
/** Opens a frame. Fired first in `ecs.update`, before any phase runs. */
|
|
19
|
+
tickBegin(tick: number, dt: number): void;
|
|
20
|
+
/** Closes the frame opened by `tickBegin` (after onSet + `clearEvents`). */
|
|
21
|
+
tickEnd(tick: number): void;
|
|
22
|
+
/** A scheduled system is about to run, in the given phase. */
|
|
23
|
+
systemStart(system: SystemDescriptor, phase: SCHEDULE): void;
|
|
24
|
+
/** The system that last `systemStart`'d has returned. */
|
|
25
|
+
systemEnd(system: SystemDescriptor): void;
|
|
26
|
+
/** A deferred structural command was enqueued (applied at the next flush). */
|
|
27
|
+
commandQueued(op: StructuralOp, entity: EntityID, component: number | null): void;
|
|
28
|
+
/** The deferred command buffer is about to drain for this phase. */
|
|
29
|
+
flushBegin(phase: SCHEDULE): void;
|
|
30
|
+
/** The deferred command buffer (and any observer cascade) has settled. */
|
|
31
|
+
flushEnd(phase: SCHEDULE): void;
|
|
32
|
+
/**
|
|
33
|
+
* A schedule phase has fully settled — every system in `phase` ran and the
|
|
34
|
+
* deferred command buffer (plus any observer cascade) flushed — so the live
|
|
35
|
+
* world sits at a consistent, fingerprint-able point WITHIN the frame. Fired
|
|
36
|
+
* once per phase that runs (each startup phase, each fixed step, and each
|
|
37
|
+
* update phase), immediately after `flushEnd(phase)`.
|
|
38
|
+
*
|
|
39
|
+
* This is the blessed seam for a consumer to read `stateHash()` between the
|
|
40
|
+
* phases of one `update()` and bisect a divergence to the exact phase that
|
|
41
|
+
* introduced it (#797 / ADR-0032). `flushEnd` marks the same instant, but it
|
|
42
|
+
* is an observation of the flush *mechanism*, not a "safe to read consistent
|
|
43
|
+
* state" contract — so per-phase fingerprinting binds to this method, not to a
|
|
44
|
+
* flush-internal name. Like every sink method the call is read-only w.r.t. the
|
|
45
|
+
* ECS: reading `stateHash()` here cannot perturb the per-tick hash or ordering.
|
|
46
|
+
*
|
|
47
|
+
* Caveat: the POST_UPDATE boundary fires BEFORE the tick-tail onSet-observer
|
|
48
|
+
* dispatch + `clearEvents`, so for a world with onSet observers the final
|
|
49
|
+
* per-tick `stateHash` (after `update()` returns) may differ from the
|
|
50
|
+
* POST_UPDATE phase hash. A world with no onSet observers reconciles exactly.
|
|
51
|
+
*/
|
|
52
|
+
phaseBoundary(phase: SCHEDULE): void;
|
|
53
|
+
/** An observer callback fired for one entity (`entity === -1` for an
|
|
54
|
+
* archetype-granular onSet, which has no per-entity id). */
|
|
55
|
+
observerFired(op: ObserverOp, component: number, entity: number, observer: SystemDescriptor): void;
|
|
56
|
+
/** A system emitted an event on `key` (the channel's symbol description). */
|
|
57
|
+
eventEmitted(key: string): void;
|
|
58
|
+
/** A system read an event channel; `count` is how many events it saw. */
|
|
59
|
+
eventRead(key: string, count: number): void;
|
|
60
|
+
}
|
|
61
|
+
/** One captured event, as plain JSON-serialisable data (string names + numeric
|
|
62
|
+
* ids), so a `FrameTrace[]` streams straight to a browser renderer. */
|
|
63
|
+
export type FrameTraceEvent = {
|
|
64
|
+
readonly kind: "system_start";
|
|
65
|
+
readonly system: string;
|
|
66
|
+
readonly phase: SCHEDULE;
|
|
67
|
+
} | {
|
|
68
|
+
readonly kind: "system_end";
|
|
69
|
+
readonly system: string;
|
|
70
|
+
} | {
|
|
71
|
+
readonly kind: "command_queued";
|
|
72
|
+
readonly op: StructuralOp;
|
|
73
|
+
readonly entity: number;
|
|
74
|
+
readonly component: number | null;
|
|
75
|
+
} | {
|
|
76
|
+
readonly kind: "flush_begin";
|
|
77
|
+
readonly phase: SCHEDULE;
|
|
78
|
+
} | {
|
|
79
|
+
readonly kind: "flush_end";
|
|
80
|
+
readonly phase: SCHEDULE;
|
|
81
|
+
} | {
|
|
82
|
+
readonly kind: "observer_fired";
|
|
83
|
+
readonly op: ObserverOp;
|
|
84
|
+
readonly component: number;
|
|
85
|
+
readonly entity: number;
|
|
86
|
+
readonly observer: string;
|
|
87
|
+
} | {
|
|
88
|
+
readonly kind: "event_emitted";
|
|
89
|
+
readonly key: string;
|
|
90
|
+
} | {
|
|
91
|
+
readonly kind: "event_read";
|
|
92
|
+
readonly key: string;
|
|
93
|
+
readonly count: number;
|
|
94
|
+
};
|
|
95
|
+
/** One frame's captured internals, in causal order. */
|
|
96
|
+
export interface FrameTrace {
|
|
97
|
+
readonly tick: number;
|
|
98
|
+
readonly dt: number;
|
|
99
|
+
readonly events: FrameTraceEvent[];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The in-tree `FrameTraceSink`. Mirrors `command_log.ts`'s `openTick` /
|
|
103
|
+
* `RecordedTick` shape: `tickBegin` opens a fresh `FrameTrace`, every other
|
|
104
|
+
* method appends to it, `tickEnd` closes it. Events arriving with no open
|
|
105
|
+
* frame (e.g. a stray pre-`update` flush) are ignored rather than throwing —
|
|
106
|
+
* the recorder is a passive observer.
|
|
107
|
+
*/
|
|
108
|
+
export declare class FrameTraceRecorder implements FrameTraceSink {
|
|
109
|
+
private readonly _frames;
|
|
110
|
+
private _current;
|
|
111
|
+
tickBegin(tick: number, dt: number): void;
|
|
112
|
+
tickEnd(_tick: number): void;
|
|
113
|
+
systemStart(system: SystemDescriptor, phase: SCHEDULE): void;
|
|
114
|
+
systemEnd(system: SystemDescriptor): void;
|
|
115
|
+
commandQueued(op: StructuralOp, entity: EntityID, component: number | null): void;
|
|
116
|
+
flushBegin(phase: SCHEDULE): void;
|
|
117
|
+
flushEnd(phase: SCHEDULE): void;
|
|
118
|
+
/** No-op (#797 / ADR-0032). The recorder already marks this instant with
|
|
119
|
+
* `flushEnd`, and a per-phase fingerprint is a consumer concern — the recorder
|
|
120
|
+
* holds no world reference to hash. The seam exists so a consumer's OWN sink can
|
|
121
|
+
* run code (e.g. `stateHash()`) at the safe post-flush point; the in-tree
|
|
122
|
+
* event-stream recorder has nothing to add, so it stays a stream of causal
|
|
123
|
+
* events, not fingerprints. */
|
|
124
|
+
phaseBoundary(_phase: SCHEDULE): void;
|
|
125
|
+
observerFired(op: ObserverOp, component: number, entity: number, observer: SystemDescriptor): void;
|
|
126
|
+
eventEmitted(key: string): void;
|
|
127
|
+
eventRead(key: string, count: number): void;
|
|
128
|
+
/** The captured frames, in `update()` order. */
|
|
129
|
+
frames(): readonly FrameTrace[];
|
|
130
|
+
/** Drop all captured frames (reuse the recorder for a fresh run). */
|
|
131
|
+
reset(): void;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=frame_trace.d.ts.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { EntityID } from './entity.js';
|
|
2
|
+
import { SystemDescriptor } from './system.js';
|
|
3
|
+
import { SCHEDULE } from './schedule.js';
|
|
4
|
+
/** A deferred structural command issued through `ctx.commands.*` (`query.ts`).
|
|
5
|
+
* `spawn`/`despawn`/`enable`/`disable` carry a `null` component; `add`/`remove`
|
|
6
|
+
* carry the affected component's id. */
|
|
7
|
+
export type StructuralOp = "spawn" | "despawn" | "add" | "remove" | "enable" | "disable";
|
|
8
|
+
/** An observer edge that fired at a flush (`add`/`remove`/`enable`/`disable`) or
|
|
9
|
+
* at the post-update onSet detection point (`set`). */
|
|
10
|
+
export type ObserverOp = "add" | "remove" | "set" | "enable" | "disable";
|
|
11
|
+
/**
|
|
12
|
+
* The push sink. All callers are `DEV`-gated, so the methods receive raw ids
|
|
13
|
+
* and descriptor references (no string-building on the hot path); the recorder
|
|
14
|
+
* decides what to retain. Implementations must be side-effect-free with respect
|
|
15
|
+
* to the ECS — they observe, never mutate.
|
|
16
|
+
*/
|
|
17
|
+
export interface FrameTraceSink {
|
|
18
|
+
/** Opens a frame. Fired first in `ecs.update`, before any phase runs. */
|
|
19
|
+
tickBegin(tick: number, dt: number): void;
|
|
20
|
+
/** Closes the frame opened by `tickBegin` (after onSet + `clearEvents`). */
|
|
21
|
+
tickEnd(tick: number): void;
|
|
22
|
+
/** A scheduled system is about to run, in the given phase. */
|
|
23
|
+
systemStart(system: SystemDescriptor, phase: SCHEDULE): void;
|
|
24
|
+
/** The system that last `systemStart`'d has returned. */
|
|
25
|
+
systemEnd(system: SystemDescriptor): void;
|
|
26
|
+
/** A deferred structural command was enqueued (applied at the next flush). */
|
|
27
|
+
commandQueued(op: StructuralOp, entity: EntityID, component: number | null): void;
|
|
28
|
+
/** The deferred command buffer is about to drain for this phase. */
|
|
29
|
+
flushBegin(phase: SCHEDULE): void;
|
|
30
|
+
/** The deferred command buffer (and any observer cascade) has settled. */
|
|
31
|
+
flushEnd(phase: SCHEDULE): void;
|
|
32
|
+
/**
|
|
33
|
+
* A schedule phase has fully settled — every system in `phase` ran and the
|
|
34
|
+
* deferred command buffer (plus any observer cascade) flushed — so the live
|
|
35
|
+
* world sits at a consistent, fingerprint-able point WITHIN the frame. Fired
|
|
36
|
+
* once per phase that runs (each startup phase, each fixed step, and each
|
|
37
|
+
* update phase), immediately after `flushEnd(phase)`.
|
|
38
|
+
*
|
|
39
|
+
* This is the blessed seam for a consumer to read `stateHash()` between the
|
|
40
|
+
* phases of one `update()` and bisect a divergence to the exact phase that
|
|
41
|
+
* introduced it (#797 / ADR-0032). `flushEnd` marks the same instant, but it
|
|
42
|
+
* is an observation of the flush *mechanism*, not a "safe to read consistent
|
|
43
|
+
* state" contract — so per-phase fingerprinting binds to this method, not to a
|
|
44
|
+
* flush-internal name. Like every sink method the call is read-only w.r.t. the
|
|
45
|
+
* ECS: reading `stateHash()` here cannot perturb the per-tick hash or ordering.
|
|
46
|
+
*
|
|
47
|
+
* Caveat: the POST_UPDATE boundary fires BEFORE the tick-tail onSet-observer
|
|
48
|
+
* dispatch + `clearEvents`, so for a world with onSet observers the final
|
|
49
|
+
* per-tick `stateHash` (after `update()` returns) may differ from the
|
|
50
|
+
* POST_UPDATE phase hash. A world with no onSet observers reconciles exactly.
|
|
51
|
+
*/
|
|
52
|
+
phaseBoundary(phase: SCHEDULE): void;
|
|
53
|
+
/** An observer callback fired for one entity (`entity === -1` for an
|
|
54
|
+
* archetype-granular onSet, which has no per-entity id). */
|
|
55
|
+
observerFired(op: ObserverOp, component: number, entity: number, observer: SystemDescriptor): void;
|
|
56
|
+
/** A system emitted an event on `key` (the channel's symbol description). */
|
|
57
|
+
eventEmitted(key: string): void;
|
|
58
|
+
/** A system read an event channel; `count` is how many events it saw. */
|
|
59
|
+
eventRead(key: string, count: number): void;
|
|
60
|
+
}
|
|
61
|
+
/** One captured event, as plain JSON-serialisable data (string names + numeric
|
|
62
|
+
* ids), so a `FrameTrace[]` streams straight to a browser renderer. */
|
|
63
|
+
export type FrameTraceEvent = {
|
|
64
|
+
readonly kind: "system_start";
|
|
65
|
+
readonly system: string;
|
|
66
|
+
readonly phase: SCHEDULE;
|
|
67
|
+
} | {
|
|
68
|
+
readonly kind: "system_end";
|
|
69
|
+
readonly system: string;
|
|
70
|
+
} | {
|
|
71
|
+
readonly kind: "command_queued";
|
|
72
|
+
readonly op: StructuralOp;
|
|
73
|
+
readonly entity: number;
|
|
74
|
+
readonly component: number | null;
|
|
75
|
+
} | {
|
|
76
|
+
readonly kind: "flush_begin";
|
|
77
|
+
readonly phase: SCHEDULE;
|
|
78
|
+
} | {
|
|
79
|
+
readonly kind: "flush_end";
|
|
80
|
+
readonly phase: SCHEDULE;
|
|
81
|
+
} | {
|
|
82
|
+
readonly kind: "observer_fired";
|
|
83
|
+
readonly op: ObserverOp;
|
|
84
|
+
readonly component: number;
|
|
85
|
+
readonly entity: number;
|
|
86
|
+
readonly observer: string;
|
|
87
|
+
} | {
|
|
88
|
+
readonly kind: "event_emitted";
|
|
89
|
+
readonly key: string;
|
|
90
|
+
} | {
|
|
91
|
+
readonly kind: "event_read";
|
|
92
|
+
readonly key: string;
|
|
93
|
+
readonly count: number;
|
|
94
|
+
};
|
|
95
|
+
/** One frame's captured internals, in causal order. */
|
|
96
|
+
export interface FrameTrace {
|
|
97
|
+
readonly tick: number;
|
|
98
|
+
readonly dt: number;
|
|
99
|
+
readonly events: FrameTraceEvent[];
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The in-tree `FrameTraceSink`. Mirrors `command_log.ts`'s `openTick` /
|
|
103
|
+
* `RecordedTick` shape: `tickBegin` opens a fresh `FrameTrace`, every other
|
|
104
|
+
* method appends to it, `tickEnd` closes it. Events arriving with no open
|
|
105
|
+
* frame (e.g. a stray pre-`update` flush) are ignored rather than throwing —
|
|
106
|
+
* the recorder is a passive observer.
|
|
107
|
+
*/
|
|
108
|
+
export declare class FrameTraceRecorder implements FrameTraceSink {
|
|
109
|
+
private readonly _frames;
|
|
110
|
+
private _current;
|
|
111
|
+
tickBegin(tick: number, dt: number): void;
|
|
112
|
+
tickEnd(_tick: number): void;
|
|
113
|
+
systemStart(system: SystemDescriptor, phase: SCHEDULE): void;
|
|
114
|
+
systemEnd(system: SystemDescriptor): void;
|
|
115
|
+
commandQueued(op: StructuralOp, entity: EntityID, component: number | null): void;
|
|
116
|
+
flushBegin(phase: SCHEDULE): void;
|
|
117
|
+
flushEnd(phase: SCHEDULE): void;
|
|
118
|
+
/** No-op (#797 / ADR-0032). The recorder already marks this instant with
|
|
119
|
+
* `flushEnd`, and a per-phase fingerprint is a consumer concern — the recorder
|
|
120
|
+
* holds no world reference to hash. The seam exists so a consumer's OWN sink can
|
|
121
|
+
* run code (e.g. `stateHash()`) at the safe post-flush point; the in-tree
|
|
122
|
+
* event-stream recorder has nothing to add, so it stays a stream of causal
|
|
123
|
+
* events, not fingerprints. */
|
|
124
|
+
phaseBoundary(_phase: SCHEDULE): void;
|
|
125
|
+
observerFired(op: ObserverOp, component: number, entity: number, observer: SystemDescriptor): void;
|
|
126
|
+
eventEmitted(key: string): void;
|
|
127
|
+
eventRead(key: string, count: number): void;
|
|
128
|
+
/** The captured frames, in `update()` order. */
|
|
129
|
+
frames(): readonly FrameTrace[];
|
|
130
|
+
/** Drop all captured frames (reuse the recorder for a fresh run). */
|
|
131
|
+
reset(): void;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=frame_trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame_trace.d.ts","sourceRoot":"","sources":["../../../src/core/ecs/frame_trace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;wCAEwC;AACxC,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzF;uDACuD;AACvD,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC9B,yEAAyE;IACzE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,4EAA4E;IAC5E,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,8DAA8D;IAC9D,WAAW,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC7D,yDAAyD;IACzD,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAC1C,8EAA8E;IAC9E,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAClF,oEAAoE;IACpE,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClC,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IAChC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IACrC;gEAC4D;IAC5D,aAAa,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACnG,6EAA6E;IAC7E,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,yEAAyE;IACzE,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5C;AAMD;uEACuE;AACvE,MAAM,MAAM,eAAe,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GACpF;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxD;IACA,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,GACxD;IACA,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CACzB,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,uDAAuD;AACvD,MAAM,WAAW,UAAU;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC;CACnC;AAQD;;;;;;GAMG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAA2B;IAE3C,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAMzC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI5B,WAAW,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,GAAG,IAAI;IAI5D,SAAS,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAIzC,aAAa,CAAC,EAAE,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIjF,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAIjC,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAI/B;;;;;mCAK+B;IAC/B,aAAa,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI;IAErC,aAAa,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAUlG,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAI/B,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI3C,gDAAgD;IAChD,MAAM,IAAI,SAAS,UAAU,EAAE;IAI/B,qEAAqE;IACrE,KAAK,IAAI,IAAI;CAIb"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { ComponentDef, ComponentSchema, CompleteFieldValues, FieldValues } from './component.cjs';
|
|
2
|
+
import { ECS } from './ecs.cjs';
|
|
3
|
+
import { EntityID } from './entity.cjs';
|
|
4
|
+
import { SystemContext } from './query.cjs';
|
|
5
|
+
import { SCHEDULE } from './schedule.cjs';
|
|
6
|
+
import { PayloadCodec } from '../store/index.cjs';
|
|
7
|
+
/** One component to attach to a freshly spawned entity. `values` are required
|
|
8
|
+
* and complete as a *strictness* choice, not a runtime need: since #716 every
|
|
9
|
+
* attach path (deferred included — `writeFields`'s `?? 0`) zero-fills omitted
|
|
10
|
+
* fields, same as templates. A host command is a reified, replayable record,
|
|
11
|
+
* so it carries explicit intent for every field rather than relying on the
|
|
12
|
+
* zero-fill; a tag component takes `{}`. Build type-safely with `spawnEntry`. */
|
|
13
|
+
export interface SpawnEntry {
|
|
14
|
+
readonly def: ComponentDef;
|
|
15
|
+
readonly values: FieldValues<ComponentSchema>;
|
|
16
|
+
}
|
|
17
|
+
/** Extracts the schema out of a `ComponentDef` handle. */
|
|
18
|
+
type SchemaOf<D extends ComponentDef> = D extends ComponentDef<infer S> ? S : ComponentSchema;
|
|
19
|
+
/** One schema-checked spawn entry: `values` is complete for its own def (see
|
|
20
|
+
* the `SpawnEntry` doc — explicit intent per field, though the attach path
|
|
21
|
+
* zero-fills since #716), and a tag takes exactly `{}`. */
|
|
22
|
+
export type SpawnEntryFor<D extends ComponentDef> = {
|
|
23
|
+
readonly def: D;
|
|
24
|
+
readonly values: CompleteFieldValues<SchemaOf<D>>;
|
|
25
|
+
};
|
|
26
|
+
/** The entries tuple for `HostCommandQueue.spawn` — each element's `values` is
|
|
27
|
+
* checked against its own `def`'s schema. Unlike `TemplateEntries` (which
|
|
28
|
+
* stays `Partial` — a template is a reusable default set), a host command
|
|
29
|
+
* demands complete values: it is a reified, replayable record, so every
|
|
30
|
+
* field is explicit even though the attach path would zero-fill (#716). */
|
|
31
|
+
export type SpawnEntries<Defs extends readonly ComponentDef[]> = readonly [
|
|
32
|
+
...{
|
|
33
|
+
[K in keyof Defs]: SpawnEntryFor<Defs[K]>;
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
/** Type-checked `SpawnEntry` constructor — keeps `values` aligned to `def`'s
|
|
37
|
+
* schema at the call site even though the stored entry is schema-erased. */
|
|
38
|
+
export declare function spawnEntry<S extends ComponentSchema>(def: ComponentDef<S>, values: CompleteFieldValues<S>): SpawnEntry;
|
|
39
|
+
/**
|
|
40
|
+
* A single world mutation, as plain data. Every transport (typed queue today, a
|
|
41
|
+
* SAB ring codec tomorrow) and every consumer (editor undo, record/replay)
|
|
42
|
+
* speaks this one vocabulary, applied by the one `applyHostCommand` dispatch.
|
|
43
|
+
*
|
|
44
|
+
* Component fields are `string` / `def: ComponentDef` (schema-erased) so the
|
|
45
|
+
* union stays flat; the typed `HostCommandQueue` methods preserve type-safety at
|
|
46
|
+
* the enqueue site. `applyHostCommand` re-applies them through `SystemContext`,
|
|
47
|
+
* which type-checks because `ComponentDef` defaults its schema to
|
|
48
|
+
* `ComponentSchema` (whose `keyof` is `string`).
|
|
49
|
+
*/
|
|
50
|
+
export type HostCommand = {
|
|
51
|
+
readonly kind: "spawn";
|
|
52
|
+
readonly components: readonly SpawnEntry[];
|
|
53
|
+
/** Fired with the new id once the spawn applies (the id only exists
|
|
54
|
+
* after the deferred create) — lets a producer/editor learn it. */
|
|
55
|
+
readonly onSpawned?: (eid: EntityID) => void;
|
|
56
|
+
} | {
|
|
57
|
+
readonly kind: "despawn";
|
|
58
|
+
readonly eid: EntityID;
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: "add_component";
|
|
61
|
+
readonly eid: EntityID;
|
|
62
|
+
readonly def: ComponentDef;
|
|
63
|
+
readonly values: FieldValues<ComponentSchema>;
|
|
64
|
+
} | {
|
|
65
|
+
readonly kind: "remove_component";
|
|
66
|
+
readonly eid: EntityID;
|
|
67
|
+
readonly def: ComponentDef;
|
|
68
|
+
} | {
|
|
69
|
+
readonly kind: "set_field";
|
|
70
|
+
readonly eid: EntityID;
|
|
71
|
+
readonly def: ComponentDef;
|
|
72
|
+
readonly field: string;
|
|
73
|
+
readonly value: number;
|
|
74
|
+
} | {
|
|
75
|
+
readonly kind: "disable";
|
|
76
|
+
readonly eid: EntityID;
|
|
77
|
+
} | {
|
|
78
|
+
readonly kind: "enable";
|
|
79
|
+
readonly eid: EntityID;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* THE ONE APPLY DISPATCH. Maps a `HostCommand` onto `SystemContext` ops. Only
|
|
83
|
+
* ever called from inside the blessed apply system, which holds the `ctx` and is
|
|
84
|
+
* `exclusive` (full access). Structural changes (`spawn`/`despawn`/component
|
|
85
|
+
* add-remove) are deferred to the phase flush, exactly like a normal system's;
|
|
86
|
+
* `setField` is immediate and bumps the change-tick. Returns the new entity for
|
|
87
|
+
* `spawn`, otherwise `undefined`.
|
|
88
|
+
*
|
|
89
|
+
* That immediate/deferred split is a sharp edge: a `setField` targeting a
|
|
90
|
+
* component the entity does NOT yet have — because an `addComponent`/`spawn`
|
|
91
|
+
* enqueued in the SAME drain is still pending its flush — would otherwise fail
|
|
92
|
+
* deep in `getColumn` with an opaque "component not registered". The `DEV`
|
|
93
|
+
* guard below turns that into an actionable message. The fix is structural, not a
|
|
94
|
+
* retry: pass the value in `addComponent`/`spawnEntry` (which carries complete
|
|
95
|
+
* field values), or issue the `setField` on a later frame.
|
|
96
|
+
*/
|
|
97
|
+
export declare function applyHostCommand(ctx: SystemContext, cmd: HostCommand): EntityID | undefined;
|
|
98
|
+
/**
|
|
99
|
+
* The host-facing write handle. Mutating methods ENQUEUE (off-schedule, pure);
|
|
100
|
+
* nothing reaches the world until the apply system drains it at the next
|
|
101
|
+
* schedule head. Mirrors Bevy's `Commands` ergonomics over the flat `HostCommand`
|
|
102
|
+
* vocabulary. The returned-from-`installHostCommandSeam` instance is the
|
|
103
|
+
* write counterpart to the reactive bridge's returned `reactiveMap`.
|
|
104
|
+
*/
|
|
105
|
+
export declare class HostCommandQueue {
|
|
106
|
+
private readonly queued;
|
|
107
|
+
/** Spawn an entity carrying `components`. `onSpawned` receives the new id
|
|
108
|
+
* once the spawn applies. Each entry's `values` is checked against its own
|
|
109
|
+
* `def`'s schema (`SpawnEntries`); the stored command stays schema-erased. */
|
|
110
|
+
spawn<Defs extends readonly ComponentDef[]>(components: SpawnEntries<Defs>, onSpawned?: (eid: EntityID) => void): void;
|
|
111
|
+
despawn(eid: EntityID): void;
|
|
112
|
+
addComponent<S extends ComponentSchema>(eid: EntityID, def: ComponentDef<S>, values: CompleteFieldValues<S>): void;
|
|
113
|
+
removeComponent(eid: EntityID, def: ComponentDef): void;
|
|
114
|
+
/** Set `field` of `def` on `eid`. Applied IMMEDIATELY at the drain, unlike the
|
|
115
|
+
* deferred structural ops — so `def` must already be on `eid`. Do NOT
|
|
116
|
+
* `addComponent`/`spawn` `def` and `setField` it in the same frame: the add is
|
|
117
|
+
* still pending its flush when the immediate set runs (carry the value in
|
|
118
|
+
* `addComponent`/`spawnEntry` instead). `applyHostCommand` throws an
|
|
119
|
+
* actionable error in `DEV` if you do. */
|
|
120
|
+
setField<S extends ComponentSchema>(eid: EntityID, def: ComponentDef<S>, field: string & keyof S, value: number): void;
|
|
121
|
+
disable(eid: EntityID): void;
|
|
122
|
+
enable(eid: EntityID): void;
|
|
123
|
+
/** Enqueue a pre-built command. The path for a SAB-ring codec, an editor's
|
|
124
|
+
* reified inverse, or a replay log — all of which produce `HostCommand` data
|
|
125
|
+
* directly rather than calling the typed sugar above. */
|
|
126
|
+
push(cmd: HostCommand): void;
|
|
127
|
+
/** Commands buffered but not yet applied. */
|
|
128
|
+
pending(): number;
|
|
129
|
+
/** Drop every buffered command without applying it (M15) — e.g. abandoning
|
|
130
|
+
* queued edits on a scene unload. Returns how many were dropped. Does not
|
|
131
|
+
* touch commands already drained into the world. */
|
|
132
|
+
clear(): number;
|
|
133
|
+
/**
|
|
134
|
+
* Apply and clear every buffered command. Called by the blessed apply system
|
|
135
|
+
* inside its schedule span; not part of the host-facing surface. Returns the
|
|
136
|
+
* count applied.
|
|
137
|
+
*
|
|
138
|
+
* `tap`, when present, is invoked with each command in apply order just before
|
|
139
|
+
* it is applied — the record/replay hook ({@link HostCommandRecorder}). It is
|
|
140
|
+
* an OPT-IN observer: the tap-free path keeps the original tight loop, so an
|
|
141
|
+
* un-recorded drain pays nothing (#702).
|
|
142
|
+
*/
|
|
143
|
+
drain(ctx: SystemContext, tap?: (cmd: HostCommand) => void): number;
|
|
144
|
+
}
|
|
145
|
+
/** Bytes of the payload region inside one `command_ring` slot (15). The slot's
|
|
146
|
+
* leading byte is the opCode; `COMMAND_RING_SLOT_BYTES` (16) is the whole slot. */
|
|
147
|
+
export declare const HOST_COMMAND_PAYLOAD_BYTES: number;
|
|
148
|
+
/**
|
|
149
|
+
* Ring codec for a `setField` on a FIXED `(def, field)`. The component + field
|
|
150
|
+
* are bound INTO the codec, not carried in the bytes — the consumer-owned-codec
|
|
151
|
+
* rule (mirroring `command_dispatch`): the engine ships the mechanism, a
|
|
152
|
+
* consumer's codec knows which component+field an opcode means.
|
|
153
|
+
*
|
|
154
|
+
* Payload (15 B): `[ eid: u32 LE @0 ][ value: f64 LE @4 ][ _reserved: 3 B @12 ]`
|
|
155
|
+
*
|
|
156
|
+
* `value` rides as `f64` (covers every numeric column type losslessly within
|
|
157
|
+
* range); the trailing 3 bytes stay zero. Packing the id as `u32` is what makes
|
|
158
|
+
* `setField` fit the 15-byte slot.
|
|
159
|
+
*/
|
|
160
|
+
export declare function ringSetFieldCodec<S extends ComponentSchema>(def: ComponentDef<S>, field: string & keyof S): PayloadCodec<HostCommand>;
|
|
161
|
+
/** Ring codec for `despawn` — `[ eid: u32 LE @0 ]`, trailing 11 B reserved-zero. */
|
|
162
|
+
export declare function ringDespawnCodec(): PayloadCodec<HostCommand>;
|
|
163
|
+
/** Ring codec for `disable` — `[ eid: u32 LE @0 ]`, trailing 11 B reserved-zero. */
|
|
164
|
+
export declare function ringDisableCodec(): PayloadCodec<HostCommand>;
|
|
165
|
+
/** Ring codec for `enable` — `[ eid: u32 LE @0 ]`, trailing 11 B reserved-zero. */
|
|
166
|
+
export declare function ringEnableCodec(): PayloadCodec<HostCommand>;
|
|
167
|
+
/**
|
|
168
|
+
* Ring codec for `removeComponent` of a FIXED `def` — `[ eid: u32 LE @0 ]`, the
|
|
169
|
+
* component bound into the codec (consumer-owned-codec rule). `spawn` /
|
|
170
|
+
* `addComponent` are deliberately absent: they carry component field values
|
|
171
|
+
* that don't fit the 15-byte slot generically, so they stay typed-transport-only
|
|
172
|
+
* (the prototype's finding — the in-process queue has no width limit).
|
|
173
|
+
*/
|
|
174
|
+
export declare function ringRemoveComponentCodec(def: ComponentDef): PayloadCodec<HostCommand>;
|
|
175
|
+
/** A ctx-aware applier for one ring opcode: runs on the system `ctx` (which the
|
|
176
|
+
* apply system holds) with the raw 15-byte payload. `tap`, when present, is the
|
|
177
|
+
* record/replay hook (#702) — a generic, `onCommand`-bound applier decodes the
|
|
178
|
+
* slot to a `HostCommand` and feeds it to `tap` before applying, so ring-sourced
|
|
179
|
+
* commands land in the same log as typed-queue ones. A raw `on` applier (a
|
|
180
|
+
* consumer's own non-`HostCommand` op, e.g. the game's `spawn_unit`) has no
|
|
181
|
+
* `HostCommand` to surface and simply ignores `tap`. */
|
|
182
|
+
export type RingCommandApplier = (ctx: SystemContext, payload: Uint8Array, tap?: (cmd: HostCommand) => void) => void;
|
|
183
|
+
/**
|
|
184
|
+
* Drains the SAB `command_ring` as the second host-command transport, dispatching
|
|
185
|
+
* each opcode to a bound applier. Two binding styles:
|
|
186
|
+
*
|
|
187
|
+
* - `onCommand(op, codec)` — decode the slot to a `HostCommand` and run it
|
|
188
|
+
* through the ONE `applyHostCommand` (the same dispatch the typed queue
|
|
189
|
+
* uses). This is the generic cross-thread / wire host-write path.
|
|
190
|
+
* - `on(op, applier)` — a raw ctx-aware handler for a consumer's OWN ring ops
|
|
191
|
+
* that aren't generic host commands (e.g. the game's `spawn_unit`, which runs
|
|
192
|
+
* a BFS placement + game spawn). Same drain, same ring: the "one bus".
|
|
193
|
+
*
|
|
194
|
+
* Opcodes + codecs are CONSUMER-supplied — the engine ships the mechanism and the
|
|
195
|
+
* `ring_*_codec` factories, never the opcode numbers (mirrors `command_dispatch`).
|
|
196
|
+
*
|
|
197
|
+
* Exactly ONE dispatcher should drain a given ring — a second drain would consume
|
|
198
|
+
* the first's slots. The in-process apply system passes its dispatcher via
|
|
199
|
+
* `installHostCommandSeam({ ring })`; a system that owns a timing-coupled drain
|
|
200
|
+
* point (e.g. `wave_spawn`, which must drain between the spatial-index build and
|
|
201
|
+
* the flow-field rebuild) constructs one and calls `drain` itself.
|
|
202
|
+
*/
|
|
203
|
+
export declare class HostCommandDispatcher {
|
|
204
|
+
private readonly appliers;
|
|
205
|
+
/** Bind a raw ctx-aware applier to `opCode`. Re-binding replaces. */
|
|
206
|
+
on(opCode: number, applier: RingCommandApplier): this;
|
|
207
|
+
/** Unbind `opCode` (M15). Returns whether a binding was removed; subsequent
|
|
208
|
+
* slots carrying it hit the unknown-opcode path. */
|
|
209
|
+
off(opCode: number): boolean;
|
|
210
|
+
/** Bind a `HostCommand` codec to `opCode`: each matching slot is decoded and
|
|
211
|
+
* run through `applyHostCommand` — the SAME dispatch the typed queue uses.
|
|
212
|
+
* A drain-time `tap` (record/replay, #702) sees the decoded command before it
|
|
213
|
+
* applies, so ring-sourced commands share one log with the typed transport. */
|
|
214
|
+
onCommand(opCode: number, codec: PayloadCodec<HostCommand>): this;
|
|
215
|
+
/** Drain every pending slot, dispatching each to its bound applier. Unbound
|
|
216
|
+
* opcodes are skipped (the read head still advances — matching
|
|
217
|
+
* `drainCommandRing` / `CommandDispatcher`). Returns slots drained. `tap`,
|
|
218
|
+
* when present, is forwarded to each applier as the record/replay hook (#702);
|
|
219
|
+
* only `onCommand`-bound (generic `HostCommand`) opcodes surface to it. */
|
|
220
|
+
drain(ctx: SystemContext, view: DataView, ringOff: number, tap?: (cmd: HostCommand) => void): number;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* A per-tick sink the apply system feeds drained commands into — the record side
|
|
224
|
+
* of record/replay (#702). Declared structurally HERE (not imported from
|
|
225
|
+
* `command_log.ts`) so the seam needs no dependency on the recorder: the one-way
|
|
226
|
+
* edge is `command_log` → `host_commands`, never back. {@link HostCommandRecorder}
|
|
227
|
+
* is the in-tree implementation.
|
|
228
|
+
*
|
|
229
|
+
* The protocol the apply system follows: at each UPDATE-phase drain it calls
|
|
230
|
+
* `openTick(tick, dt)` to open that tick's bucket; STARTUP-phase drains skip
|
|
231
|
+
* `openTick`, so seed-time commands land in the recorder's startup bucket. Then
|
|
232
|
+
* every applied command (both transports) is handed to `record`.
|
|
233
|
+
*/
|
|
234
|
+
export interface HostCommandSink {
|
|
235
|
+
/** Open (or reuse) the bucket for update `tick` with its `dt`. Multiple drains
|
|
236
|
+
* in one tick (several update phases) reuse the one bucket. */
|
|
237
|
+
openTick(tick: number, dt: number): void;
|
|
238
|
+
/** Record one applied command, in apply order. Pre-bound so it can be passed
|
|
239
|
+
* straight as a drain `tap` without per-tick closure allocation. */
|
|
240
|
+
readonly record: (cmd: HostCommand) => void;
|
|
241
|
+
}
|
|
242
|
+
/** Options for {@link installHostCommandSeam}. */
|
|
243
|
+
export interface HostCommandSeamOptions {
|
|
244
|
+
/** Schedule phases whose head drains the queue. Default
|
|
245
|
+
* `[PRE_STARTUP, PRE_UPDATE]` — seed-time edits plus every frame. */
|
|
246
|
+
readonly schedules?: readonly SCHEDULE[];
|
|
247
|
+
/** Apply-system name (diagnostics). Default `"host_command_apply"`. */
|
|
248
|
+
readonly name?: string;
|
|
249
|
+
/** When provided, the apply system ALSO drains the world's SAB `command_ring`
|
|
250
|
+
* through this dispatcher at each schedule head — the cross-thread / wire
|
|
251
|
+
* transport, resolving to the same `applyHostCommand` as the typed queue.
|
|
252
|
+
* The ECS `Store` always allocates a ring; if one is somehow absent
|
|
253
|
+
* (`command_ring_off === 0`) the ring drain is a no-op. Bind opcodes with the
|
|
254
|
+
* `ring_*_codec` factories, or `dispatcher.on` for a consumer's own ops. */
|
|
255
|
+
readonly ring?: HostCommandDispatcher;
|
|
256
|
+
/** When provided, every command the apply system drains — from BOTH transports
|
|
257
|
+
* (typed queue + `onCommand`-bound ring ops) — is logged into this sink,
|
|
258
|
+
* tagged with the tick + `dt`, for record/replay (#702). Off by default: an
|
|
259
|
+
* un-recorded seam keeps the original tap-free drain and pays nothing.
|
|
260
|
+
* {@link HostCommandRecorder} is the in-tree sink; replay it with
|
|
261
|
+
* `replayCommandLog`. */
|
|
262
|
+
readonly recorder?: HostCommandSink;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Tear down a seam installed by {@link installHostCommandSeam} (M15): removes
|
|
266
|
+
* its apply systems from the world's schedule and clears any still-buffered
|
|
267
|
+
* commands. The queue itself stays usable as a buffer, but nothing drains it
|
|
268
|
+
* until a new seam is installed. No-op (returns `false`) if `queue` was not
|
|
269
|
+
* produced by `installHostCommandSeam` on this world.
|
|
270
|
+
*/
|
|
271
|
+
export declare function uninstallHostCommandSeam(ecs: ECS, queue: HostCommandQueue): boolean;
|
|
272
|
+
export declare function installHostCommandSeam(ecs: ECS, opts?: HostCommandSeamOptions): HostCommandQueue;
|
|
273
|
+
export {};
|
|
274
|
+
//# sourceMappingURL=host_commands.d.ts.map
|