@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,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command dispatch — the generic drain surface a consumer registers against
|
|
3
|
+
* (#624, game-agnostic ECS / epic #616).
|
|
4
|
+
*
|
|
5
|
+
* The command ring (`command_ring.ts`) carries opaque `(opCode, payload)`
|
|
6
|
+
* slots; the engine never interprets a code. This module is the thin,
|
|
7
|
+
* game-free glue that lets a consumer bind a payload codec + typed handler to
|
|
8
|
+
* each opcode and drain the ring in one call. The opcode enum and the codecs
|
|
9
|
+
* themselves stay consumer-owned — for our game they live in
|
|
10
|
+
* `@internal/sim`'s `command_payloads.ts` (`COMMAND_OP`, `SpawnUnitFields`,
|
|
11
|
+
* `encode/decode_spawn_unit_payload`); the engine knows none of those names.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
*
|
|
15
|
+
* const dispatcher = new CommandDispatcher()
|
|
16
|
+
* .on(COMMAND_OP.spawn_unit, spawn_unit_codec, (fields) => spawn(fields));
|
|
17
|
+
* dispatcher.drain(view, ringOff); // runs after wasm.tick() returns
|
|
18
|
+
*
|
|
19
|
+
* Unregistered opcodes are skipped (the same forward-compatible stance as the
|
|
20
|
+
* raw `drainCommandRing` handler that ignores codes it doesn't know).
|
|
21
|
+
*/
|
|
22
|
+
/** Decode (and, symmetrically, encode) the 15-byte payload region of a
|
|
23
|
+
* command slot into a typed value. A consumer supplies one per opcode it
|
|
24
|
+
* cares about; the engine only ever calls `decode` during a drain, but the
|
|
25
|
+
* `encode` half keeps the codec a single round-trippable unit (and is what
|
|
26
|
+
* test/host producers use to push). */
|
|
27
|
+
export interface PayloadCodec<T> {
|
|
28
|
+
/** Encode `value` into a fresh `COMMAND_RING_SLOT_BYTES - 1` (15) byte
|
|
29
|
+
* payload, ready for `pushCommand(view, off, op, payload)`. */
|
|
30
|
+
encode(value: T): Uint8Array;
|
|
31
|
+
/** Decode a 15-byte payload back into the typed value. */
|
|
32
|
+
decode(payload: Uint8Array): T;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Registry mapping command opcodes to a payload codec + handler. Generic over
|
|
36
|
+
* the consumer's opcodes — the engine ships the mechanism; the game supplies
|
|
37
|
+
* the codes and codecs.
|
|
38
|
+
*/
|
|
39
|
+
export declare class CommandDispatcher {
|
|
40
|
+
private readonly bindings;
|
|
41
|
+
/** Register `handler` for `opCode`, decoding each slot with `codec`.
|
|
42
|
+
* Re-registering an opcode replaces its binding. `opCode` must be a u8 in
|
|
43
|
+
* `[1, 255]` — `0` is the reserved empty-slot marker and can never carry a
|
|
44
|
+
* command. Returns `this` for chaining. */
|
|
45
|
+
on<T>(opCode: number, codec: PayloadCodec<T>, handler: (value: T) => void): this;
|
|
46
|
+
/** Drain every pending command, decoding + dispatching each to its
|
|
47
|
+
* registered handler. Commands with no registered opcode are skipped.
|
|
48
|
+
* Returns the number of slots drained (including skipped ones — the ring is
|
|
49
|
+
* advanced regardless, matching `drainCommandRing`). */
|
|
50
|
+
drain(view: DataView, ringOff: number): number;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=command_dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command_dispatch.d.ts","sourceRoot":"","sources":["../../../src/core/store/command_dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH;;;;uCAIuC;AACvC,MAAM,WAAW,YAAY,CAAC,CAAC;IAC9B;mEAC+D;IAC/D,MAAM,CAAC,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC;IAC7B,0DAA0D;IAC1D,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,CAAC,CAAC;CAC/B;AAWD;;;;GAIG;AACH,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAE7D;;;+CAG2C;IAC3C,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,IAAI;IAgBhF;;;4DAGwD;IACxD,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;CAO9C"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command ring — WASM-side producer / TS-side consumer SPSC ring buffer
|
|
3
|
+
* for structural-change intents emitted during `sim.tick()`. Plan §7.5
|
|
4
|
+
* (`docs/ideas/buffer-wasm-sim-plan-2026-05-14T1600.md`).
|
|
5
|
+
*
|
|
6
|
+
* Layout:
|
|
7
|
+
*
|
|
8
|
+
* [ write_head: u32 ] slot 0..N-1, monotonic (NOT slot-modulo)
|
|
9
|
+
* [ read_head: u32 ] slot 0..N-1, monotonic
|
|
10
|
+
* [ capacity: u32 ] slot count, power-of-two
|
|
11
|
+
* [ overflow: u32 ] 0 = OK; 1 = WASM exhausted the ring this tick
|
|
12
|
+
* [ slot 0: 16 B ] opCode: u8, payload: [15]u8
|
|
13
|
+
* [ slot 1: 16 B ] ...
|
|
14
|
+
* ...
|
|
15
|
+
*
|
|
16
|
+
* SPSC contract (Phase 4 scope):
|
|
17
|
+
* - Producer: WASM `sim.tick()`. Pushes 0..N commands during one tick;
|
|
18
|
+
* bumps `write_head` after each.
|
|
19
|
+
* - Consumer: TS host, immediately after `wasm.tick()` returns. Drains
|
|
20
|
+
* 0..N pending commands; bumps `read_head` after each.
|
|
21
|
+
* - The two never run concurrently in Phase 4 (single host thread
|
|
22
|
+
* orchestrates both). Phase 5's worker offload promotes the head
|
|
23
|
+
* bumps to `Atomics.store`; that's an additive change without
|
|
24
|
+
* altering the layout.
|
|
25
|
+
*
|
|
26
|
+
* Overflow:
|
|
27
|
+
* - If WASM would write a slot when (`write_head - read_head == capacity`),
|
|
28
|
+
* it sets `overflow = 1` and drops the command. TS treats overflow as
|
|
29
|
+
* a hard error in dev builds; production logs and continues (a command
|
|
30
|
+
* might be lost rather than crash the host).
|
|
31
|
+
*
|
|
32
|
+
* Slot format (plan §7.5):
|
|
33
|
+
* byte 0: opCode (u8). 0 is reserved as the empty-slot marker
|
|
34
|
+
* (`COMMAND_OP_EMPTY`); all other codes are consumer-defined.
|
|
35
|
+
* The engine never interprets a code — it drains
|
|
36
|
+
* `(opCode, payload)` and hands them to the attached
|
|
37
|
+
* consumer, which owns the opcode enum + payload codecs (the
|
|
38
|
+
* game's live in `@internal/sim`'s `command_payloads.ts`).
|
|
39
|
+
* bytes 1..15: payload, op-specific. Multi-byte fields may be
|
|
40
|
+
* unaligned within the payload; readers must use byte-
|
|
41
|
+
* oriented helpers (DataView in TS, `mem.readInt` in Zig).
|
|
42
|
+
*
|
|
43
|
+
* The ring lives BEFORE the layout-descriptor region in the SAB (right
|
|
44
|
+
* after the 32-byte header) so its offset is stable across descriptor /
|
|
45
|
+
* column-region growth. The host writes `header.command_ring_off` to
|
|
46
|
+
* point at it during `createColumnStore`; absent ring is signalled by
|
|
47
|
+
* `command_ring_off === 0`.
|
|
48
|
+
*/
|
|
49
|
+
/** Total bytes for the ring header. */
|
|
50
|
+
export declare const COMMAND_RING_HEADER_BYTES = 16;
|
|
51
|
+
/** Fixed slot size — 1-byte opCode + 15-byte payload. */
|
|
52
|
+
export declare const COMMAND_RING_SLOT_BYTES = 16;
|
|
53
|
+
/** Default ring capacity in slots. 256 × 16 B = 4 KiB of ring data plus
|
|
54
|
+
* 16 B header. Sized for the worst-case burst (peak spawn intents per
|
|
55
|
+
* tick) × small safety margin. Tune up if a Phase 4 burst pushes past it
|
|
56
|
+
* in the bench harness. */
|
|
57
|
+
export declare const COMMAND_RING_DEFAULT_CAPACITY_SLOTS = 256;
|
|
58
|
+
/** Byte offsets within the ring header. Mirrored on the Zig side in
|
|
59
|
+
* `packages/sim/src/command_ring.zig` — keep in sync. */
|
|
60
|
+
export declare const COMMAND_RING_HEADER_OFFSETS: {
|
|
61
|
+
readonly write_head: 0;
|
|
62
|
+
readonly read_head: 4;
|
|
63
|
+
readonly capacity_slots: 8;
|
|
64
|
+
readonly overflow_flag: 12;
|
|
65
|
+
};
|
|
66
|
+
/** Op-code `0` is reserved across the SAB layer as the empty-slot marker
|
|
67
|
+
* so a zero-initialised SAB doesn't appear to hold a valid command (mirror
|
|
68
|
+
* of `EVENT_OP_EMPTY`). All non-zero codes are opaque to the engine —
|
|
69
|
+
* the attached consumer owns the opcode enum + payload codecs (the game's
|
|
70
|
+
* `COMMAND_OP` + `SpawnUnitFields` live in `@internal/sim`). */
|
|
71
|
+
export declare const COMMAND_OP_EMPTY = 0;
|
|
72
|
+
/** Total bytes the ring occupies for `capacity_slots` slots. */
|
|
73
|
+
export declare function commandRingBytes(capacitySlots: number): number;
|
|
74
|
+
export declare class CommandRingError extends Error {
|
|
75
|
+
constructor(message: string);
|
|
76
|
+
}
|
|
77
|
+
/** Initialise the ring header at `ringOff` in the SAB. Zeroes
|
|
78
|
+
* `write_head`, `read_head`, and `overflow_flag`; sets `capacity_slots`.
|
|
79
|
+
* Slot bytes are left as-is (callers normally allocate the ring on a
|
|
80
|
+
* fresh, zero-initialised SAB). */
|
|
81
|
+
export declare function initCommandRing(view: DataView, ringOff: number, capacitySlots: number): void;
|
|
82
|
+
/** Read live ring-header field. */
|
|
83
|
+
export declare function ringWriteHead(view: DataView, ringOff: number): number;
|
|
84
|
+
export declare function ringReadHead(view: DataView, ringOff: number): number;
|
|
85
|
+
export declare function ringCapacitySlots(view: DataView, ringOff: number): number;
|
|
86
|
+
export declare function ringOverflow(view: DataView, ringOff: number): boolean;
|
|
87
|
+
/** Pending command count = `(write_head - read_head) mod 2^32`. The
|
|
88
|
+
* `>>> 0` keeps the result a u32 in the wrap-around case (rings live for
|
|
89
|
+
* the host's lifetime; 2^32 commands at 50 Hz ≈ 2 years, but the
|
|
90
|
+
* arithmetic should be correct regardless). */
|
|
91
|
+
export declare function pendingCommandCount(view: DataView, ringOff: number): number;
|
|
92
|
+
/** Push a command into the ring from the TS side. Production producer is
|
|
93
|
+
* WASM (via `command_ring.zig`); this is for host-side tests and for
|
|
94
|
+
* symmetric tests across the two sides. Returns `false` on overflow and
|
|
95
|
+
* sets the overflow flag. Payload must be exactly 15 bytes. */
|
|
96
|
+
export declare function pushCommand(view: DataView, ringOff: number, opCode: number, payload: Uint8Array): boolean;
|
|
97
|
+
/** Read one command from the ring. Returns opCode (0 = empty/no
|
|
98
|
+
* command) and fills `outPayload` (15 bytes) with the slot's payload.
|
|
99
|
+
* When 0 is returned, `outPayload` is untouched. */
|
|
100
|
+
export declare function popCommand(view: DataView, ringOff: number, outPayload: Uint8Array): number;
|
|
101
|
+
/** Visit every pending command and bump `read_head` past them. Yields
|
|
102
|
+
* `{ opCode, payload }` per slot where `payload` is a freshly-copied
|
|
103
|
+
* 15-byte Uint8Array (so the handler can hold it past the next pop
|
|
104
|
+
* without aliasing the ring). Stops when the ring is empty. Used by the
|
|
105
|
+
* TS host drain that runs right after `wasm.tick()` returns. */
|
|
106
|
+
export declare function drainCommandRing(view: DataView, ringOff: number, handler: (opCode: number, payload: Uint8Array) => void): number;
|
|
107
|
+
//# sourceMappingURL=command_ring.d.ts.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command ring — WASM-side producer / TS-side consumer SPSC ring buffer
|
|
3
|
+
* for structural-change intents emitted during `sim.tick()`. Plan §7.5
|
|
4
|
+
* (`docs/ideas/buffer-wasm-sim-plan-2026-05-14T1600.md`).
|
|
5
|
+
*
|
|
6
|
+
* Layout:
|
|
7
|
+
*
|
|
8
|
+
* [ write_head: u32 ] slot 0..N-1, monotonic (NOT slot-modulo)
|
|
9
|
+
* [ read_head: u32 ] slot 0..N-1, monotonic
|
|
10
|
+
* [ capacity: u32 ] slot count, power-of-two
|
|
11
|
+
* [ overflow: u32 ] 0 = OK; 1 = WASM exhausted the ring this tick
|
|
12
|
+
* [ slot 0: 16 B ] opCode: u8, payload: [15]u8
|
|
13
|
+
* [ slot 1: 16 B ] ...
|
|
14
|
+
* ...
|
|
15
|
+
*
|
|
16
|
+
* SPSC contract (Phase 4 scope):
|
|
17
|
+
* - Producer: WASM `sim.tick()`. Pushes 0..N commands during one tick;
|
|
18
|
+
* bumps `write_head` after each.
|
|
19
|
+
* - Consumer: TS host, immediately after `wasm.tick()` returns. Drains
|
|
20
|
+
* 0..N pending commands; bumps `read_head` after each.
|
|
21
|
+
* - The two never run concurrently in Phase 4 (single host thread
|
|
22
|
+
* orchestrates both). Phase 5's worker offload promotes the head
|
|
23
|
+
* bumps to `Atomics.store`; that's an additive change without
|
|
24
|
+
* altering the layout.
|
|
25
|
+
*
|
|
26
|
+
* Overflow:
|
|
27
|
+
* - If WASM would write a slot when (`write_head - read_head == capacity`),
|
|
28
|
+
* it sets `overflow = 1` and drops the command. TS treats overflow as
|
|
29
|
+
* a hard error in dev builds; production logs and continues (a command
|
|
30
|
+
* might be lost rather than crash the host).
|
|
31
|
+
*
|
|
32
|
+
* Slot format (plan §7.5):
|
|
33
|
+
* byte 0: opCode (u8). 0 is reserved as the empty-slot marker
|
|
34
|
+
* (`COMMAND_OP_EMPTY`); all other codes are consumer-defined.
|
|
35
|
+
* The engine never interprets a code — it drains
|
|
36
|
+
* `(opCode, payload)` and hands them to the attached
|
|
37
|
+
* consumer, which owns the opcode enum + payload codecs (the
|
|
38
|
+
* game's live in `@internal/sim`'s `command_payloads.ts`).
|
|
39
|
+
* bytes 1..15: payload, op-specific. Multi-byte fields may be
|
|
40
|
+
* unaligned within the payload; readers must use byte-
|
|
41
|
+
* oriented helpers (DataView in TS, `mem.readInt` in Zig).
|
|
42
|
+
*
|
|
43
|
+
* The ring lives BEFORE the layout-descriptor region in the SAB (right
|
|
44
|
+
* after the 32-byte header) so its offset is stable across descriptor /
|
|
45
|
+
* column-region growth. The host writes `header.command_ring_off` to
|
|
46
|
+
* point at it during `createColumnStore`; absent ring is signalled by
|
|
47
|
+
* `command_ring_off === 0`.
|
|
48
|
+
*/
|
|
49
|
+
/** Total bytes for the ring header. */
|
|
50
|
+
export declare const COMMAND_RING_HEADER_BYTES = 16;
|
|
51
|
+
/** Fixed slot size — 1-byte opCode + 15-byte payload. */
|
|
52
|
+
export declare const COMMAND_RING_SLOT_BYTES = 16;
|
|
53
|
+
/** Default ring capacity in slots. 256 × 16 B = 4 KiB of ring data plus
|
|
54
|
+
* 16 B header. Sized for the worst-case burst (peak spawn intents per
|
|
55
|
+
* tick) × small safety margin. Tune up if a Phase 4 burst pushes past it
|
|
56
|
+
* in the bench harness. */
|
|
57
|
+
export declare const COMMAND_RING_DEFAULT_CAPACITY_SLOTS = 256;
|
|
58
|
+
/** Byte offsets within the ring header. Mirrored on the Zig side in
|
|
59
|
+
* `packages/sim/src/command_ring.zig` — keep in sync. */
|
|
60
|
+
export declare const COMMAND_RING_HEADER_OFFSETS: {
|
|
61
|
+
readonly write_head: 0;
|
|
62
|
+
readonly read_head: 4;
|
|
63
|
+
readonly capacity_slots: 8;
|
|
64
|
+
readonly overflow_flag: 12;
|
|
65
|
+
};
|
|
66
|
+
/** Op-code `0` is reserved across the SAB layer as the empty-slot marker
|
|
67
|
+
* so a zero-initialised SAB doesn't appear to hold a valid command (mirror
|
|
68
|
+
* of `EVENT_OP_EMPTY`). All non-zero codes are opaque to the engine —
|
|
69
|
+
* the attached consumer owns the opcode enum + payload codecs (the game's
|
|
70
|
+
* `COMMAND_OP` + `SpawnUnitFields` live in `@internal/sim`). */
|
|
71
|
+
export declare const COMMAND_OP_EMPTY = 0;
|
|
72
|
+
/** Total bytes the ring occupies for `capacity_slots` slots. */
|
|
73
|
+
export declare function commandRingBytes(capacitySlots: number): number;
|
|
74
|
+
export declare class CommandRingError extends Error {
|
|
75
|
+
constructor(message: string);
|
|
76
|
+
}
|
|
77
|
+
/** Initialise the ring header at `ringOff` in the SAB. Zeroes
|
|
78
|
+
* `write_head`, `read_head`, and `overflow_flag`; sets `capacity_slots`.
|
|
79
|
+
* Slot bytes are left as-is (callers normally allocate the ring on a
|
|
80
|
+
* fresh, zero-initialised SAB). */
|
|
81
|
+
export declare function initCommandRing(view: DataView, ringOff: number, capacitySlots: number): void;
|
|
82
|
+
/** Read live ring-header field. */
|
|
83
|
+
export declare function ringWriteHead(view: DataView, ringOff: number): number;
|
|
84
|
+
export declare function ringReadHead(view: DataView, ringOff: number): number;
|
|
85
|
+
export declare function ringCapacitySlots(view: DataView, ringOff: number): number;
|
|
86
|
+
export declare function ringOverflow(view: DataView, ringOff: number): boolean;
|
|
87
|
+
/** Pending command count = `(write_head - read_head) mod 2^32`. The
|
|
88
|
+
* `>>> 0` keeps the result a u32 in the wrap-around case (rings live for
|
|
89
|
+
* the host's lifetime; 2^32 commands at 50 Hz ≈ 2 years, but the
|
|
90
|
+
* arithmetic should be correct regardless). */
|
|
91
|
+
export declare function pendingCommandCount(view: DataView, ringOff: number): number;
|
|
92
|
+
/** Push a command into the ring from the TS side. Production producer is
|
|
93
|
+
* WASM (via `command_ring.zig`); this is for host-side tests and for
|
|
94
|
+
* symmetric tests across the two sides. Returns `false` on overflow and
|
|
95
|
+
* sets the overflow flag. Payload must be exactly 15 bytes. */
|
|
96
|
+
export declare function pushCommand(view: DataView, ringOff: number, opCode: number, payload: Uint8Array): boolean;
|
|
97
|
+
/** Read one command from the ring. Returns opCode (0 = empty/no
|
|
98
|
+
* command) and fills `outPayload` (15 bytes) with the slot's payload.
|
|
99
|
+
* When 0 is returned, `outPayload` is untouched. */
|
|
100
|
+
export declare function popCommand(view: DataView, ringOff: number, outPayload: Uint8Array): number;
|
|
101
|
+
/** Visit every pending command and bump `read_head` past them. Yields
|
|
102
|
+
* `{ opCode, payload }` per slot where `payload` is a freshly-copied
|
|
103
|
+
* 15-byte Uint8Array (so the handler can hold it past the next pop
|
|
104
|
+
* without aliasing the ring). Stops when the ring is empty. Used by the
|
|
105
|
+
* TS host drain that runs right after `wasm.tick()` returns. */
|
|
106
|
+
export declare function drainCommandRing(view: DataView, ringOff: number, handler: (opCode: number, payload: Uint8Array) => void): number;
|
|
107
|
+
//# sourceMappingURL=command_ring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command_ring.d.ts","sourceRoot":"","sources":["../../../src/core/store/command_ring.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,uCAAuC;AACvC,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAE5C,yDAAyD;AACzD,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAE1C;;;2BAG2B;AAC3B,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD;yDACyD;AACzD,eAAO,MAAM,2BAA2B;;;;;CAK9B,CAAC;AAEX;;;;gEAIgE;AAChE,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE9D;AASD,qBAAa,gBAAiB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI3B;AAED;;;mCAGmC;AACnC,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,CAU5F;AAED,mCAAmC;AACnC,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAErE;AACD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpE;AACD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEzE;AACD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAErE;AAED;;;+CAG+C;AAC/C,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;;+DAG+D;AAC/D,wBAAgB,WAAW,CAC1B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,UAAU,GACjB,OAAO,CAmCT;AAED;;oDAEoD;AACpD,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CAiB1F;AAED;;;;gEAIgE;AAChE,wBAAgB,gBAAgB,CAC/B,IAAI,EAAE,QAAQ,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,KAAK,IAAI,GACpD,MAAM,CAWR"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { COLUMN_DESCRIPTOR_BYTES, COLUMN_DESCRIPTOR_OFFSETS, ARCHETYPE_DESCRIPTOR_HEADER_BYTES, ARCHETYPE_DESCRIPTOR_OFFSETS, COMPONENT_MASK_WORDS } from './vendored_abi/abi.cjs';
|
|
2
|
+
export { COLUMN_DESCRIPTOR_BYTES, COLUMN_DESCRIPTOR_OFFSETS, ARCHETYPE_DESCRIPTOR_HEADER_BYTES, ARCHETYPE_DESCRIPTOR_OFFSETS, COMPONENT_MASK_WORDS };
|
|
3
|
+
/** type_tag values for `ColumnDescriptor.type_tag`. The order matches the
|
|
4
|
+
* TypedArrayTag union in `packages/std/type_primitives` so a tag's element
|
|
5
|
+
* width is `TYPE_TAG_STRIDE[tag]`. */
|
|
6
|
+
export declare const TYPE_TAG: {
|
|
7
|
+
readonly u8: 0;
|
|
8
|
+
readonly i8: 1;
|
|
9
|
+
readonly u16: 2;
|
|
10
|
+
readonly i16: 3;
|
|
11
|
+
readonly u32: 4;
|
|
12
|
+
readonly i32: 5;
|
|
13
|
+
readonly f32: 6;
|
|
14
|
+
readonly f64: 7;
|
|
15
|
+
};
|
|
16
|
+
export type TypeTagValue = (typeof TYPE_TAG)[keyof typeof TYPE_TAG];
|
|
17
|
+
/** Element width in bytes for each `TYPE_TAG`. Indexed by tag value so a
|
|
18
|
+
* `stride = TYPE_TAG_STRIDE[tag]` lookup is O(1) array access. */
|
|
19
|
+
export declare const TYPE_TAG_STRIDE: Readonly<Record<TypeTagValue, number>>;
|
|
20
|
+
/** String-tag → numeric-tag bridge. `TypedArrayTag` ("u8", "f32", …) is the
|
|
21
|
+
* vocabulary the TS-side component registry speaks; `TypeTagValue` is the
|
|
22
|
+
* numeric enum the SAB descriptors carry on the wire. Bridging happens at
|
|
23
|
+
* the ECS↔SAB seam — Archetype layouts come in as strings, ColumnSpecs go
|
|
24
|
+
* out as integers. */
|
|
25
|
+
export declare const TYPED_ARRAY_TAG_TO_TYPE_TAG: {
|
|
26
|
+
readonly u8: 0;
|
|
27
|
+
readonly i8: 1;
|
|
28
|
+
readonly u16: 2;
|
|
29
|
+
readonly i16: 3;
|
|
30
|
+
readonly u32: 4;
|
|
31
|
+
readonly i32: 5;
|
|
32
|
+
readonly f32: 6;
|
|
33
|
+
readonly f64: 7;
|
|
34
|
+
};
|
|
35
|
+
export interface ColumnDescriptor {
|
|
36
|
+
readonly componentId: number;
|
|
37
|
+
readonly fieldId: number;
|
|
38
|
+
readonly typeTag: TypeTagValue;
|
|
39
|
+
/** Byte offset of the column's first row, measured from SAB byte 0. */
|
|
40
|
+
readonly byteOff: number;
|
|
41
|
+
/** Element width in bytes; should always equal `TYPE_TAG_STRIDE[type_tag]`. */
|
|
42
|
+
readonly stride: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function writeColumnDescriptor(view: DataView, off: number, c: ColumnDescriptor): void;
|
|
45
|
+
export declare function readColumnDescriptor(view: DataView, off: number): ColumnDescriptor;
|
|
46
|
+
/** Number of distinct components the cross-language ECS supports:
|
|
47
|
+
* `COMPONENT_MASK_WORDS × 32` bits in the SAB archetype descriptor mask. The
|
|
48
|
+
* Zig side matches archetypes purely on this mask, so a component whose ID is
|
|
49
|
+
* ≥ this limit would be invisible there while the heap-side `BitSet` stayed
|
|
50
|
+
* correct — silently conflating archetypes that differ only in such a
|
|
51
|
+
* component. `Store.registerComponent` enforces this as a hard registration
|
|
52
|
+
* ceiling so the overflow fails loudly instead (#381). The mask is sized to
|
|
53
|
+
* the `BitSet`'s `INITIAL_WORD_COUNT`, so a registry within the limit never
|
|
54
|
+
* grows a component mask past its initial words. */
|
|
55
|
+
export declare const STORE_DESCRIPTOR_COMPONENT_LIMIT: number;
|
|
56
|
+
export interface ArchetypeDescriptor {
|
|
57
|
+
readonly archetypeId: number;
|
|
58
|
+
/** Component bitmask, `COMPONENT_MASK_WORDS` little-endian u32 words. Word
|
|
59
|
+
* `w` holds component IDs in `[w*32, w*32+32)`. */
|
|
60
|
+
readonly componentMask: readonly number[];
|
|
61
|
+
readonly rowCount: number;
|
|
62
|
+
readonly rowCapacity: number;
|
|
63
|
+
/** Enabled-row count `≤ row_count` (#577 / #599). Per-row entity-scan loops in
|
|
64
|
+
* the WASM sim bound on this so disabled entities (swapped to the tail
|
|
65
|
+
* `[enabled_count, row_count)`) are not simulated; row-indexed cross-entity
|
|
66
|
+
* reads still use `row_count`. */
|
|
67
|
+
readonly enabledCount: number;
|
|
68
|
+
readonly columns: readonly ColumnDescriptor[];
|
|
69
|
+
}
|
|
70
|
+
/** Total bytes a descriptor will occupy, given its column count. Useful for
|
|
71
|
+
* planning the layout descriptor region size up front. */
|
|
72
|
+
export declare function archetypeDescriptorBytes(columnCount: number): number;
|
|
73
|
+
export declare function writeArchetypeDescriptor(view: DataView, off: number, d: ArchetypeDescriptor): number;
|
|
74
|
+
export declare function readArchetypeDescriptor(view: DataView, off: number): ArchetypeDescriptor;
|
|
75
|
+
export declare function writeLayoutDescriptorRegion(view: DataView, regionOff: number, descriptors: readonly ArchetypeDescriptor[]): number;
|
|
76
|
+
export declare function readLayoutDescriptorRegion(view: DataView, regionOff: number, archetypeCount: number): readonly ArchetypeDescriptor[];
|
|
77
|
+
/** Total bytes needed for a layout descriptor region holding these archetypes.
|
|
78
|
+
* Use to size the SAB region between header end and the first column. */
|
|
79
|
+
export declare function layoutDescriptorRegionBytes(descriptors: readonly ArchetypeDescriptor[]): number;
|
|
80
|
+
//# sourceMappingURL=descriptor.d.ts.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { COLUMN_DESCRIPTOR_BYTES, COLUMN_DESCRIPTOR_OFFSETS, ARCHETYPE_DESCRIPTOR_HEADER_BYTES, ARCHETYPE_DESCRIPTOR_OFFSETS, COMPONENT_MASK_WORDS } from './vendored_abi/abi.js';
|
|
2
|
+
export { COLUMN_DESCRIPTOR_BYTES, COLUMN_DESCRIPTOR_OFFSETS, ARCHETYPE_DESCRIPTOR_HEADER_BYTES, ARCHETYPE_DESCRIPTOR_OFFSETS, COMPONENT_MASK_WORDS };
|
|
3
|
+
/** type_tag values for `ColumnDescriptor.type_tag`. The order matches the
|
|
4
|
+
* TypedArrayTag union in `packages/std/type_primitives` so a tag's element
|
|
5
|
+
* width is `TYPE_TAG_STRIDE[tag]`. */
|
|
6
|
+
export declare const TYPE_TAG: {
|
|
7
|
+
readonly u8: 0;
|
|
8
|
+
readonly i8: 1;
|
|
9
|
+
readonly u16: 2;
|
|
10
|
+
readonly i16: 3;
|
|
11
|
+
readonly u32: 4;
|
|
12
|
+
readonly i32: 5;
|
|
13
|
+
readonly f32: 6;
|
|
14
|
+
readonly f64: 7;
|
|
15
|
+
};
|
|
16
|
+
export type TypeTagValue = (typeof TYPE_TAG)[keyof typeof TYPE_TAG];
|
|
17
|
+
/** Element width in bytes for each `TYPE_TAG`. Indexed by tag value so a
|
|
18
|
+
* `stride = TYPE_TAG_STRIDE[tag]` lookup is O(1) array access. */
|
|
19
|
+
export declare const TYPE_TAG_STRIDE: Readonly<Record<TypeTagValue, number>>;
|
|
20
|
+
/** String-tag → numeric-tag bridge. `TypedArrayTag` ("u8", "f32", …) is the
|
|
21
|
+
* vocabulary the TS-side component registry speaks; `TypeTagValue` is the
|
|
22
|
+
* numeric enum the SAB descriptors carry on the wire. Bridging happens at
|
|
23
|
+
* the ECS↔SAB seam — Archetype layouts come in as strings, ColumnSpecs go
|
|
24
|
+
* out as integers. */
|
|
25
|
+
export declare const TYPED_ARRAY_TAG_TO_TYPE_TAG: {
|
|
26
|
+
readonly u8: 0;
|
|
27
|
+
readonly i8: 1;
|
|
28
|
+
readonly u16: 2;
|
|
29
|
+
readonly i16: 3;
|
|
30
|
+
readonly u32: 4;
|
|
31
|
+
readonly i32: 5;
|
|
32
|
+
readonly f32: 6;
|
|
33
|
+
readonly f64: 7;
|
|
34
|
+
};
|
|
35
|
+
export interface ColumnDescriptor {
|
|
36
|
+
readonly componentId: number;
|
|
37
|
+
readonly fieldId: number;
|
|
38
|
+
readonly typeTag: TypeTagValue;
|
|
39
|
+
/** Byte offset of the column's first row, measured from SAB byte 0. */
|
|
40
|
+
readonly byteOff: number;
|
|
41
|
+
/** Element width in bytes; should always equal `TYPE_TAG_STRIDE[type_tag]`. */
|
|
42
|
+
readonly stride: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function writeColumnDescriptor(view: DataView, off: number, c: ColumnDescriptor): void;
|
|
45
|
+
export declare function readColumnDescriptor(view: DataView, off: number): ColumnDescriptor;
|
|
46
|
+
/** Number of distinct components the cross-language ECS supports:
|
|
47
|
+
* `COMPONENT_MASK_WORDS × 32` bits in the SAB archetype descriptor mask. The
|
|
48
|
+
* Zig side matches archetypes purely on this mask, so a component whose ID is
|
|
49
|
+
* ≥ this limit would be invisible there while the heap-side `BitSet` stayed
|
|
50
|
+
* correct — silently conflating archetypes that differ only in such a
|
|
51
|
+
* component. `Store.registerComponent` enforces this as a hard registration
|
|
52
|
+
* ceiling so the overflow fails loudly instead (#381). The mask is sized to
|
|
53
|
+
* the `BitSet`'s `INITIAL_WORD_COUNT`, so a registry within the limit never
|
|
54
|
+
* grows a component mask past its initial words. */
|
|
55
|
+
export declare const STORE_DESCRIPTOR_COMPONENT_LIMIT: number;
|
|
56
|
+
export interface ArchetypeDescriptor {
|
|
57
|
+
readonly archetypeId: number;
|
|
58
|
+
/** Component bitmask, `COMPONENT_MASK_WORDS` little-endian u32 words. Word
|
|
59
|
+
* `w` holds component IDs in `[w*32, w*32+32)`. */
|
|
60
|
+
readonly componentMask: readonly number[];
|
|
61
|
+
readonly rowCount: number;
|
|
62
|
+
readonly rowCapacity: number;
|
|
63
|
+
/** Enabled-row count `≤ row_count` (#577 / #599). Per-row entity-scan loops in
|
|
64
|
+
* the WASM sim bound on this so disabled entities (swapped to the tail
|
|
65
|
+
* `[enabled_count, row_count)`) are not simulated; row-indexed cross-entity
|
|
66
|
+
* reads still use `row_count`. */
|
|
67
|
+
readonly enabledCount: number;
|
|
68
|
+
readonly columns: readonly ColumnDescriptor[];
|
|
69
|
+
}
|
|
70
|
+
/** Total bytes a descriptor will occupy, given its column count. Useful for
|
|
71
|
+
* planning the layout descriptor region size up front. */
|
|
72
|
+
export declare function archetypeDescriptorBytes(columnCount: number): number;
|
|
73
|
+
export declare function writeArchetypeDescriptor(view: DataView, off: number, d: ArchetypeDescriptor): number;
|
|
74
|
+
export declare function readArchetypeDescriptor(view: DataView, off: number): ArchetypeDescriptor;
|
|
75
|
+
export declare function writeLayoutDescriptorRegion(view: DataView, regionOff: number, descriptors: readonly ArchetypeDescriptor[]): number;
|
|
76
|
+
export declare function readLayoutDescriptorRegion(view: DataView, regionOff: number, archetypeCount: number): readonly ArchetypeDescriptor[];
|
|
77
|
+
/** Total bytes needed for a layout descriptor region holding these archetypes.
|
|
78
|
+
* Use to size the SAB region between header end and the first column. */
|
|
79
|
+
export declare function layoutDescriptorRegionBytes(descriptors: readonly ArchetypeDescriptor[]): number;
|
|
80
|
+
//# sourceMappingURL=descriptor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descriptor.d.ts","sourceRoot":"","sources":["../../../src/core/store/descriptor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAeH,OAAO,EACN,uBAAuB,EACvB,yBAAyB,EACzB,iCAAiC,EACjC,4BAA4B,EAC5B,oBAAoB,EACpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACN,uBAAuB,EACvB,yBAAyB,EACzB,iCAAiC,EACjC,4BAA4B,EAC5B,oBAAoB,EACpB,CAAC;AAEF;;sCAEsC;AACtC,eAAO,MAAM,QAAQ;;;;;;;;;CASX,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,OAAO,QAAQ,CAAC,CAAC;AAEpE;kEACkE;AAClE,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CASjE,CAAC;AAEH;;;;sBAIsB;AACtB,eAAO,MAAM,2BAA2B;;;;;;;;;CAS9B,CAAC;AAUX,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAS5F;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAQlF;AAeD;;;;;;;;oDAQoD;AACpD,eAAO,MAAM,gCAAgC,QAA4B,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;uDACmD;IACnD,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;sCAGkC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;CAC9C;AAED;0DAC0D;AAC1D,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,wBAAwB,CACvC,IAAI,EAAE,QAAQ,EACd,GAAG,EAAE,MAAM,EACX,CAAC,EAAE,mBAAmB,GACpB,MAAM,CAoBR;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,mBAAmB,CAuBxF;AAcD,wBAAgB,2BAA2B,CAC1C,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,SAAS,mBAAmB,EAAE,GACzC,MAAM,CAMR;AAED,wBAAgB,0BAA0B,CACzC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GACpB,SAAS,mBAAmB,EAAE,CAShC;AAED;yEACyE;AACzE,wBAAgB,2BAA2B,CAC1C,WAAW,EAAE,SAAS,mBAAmB,EAAE,GACzC,MAAM,CAMR"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity-index SAB region — `EntityID` → `(archetype_id, row, generation)`
|
|
3
|
+
* lookup table shared with the Zig sim. (#245 / Phase 4 PR 4B)
|
|
4
|
+
*
|
|
5
|
+
* Every hot fixed-update system that needs to resolve an `EntityID`
|
|
6
|
+
* held in a column (e.g. a component field that points at another
|
|
7
|
+
* entity → that entity's columns) has to do
|
|
8
|
+
* `entityId → archetype + row` translation. The plan forbids
|
|
9
|
+
* WASM-into-TS callbacks during `tick()` (§4 / §5.2), so the lookup
|
|
10
|
+
* tables must live in shared memory.
|
|
11
|
+
*
|
|
12
|
+
* Layout:
|
|
13
|
+
*
|
|
14
|
+
* [ length: u32 ] high-water index (count of slots ever issued)
|
|
15
|
+
* [ capacity: u32 ] backing-array length (slots × 1; not bytes)
|
|
16
|
+
* [ _pad0: u32 ] alignment pad to 16 bytes
|
|
17
|
+
* [ _pad1: u32 ] ───
|
|
18
|
+
* [ generations[capacity]: i32 ]
|
|
19
|
+
* [ archetypes[capacity]: i32 ]
|
|
20
|
+
* [ rows[capacity]: i32 ]
|
|
21
|
+
*
|
|
22
|
+
* Sentinels:
|
|
23
|
+
* - `generations[i] = 0` (INITIAL_GENERATION) for never-used slots.
|
|
24
|
+
* Generation grows by 1 on every destroy; an `EntityID`'s generation
|
|
25
|
+
* field matches `generations[index]` iff it's still alive.
|
|
26
|
+
* - `archetypes[i] = -1` (UNASSIGNED) when the slot hasn't been placed
|
|
27
|
+
* into an archetype, OR when the entity is destroyed.
|
|
28
|
+
* - `rows[i] = -1` (UNASSIGNED) on destroy / not-placed.
|
|
29
|
+
*
|
|
30
|
+
* Field width: i32 (signed) on the TS side so `-1` round-trips through
|
|
31
|
+
* `Int32Array` without unsigned coercion. Zig reads as i32 too — bit
|
|
32
|
+
* pattern is identical to u32 `0xFFFFFFFF` for the UNASSIGNED case, and
|
|
33
|
+
* for valid archetype_ids (bounded by `MAX_INDEX = 2^20`) the sign bit
|
|
34
|
+
* is never set, so signed/unsigned interpretation agrees.
|
|
35
|
+
*
|
|
36
|
+
* Region placement: between command ring and descriptor region so the
|
|
37
|
+
* offset is stable across descriptor / column-region growth (same
|
|
38
|
+
* property the command ring gets). Grow path (when entityHighWater
|
|
39
|
+
* exceeds `capacity`) uses `growColumnStore` — slow path, same as
|
|
40
|
+
* descriptor-region overflow.
|
|
41
|
+
*
|
|
42
|
+
* Reading from Zig: see `packages/sim/src/entity_index.zig` for the
|
|
43
|
+
* symmetric reader.
|
|
44
|
+
*/
|
|
45
|
+
/** Fixed bytes of the region header (length, capacity, two pad u32s for
|
|
46
|
+
* 16-byte alignment of the i32 arrays that follow). */
|
|
47
|
+
export declare const ENTITY_INDEX_HEADER_BYTES = 16;
|
|
48
|
+
/** Bytes per slot: three i32 columns. */
|
|
49
|
+
export declare const ENTITY_INDEX_BYTES_PER_SLOT = 12;
|
|
50
|
+
/** Byte offsets within the region header. Locked by the
|
|
51
|
+
* `entity_index.test.ts` golden bytes; any change here is a
|
|
52
|
+
* `SIM_ABI_VERSION` bump. */
|
|
53
|
+
export declare const ENTITY_INDEX_HEADER_OFFSETS: {
|
|
54
|
+
readonly length: 0;
|
|
55
|
+
readonly capacity: 4;
|
|
56
|
+
};
|
|
57
|
+
/** Default initial slot count when the engine creates a Store. Matches
|
|
58
|
+
* `MAX_INDEX = (1 << 20) - 1 + 1 = 1_048_576` (the EntityID 20-bit index
|
|
59
|
+
* range, see `entity.ts`), so the region pre-sizes to the entire
|
|
60
|
+
* addressable entity space and `createEntity` can never run out under
|
|
61
|
+
* the default. 1M × 12 B ≈ 12 MiB SAB region — virtual memory only;
|
|
62
|
+
* physical pages allocate lazily via OS page-fault on first touch, so
|
|
63
|
+
* the typical 1000-entity workload pays for ~12 KiB physical even though the
|
|
64
|
+
* virtual reservation is 12 MiB.
|
|
65
|
+
*
|
|
66
|
+
* Tests / benches can pass a smaller `StoreOptions.entityIndexCapacity`
|
|
67
|
+
* to bench tighter reservations. A future PR will replace this with
|
|
68
|
+
* on-demand growth via `growColumnStore` so the default can drop. */
|
|
69
|
+
export declare const ENTITY_INDEX_DEFAULT_CAPACITY: number;
|
|
70
|
+
/** Total region bytes for `capacity` slots: header + 3 i32 columns. */
|
|
71
|
+
export declare function entityIndexRegionBytes(capacity: number): number;
|
|
72
|
+
/** Byte offset of the generations column within the region. */
|
|
73
|
+
export declare function entityIndexGenerationsOff(regionOff: number): number;
|
|
74
|
+
/** Byte offset of the archetype-id column. */
|
|
75
|
+
export declare function entityIndexArchetypesOff(regionOff: number, capacity: number): number;
|
|
76
|
+
/** Byte offset of the row column. */
|
|
77
|
+
export declare function entityIndexRowsOff(regionOff: number, capacity: number): number;
|
|
78
|
+
export declare class EntityIndexError extends Error {
|
|
79
|
+
constructor(message: string);
|
|
80
|
+
}
|
|
81
|
+
/** Initialise the region header at `regionOff`. Sets `length=0` and
|
|
82
|
+
* `capacity=<arg>`. The i32 arrays past the header are left untouched
|
|
83
|
+
* (callers normally allocate the region on a fresh, zero-initialised
|
|
84
|
+
* SAB; generation 0 is the INITIAL_GENERATION sentinel, archetype/row
|
|
85
|
+
* sentinels of 0 are caught by `length=0` so no read reaches them). */
|
|
86
|
+
export declare function initEntityIndexRegion(view: DataView, regionOff: number, capacity: number): void;
|
|
87
|
+
/** Read the region's current length (high-water index, ≤ capacity). */
|
|
88
|
+
export declare function entityIndexLength(view: DataView, regionOff: number): number;
|
|
89
|
+
/** Read the region's current capacity (backing-array length). */
|
|
90
|
+
export declare function entityIndexCapacity(view: DataView, regionOff: number): number;
|
|
91
|
+
/** Update the region's length (called from the engine's Store on every
|
|
92
|
+
* entity allocation that pushes `entityHighWater` past its previous
|
|
93
|
+
* value). */
|
|
94
|
+
export declare function setEntityIndexLength(view: DataView, regionOff: number, length: number): void;
|
|
95
|
+
/** Materialise the three Int32Array views over the region's column data
|
|
96
|
+
* for use by the engine's Store. The views live as long as the
|
|
97
|
+
* SharedArrayBuffer hasn't been reallocated — refresh on `view_stamp`
|
|
98
|
+
* bump (the engine's `_onBufferResized` callback).
|
|
99
|
+
*
|
|
100
|
+
* Three separate views (not a single struct-of-arrays object) because
|
|
101
|
+
* the engine's hot paths benefit from each being a direct typed-array
|
|
102
|
+
* read; V8 specialises `arr[i] = v` aggressively for typed arrays. */
|
|
103
|
+
export declare function buildEntityIndexViews(buffer: ArrayBufferLike, regionOff: number, capacity: number): {
|
|
104
|
+
readonly generations: Int32Array;
|
|
105
|
+
readonly archetypes: Int32Array;
|
|
106
|
+
readonly rows: Int32Array;
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=entity_index.d.ts.map
|