@oasys/oecs 0.5.3 → 0.6.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 +1399 -276
- package/README.md +327 -169
- package/dist/core/ecs/access_check.d.cts +31 -31
- package/dist/core/ecs/access_check.d.ts +31 -31
- package/dist/core/ecs/access_check.d.ts.map +1 -1
- package/dist/core/ecs/archetype.d.cts +273 -205
- package/dist/core/ecs/archetype.d.ts +273 -205
- package/dist/core/ecs/archetype.d.ts.map +1 -1
- package/dist/core/ecs/archetype_closure.d.cts +23 -0
- package/dist/core/ecs/archetype_closure.d.ts +23 -0
- package/dist/core/ecs/archetype_closure.d.ts.map +1 -0
- package/dist/core/ecs/archetype_graph.d.cts +34 -34
- package/dist/core/ecs/archetype_graph.d.ts +34 -34
- package/dist/core/ecs/archetype_graph.d.ts.map +1 -1
- package/dist/core/ecs/archetype_types.d.cts +55 -0
- package/dist/core/ecs/archetype_types.d.ts +55 -0
- package/dist/core/ecs/archetype_types.d.ts.map +1 -0
- package/dist/core/ecs/change_feed.d.cts +53 -0
- package/dist/core/ecs/change_feed.d.ts +53 -0
- package/dist/core/ecs/change_feed.d.ts.map +1 -0
- package/dist/core/ecs/changed_query.d.cts +36 -0
- package/dist/core/ecs/changed_query.d.ts +36 -0
- package/dist/core/ecs/changed_query.d.ts.map +1 -0
- package/dist/core/ecs/chunk_columns.d.cts +49 -0
- package/dist/core/ecs/chunk_columns.d.ts +49 -0
- package/dist/core/ecs/chunk_columns.d.ts.map +1 -0
- package/dist/core/ecs/command_log.d.cts +30 -30
- package/dist/core/ecs/command_log.d.ts +30 -30
- package/dist/core/ecs/command_log.d.ts.map +1 -1
- package/dist/core/ecs/component.d.cts +45 -44
- package/dist/core/ecs/component.d.ts +45 -44
- package/dist/core/ecs/component.d.ts.map +1 -1
- package/dist/core/ecs/component_registry.d.cts +53 -0
- package/dist/core/ecs/component_registry.d.ts +53 -0
- package/dist/core/ecs/component_registry.d.ts.map +1 -0
- package/dist/core/ecs/compute_backend.d.cts +18 -7
- package/dist/core/ecs/compute_backend.d.ts +18 -7
- package/dist/core/ecs/compute_backend.d.ts.map +1 -1
- package/dist/core/ecs/deferred_commands.d.cts +15 -14
- package/dist/core/ecs/deferred_commands.d.ts +15 -14
- package/dist/core/ecs/deferred_commands.d.ts.map +1 -1
- package/dist/core/ecs/dispatch_trace.d.cts +32 -31
- package/dist/core/ecs/dispatch_trace.d.ts +32 -31
- package/dist/core/ecs/dispatch_trace.d.ts.map +1 -1
- package/dist/core/ecs/ecs.d.cts +337 -276
- package/dist/core/ecs/ecs.d.ts +337 -276
- package/dist/core/ecs/ecs.d.ts.map +1 -1
- package/dist/core/ecs/ecs_memory.d.cts +134 -136
- package/dist/core/ecs/ecs_memory.d.ts +134 -136
- package/dist/core/ecs/ecs_memory.d.ts.map +1 -1
- package/dist/core/ecs/ecs_options.d.cts +89 -0
- package/dist/core/ecs/ecs_options.d.ts +89 -0
- package/dist/core/ecs/ecs_options.d.ts.map +1 -0
- package/dist/core/ecs/entity.d.cts +4 -4
- package/dist/core/ecs/entity.d.ts +4 -4
- package/dist/core/ecs/entity_allocator.d.cts +24 -22
- package/dist/core/ecs/entity_allocator.d.ts +24 -22
- package/dist/core/ecs/entity_allocator.d.ts.map +1 -1
- package/dist/core/ecs/event.d.cts +37 -23
- package/dist/core/ecs/event.d.ts +37 -23
- package/dist/core/ecs/event.d.ts.map +1 -1
- package/dist/core/ecs/facades.d.cts +12 -126
- package/dist/core/ecs/facades.d.ts +12 -126
- package/dist/core/ecs/facades.d.ts.map +1 -1
- package/dist/core/ecs/frame_stepper.d.cts +12 -12
- package/dist/core/ecs/frame_stepper.d.ts +12 -12
- package/dist/core/ecs/frame_stepper.d.ts.map +1 -1
- package/dist/core/ecs/frame_trace.d.cts +34 -34
- package/dist/core/ecs/frame_trace.d.ts +34 -34
- package/dist/core/ecs/frame_trace.d.ts.map +1 -1
- package/dist/core/ecs/host_commands.d.cts +107 -89
- package/dist/core/ecs/host_commands.d.ts +107 -89
- package/dist/core/ecs/host_commands.d.ts.map +1 -1
- package/dist/core/ecs/index.d.cts +22 -12
- package/dist/core/ecs/index.d.ts +22 -12
- package/dist/core/ecs/index.d.ts.map +1 -1
- package/dist/core/ecs/observer.d.cts +41 -122
- package/dist/core/ecs/observer.d.ts +41 -122
- package/dist/core/ecs/observer.d.ts.map +1 -1
- package/dist/core/ecs/phase.d.cts +79 -0
- package/dist/core/ecs/phase.d.ts +79 -0
- package/dist/core/ecs/phase.d.ts.map +1 -0
- package/dist/core/ecs/plugin.d.cts +125 -0
- package/dist/core/ecs/plugin.d.ts +125 -0
- package/dist/core/ecs/plugin.d.ts.map +1 -0
- package/dist/core/ecs/plugin_slots.d.cts +26 -0
- package/dist/core/ecs/plugin_slots.d.ts +26 -0
- package/dist/core/ecs/plugin_slots.d.ts.map +1 -0
- package/dist/core/ecs/query.d.cts +219 -568
- package/dist/core/ecs/query.d.ts +219 -568
- package/dist/core/ecs/query.d.ts.map +1 -1
- package/dist/core/ecs/query_cache.d.cts +130 -0
- package/dist/core/ecs/query_cache.d.ts +130 -0
- package/dist/core/ecs/query_cache.d.ts.map +1 -0
- package/dist/core/ecs/query_registry.d.cts +47 -0
- package/dist/core/ecs/query_registry.d.ts +47 -0
- package/dist/core/ecs/query_registry.d.ts.map +1 -0
- package/dist/core/ecs/query_terms.d.cts +126 -0
- package/dist/core/ecs/query_terms.d.ts +126 -0
- package/dist/core/ecs/query_terms.d.ts.map +1 -0
- package/dist/core/ecs/ref.d.cts +129 -16
- package/dist/core/ecs/ref.d.ts +129 -16
- package/dist/core/ecs/ref.d.ts.map +1 -1
- package/dist/core/ecs/relation.d.cts +105 -186
- package/dist/core/ecs/relation.d.ts +105 -186
- package/dist/core/ecs/relation.d.ts.map +1 -1
- package/dist/core/ecs/resource.d.cts +14 -14
- package/dist/core/ecs/resource.d.ts +14 -14
- package/dist/core/ecs/resource_registry.d.cts +6 -6
- package/dist/core/ecs/resource_registry.d.ts +6 -6
- package/dist/core/ecs/resource_registry.d.ts.map +1 -1
- package/dist/core/ecs/row_kinds.d.cts +24 -0
- package/dist/core/ecs/row_kinds.d.ts +24 -0
- package/dist/core/ecs/row_kinds.d.ts.map +1 -0
- package/dist/core/ecs/run_condition.d.cts +23 -23
- package/dist/core/ecs/run_condition.d.ts +23 -23
- package/dist/core/ecs/run_condition.d.ts.map +1 -1
- package/dist/core/ecs/schedule.d.cts +98 -114
- package/dist/core/ecs/schedule.d.ts +98 -114
- package/dist/core/ecs/schedule.d.ts.map +1 -1
- package/dist/core/ecs/schedule_plan.d.cts +33 -0
- package/dist/core/ecs/schedule_plan.d.ts +33 -0
- package/dist/core/ecs/schedule_plan.d.ts.map +1 -0
- package/dist/core/ecs/snapshot.d.cts +77 -0
- package/dist/core/ecs/snapshot.d.ts +77 -0
- package/dist/core/ecs/snapshot.d.ts.map +1 -0
- package/dist/core/ecs/snapshot_mount.d.cts +42 -0
- package/dist/core/ecs/snapshot_mount.d.ts +42 -0
- package/dist/core/ecs/snapshot_mount.d.ts.map +1 -0
- package/dist/core/ecs/sparse_store.d.cts +110 -53
- package/dist/core/ecs/sparse_store.d.ts +110 -53
- package/dist/core/ecs/sparse_store.d.ts.map +1 -1
- package/dist/core/ecs/store.d.cts +644 -511
- package/dist/core/ecs/store.d.ts +644 -511
- package/dist/core/ecs/store.d.ts.map +1 -1
- package/dist/core/ecs/store_layout_listener.d.cts +8 -8
- package/dist/core/ecs/store_layout_listener.d.ts +8 -8
- package/dist/core/ecs/store_types.d.cts +122 -0
- package/dist/core/ecs/store_types.d.ts +122 -0
- package/dist/core/ecs/store_types.d.ts.map +1 -0
- package/dist/core/ecs/system.d.cts +151 -66
- package/dist/core/ecs/system.d.ts +151 -66
- package/dist/core/ecs/system.d.ts.map +1 -1
- package/dist/core/ecs/system_context.d.cts +296 -0
- package/dist/core/ecs/system_context.d.ts +296 -0
- package/dist/core/ecs/system_context.d.ts.map +1 -0
- package/dist/core/ecs/system_set.d.cts +48 -0
- package/dist/core/ecs/system_set.d.ts +48 -0
- package/dist/core/ecs/system_set.d.ts.map +1 -0
- package/dist/core/ecs/utils/arrays.d.cts +15 -1
- package/dist/core/ecs/utils/arrays.d.ts +15 -1
- package/dist/core/ecs/utils/arrays.d.ts.map +1 -1
- package/dist/core/ecs/utils/error.d.cts +87 -4
- package/dist/core/ecs/utils/error.d.ts +87 -4
- package/dist/core/ecs/utils/error.d.ts.map +1 -1
- package/dist/core/ecs/utils/plugin_error.d.cts +10 -0
- package/dist/core/ecs/utils/plugin_error.d.ts +10 -0
- package/dist/core/ecs/utils/plugin_error.d.ts.map +1 -0
- package/dist/core/restore_errors.d.cts +30 -0
- package/dist/core/restore_errors.d.ts +30 -0
- package/dist/core/restore_errors.d.ts.map +1 -0
- package/dist/core/store/action_ring.d.cts +48 -49
- package/dist/core/store/action_ring.d.ts +48 -49
- package/dist/core/store/action_ring.d.ts.map +1 -1
- package/dist/core/store/allocator.d.cts +166 -114
- package/dist/core/store/allocator.d.ts +166 -114
- package/dist/core/store/allocator.d.ts.map +1 -1
- package/dist/core/store/buffer_backed_column.d.cts +21 -22
- package/dist/core/store/buffer_backed_column.d.ts +21 -22
- package/dist/core/store/buffer_backed_column.d.ts.map +1 -1
- package/dist/core/store/column_store.d.cts +138 -100
- package/dist/core/store/column_store.d.ts +138 -100
- package/dist/core/store/column_store.d.ts.map +1 -1
- package/dist/core/store/command_dispatch.d.cts +14 -15
- package/dist/core/store/command_dispatch.d.ts +14 -15
- package/dist/core/store/command_dispatch.d.ts.map +1 -1
- package/dist/core/store/command_ring.d.cts +41 -45
- package/dist/core/store/command_ring.d.ts +41 -45
- package/dist/core/store/command_ring.d.ts.map +1 -1
- package/dist/core/store/descriptor.d.cts +35 -18
- package/dist/core/store/descriptor.d.ts +35 -18
- package/dist/core/store/descriptor.d.ts.map +1 -1
- package/dist/core/store/entity_index.d.cts +34 -35
- package/dist/core/store/entity_index.d.ts +34 -35
- package/dist/core/store/entity_index.d.ts.map +1 -1
- package/dist/core/store/event_ring.d.cts +40 -42
- package/dist/core/store/event_ring.d.ts +40 -42
- package/dist/core/store/event_ring.d.ts.map +1 -1
- package/dist/core/store/extend.d.cts +15 -9
- package/dist/core/store/extend.d.ts +15 -9
- package/dist/core/store/extend.d.ts.map +1 -1
- package/dist/core/store/grow.d.cts +3 -3
- package/dist/core/store/grow.d.ts +3 -3
- package/dist/core/store/grow.d.ts.map +1 -1
- package/dist/core/store/header.d.cts +37 -33
- package/dist/core/store/header.d.ts +37 -33
- package/dist/core/store/header.d.ts.map +1 -1
- package/dist/core/store/index.d.cts +10 -10
- package/dist/core/store/index.d.ts +10 -10
- package/dist/core/store/index.d.ts.map +1 -1
- package/dist/core/store/layout_ops.d.cts +72 -63
- package/dist/core/store/layout_ops.d.ts +72 -63
- package/dist/core/store/layout_ops.d.ts.map +1 -1
- package/dist/core/store/region_table.d.cts +28 -21
- package/dist/core/store/region_table.d.ts +28 -21
- package/dist/core/store/region_table.d.ts.map +1 -1
- package/dist/core/store/snapshot.d.cts +31 -16
- package/dist/core/store/snapshot.d.ts +31 -16
- package/dist/core/store/snapshot.d.ts.map +1 -1
- package/dist/core/store/state_hash.d.cts +14 -14
- package/dist/core/store/state_hash.d.ts +14 -14
- package/dist/core/store/state_hash.d.ts.map +1 -1
- package/dist/core/store/store_regions.d.cts +10 -10
- package/dist/core/store/store_regions.d.ts +10 -10
- package/dist/core/store/store_regions.d.ts.map +1 -1
- package/dist/core/store/vendored_abi/abi.d.cts +27 -2
- package/dist/core/store/vendored_abi/abi.d.ts +27 -2
- package/dist/core/store/vendored_abi/abi.d.ts.map +1 -1
- package/dist/host_commands-B2KMx8O2.cjs +1 -0
- package/dist/host_commands-ClVPvhSa.cjs +2 -0
- package/dist/host_commands-CwxVQS1z.js +1524 -0
- package/dist/host_commands-nJvTy88E.js +1667 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +16 -11
- package/dist/index.d.ts +16 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.development.cjs +1 -1
- package/dist/index.development.js +6709 -5729
- package/dist/index.js +5664 -4743
- package/dist/internal.cjs +2 -2
- package/dist/internal.d.cts +5 -2
- package/dist/internal.d.ts +5 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.development.cjs +1 -1
- package/dist/internal.development.js +70 -20
- package/dist/internal.js +138 -83
- package/dist/plugins/abi-D3V-QUSf.cjs +1 -0
- package/dist/plugins/abi-NP8pNxpd.js +42 -0
- package/dist/plugins/arrays-Dr_1hCjL.js +28 -0
- package/dist/plugins/arrays-Dt-Joc4i.cjs +1 -0
- package/dist/plugins/assertions-gtDVfbnQ.js +6 -0
- package/dist/plugins/assertions-z5GQd_bB.cjs +1 -0
- package/dist/{extensions → plugins}/editor/editor.d.cts +56 -56
- package/dist/{extensions → plugins}/editor/editor.d.ts +56 -56
- package/dist/plugins/editor/editor.d.ts.map +1 -0
- package/dist/{extensions → plugins}/editor/field_handle.d.cts +13 -13
- package/dist/{extensions → plugins}/editor/field_handle.d.ts +13 -13
- package/dist/plugins/editor/field_handle.d.ts.map +1 -0
- package/dist/plugins/editor/index.d.cts +22 -0
- package/dist/plugins/editor/index.d.ts +22 -0
- package/dist/plugins/editor/index.d.ts.map +1 -0
- package/dist/plugins/editor.cjs +1 -0
- package/dist/plugins/editor.d.cts +1 -0
- package/dist/plugins/editor.d.ts +1 -0
- package/dist/plugins/editor.development.cjs +1 -0
- package/dist/{extensions/editor/index.development.js → plugins/editor.development.js} +83 -66
- package/dist/{extensions/editor/index.js → plugins/editor.js} +83 -66
- package/dist/plugins/events/event_channel.d.cts +13 -0
- package/dist/plugins/events/event_channel.d.ts +13 -0
- package/dist/plugins/events/event_channel.d.ts.map +1 -0
- package/dist/plugins/events/event_registry.d.cts +22 -0
- package/dist/plugins/events/event_registry.d.ts +22 -0
- package/dist/plugins/events/event_registry.d.ts.map +1 -0
- package/dist/plugins/events/facade.d.cts +26 -0
- package/dist/plugins/events/facade.d.ts +26 -0
- package/dist/plugins/events/facade.d.ts.map +1 -0
- package/dist/plugins/events/index.d.cts +10 -0
- package/dist/plugins/events/index.d.ts +10 -0
- package/dist/plugins/events/index.d.ts.map +1 -0
- package/dist/plugins/events.cjs +1 -0
- package/dist/plugins/events.d.cts +1 -0
- package/dist/plugins/events.d.ts +1 -0
- package/dist/plugins/events.development.cjs +1 -0
- package/dist/plugins/events.development.js +174 -0
- package/dist/plugins/events.js +159 -0
- package/dist/plugins/observers/index.d.cts +23 -0
- package/dist/plugins/observers/index.d.ts +23 -0
- package/dist/plugins/observers/index.d.ts.map +1 -0
- package/dist/plugins/observers/observer_registry.d.cts +101 -0
- package/dist/plugins/observers/observer_registry.d.ts +101 -0
- package/dist/plugins/observers/observer_registry.d.ts.map +1 -0
- package/dist/plugins/observers.cjs +1 -0
- package/dist/plugins/observers.d.cts +1 -0
- package/dist/plugins/observers.d.ts +1 -0
- package/dist/plugins/observers.development.cjs +1 -0
- package/dist/plugins/observers.development.js +431 -0
- package/dist/plugins/observers.js +403 -0
- package/dist/plugins/relation_store-BsoegrZh.js +313 -0
- package/dist/plugins/relation_store-DQKSnxsm.cjs +1 -0
- package/dist/plugins/relation_store-RBhpcE28.cjs +1 -0
- package/dist/plugins/relation_store-RqW5Snx6.js +328 -0
- package/dist/plugins/relations/builtin_relations.d.cts +48 -0
- package/dist/plugins/relations/builtin_relations.d.ts +48 -0
- package/dist/plugins/relations/builtin_relations.d.ts.map +1 -0
- package/dist/plugins/relations/facade.d.cts +73 -0
- package/dist/plugins/relations/facade.d.ts +73 -0
- package/dist/plugins/relations/facade.d.ts.map +1 -0
- package/dist/plugins/relations/index.d.cts +11 -0
- package/dist/plugins/relations/index.d.ts +11 -0
- package/dist/plugins/relations/index.d.ts.map +1 -0
- package/dist/{core/ecs → plugins/relations}/relation_service.d.cts +93 -105
- package/dist/{core/ecs → plugins/relations}/relation_service.d.ts +93 -105
- package/dist/plugins/relations/relation_service.d.ts.map +1 -0
- package/dist/plugins/relations/relation_store.d.cts +177 -0
- package/dist/plugins/relations/relation_store.d.ts +177 -0
- package/dist/plugins/relations/relation_store.d.ts.map +1 -0
- package/dist/plugins/relations.cjs +1 -0
- package/dist/plugins/relations.d.cts +1 -0
- package/dist/plugins/relations.d.ts +1 -0
- package/dist/plugins/relations.development.cjs +1 -0
- package/dist/plugins/relations.development.js +603 -0
- package/dist/plugins/relations.js +553 -0
- package/dist/plugins/snapshots/index.d.cts +33 -0
- package/dist/plugins/snapshots/index.d.ts +33 -0
- package/dist/plugins/snapshots/index.d.ts.map +1 -0
- package/dist/plugins/snapshots/resume.d.cts +57 -0
- package/dist/plugins/snapshots/resume.d.ts +57 -0
- package/dist/plugins/snapshots/resume.d.ts.map +1 -0
- package/dist/plugins/snapshots/snapshot_service.d.cts +59 -0
- package/dist/plugins/snapshots/snapshot_service.d.ts +59 -0
- package/dist/plugins/snapshots/snapshot_service.d.ts.map +1 -0
- package/dist/plugins/snapshots.cjs +1 -0
- package/dist/plugins/snapshots.d.cts +1 -0
- package/dist/plugins/snapshots.d.ts +1 -0
- package/dist/plugins/snapshots.development.cjs +1 -0
- package/dist/plugins/snapshots.development.js +737 -0
- package/dist/plugins/snapshots.js +737 -0
- package/dist/plugins/solid/index.d.cts +8 -0
- package/dist/plugins/solid/index.d.ts +8 -0
- package/dist/plugins/solid/index.d.ts.map +1 -0
- package/dist/plugins/solid/solid.d.cts +97 -0
- package/dist/plugins/solid/solid.d.ts +97 -0
- package/dist/plugins/solid/solid.d.ts.map +1 -0
- package/dist/plugins/solid.cjs +1 -0
- package/dist/plugins/solid.d.cts +1 -0
- package/dist/plugins/solid.d.ts +1 -0
- package/dist/plugins/solid.development.cjs +1 -0
- package/dist/plugins/solid.development.js +319 -0
- package/dist/plugins/solid.js +319 -0
- package/dist/plugins/workers/index.d.cts +75 -0
- package/dist/plugins/workers/index.d.ts +75 -0
- package/dist/plugins/workers/index.d.ts.map +1 -0
- package/dist/plugins/workers/node_threads.d.cts +25 -0
- package/dist/plugins/workers/node_threads.d.ts +25 -0
- package/dist/plugins/workers/node_threads.d.ts.map +1 -0
- package/dist/plugins/workers/plan.d.cts +61 -0
- package/dist/plugins/workers/plan.d.ts +61 -0
- package/dist/plugins/workers/plan.d.ts.map +1 -0
- package/dist/plugins/workers/pool.d.cts +144 -0
- package/dist/plugins/workers/pool.d.ts +144 -0
- package/dist/plugins/workers/pool.d.ts.map +1 -0
- package/dist/plugins/workers/protocol.d.cts +112 -0
- package/dist/plugins/workers/protocol.d.ts +112 -0
- package/dist/plugins/workers/protocol.d.ts.map +1 -0
- package/dist/plugins/workers/worker_bind.d.cts +64 -0
- package/dist/plugins/workers/worker_bind.d.ts +64 -0
- package/dist/plugins/workers/worker_bind.d.ts.map +1 -0
- package/dist/plugins/workers/worker_loop.d.cts +14 -0
- package/dist/plugins/workers/worker_loop.d.ts +14 -0
- package/dist/plugins/workers/worker_loop.d.ts.map +1 -0
- package/dist/plugins/workers.cjs +1 -0
- package/dist/plugins/workers.d.cts +1 -0
- package/dist/plugins/workers.d.ts +1 -0
- package/dist/plugins/workers.development.cjs +1 -0
- package/dist/plugins/workers.development.js +596 -0
- package/dist/plugins/workers.js +474 -0
- package/dist/primitives.cjs +1 -1
- package/dist/primitives.d.cts +4 -4
- package/dist/primitives.d.ts +4 -4
- package/dist/primitives.development.cjs +1 -1
- package/dist/primitives.development.js +66 -22
- package/dist/primitives.js +66 -22
- package/dist/shared-BKl12xF1.js +132 -0
- package/dist/shared-Dk-pACZT.cjs +1 -0
- package/dist/shared.cjs +1 -1
- package/dist/shared.d.cts +7 -7
- package/dist/shared.d.ts +7 -7
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.development.cjs +1 -1
- package/dist/shared.development.js +4 -3
- package/dist/shared.js +4 -3
- package/dist/topological_sort-Cc_yXqPT.js +346 -0
- package/dist/topological_sort-vp1imU6D.cjs +1 -0
- package/dist/type_primitives/assertions.d.cts +12 -16
- package/dist/type_primitives/assertions.d.ts +12 -16
- package/dist/type_primitives/assertions.d.ts.map +1 -1
- package/dist/type_primitives/binary_heap/binary_heap.d.cts +2 -6
- package/dist/type_primitives/binary_heap/binary_heap.d.ts +2 -6
- package/dist/type_primitives/binary_heap/binary_heap.d.ts.map +1 -1
- package/dist/type_primitives/bitset/bitset.d.cts +7 -11
- package/dist/type_primitives/bitset/bitset.d.ts +7 -11
- package/dist/type_primitives/bitset/bitset.d.ts.map +1 -1
- package/dist/type_primitives/brand.d.cts +2 -7
- package/dist/type_primitives/brand.d.ts +2 -7
- package/dist/type_primitives/brand.d.ts.map +1 -1
- package/dist/type_primitives/error.d.cts +1 -2
- package/dist/type_primitives/error.d.ts +1 -2
- package/dist/type_primitives/error.d.ts.map +1 -1
- package/dist/type_primitives/index.d.cts +1 -1
- package/dist/type_primitives/index.d.ts +1 -1
- package/dist/type_primitives/sparse_map/sparse_map.d.cts +3 -7
- package/dist/type_primitives/sparse_map/sparse_map.d.ts +3 -7
- package/dist/type_primitives/sparse_map/sparse_map.d.ts.map +1 -1
- package/dist/type_primitives/sparse_set/sparse_set.d.cts +2 -6
- package/dist/type_primitives/sparse_set/sparse_set.d.ts +2 -6
- package/dist/type_primitives/sparse_set/sparse_set.d.ts.map +1 -1
- package/dist/type_primitives/topological_sort/topological_sort.d.cts +1 -5
- package/dist/type_primitives/topological_sort/topological_sort.d.ts +1 -5
- package/dist/type_primitives/topological_sort/topological_sort.d.ts.map +1 -1
- package/dist/type_primitives/typed_arrays/typed_arrays.d.cts +18 -17
- package/dist/type_primitives/typed_arrays/typed_arrays.d.ts +18 -17
- package/dist/type_primitives/typed_arrays/typed_arrays.d.ts.map +1 -1
- package/dist/utils/error.d.ts.map +1 -1
- package/dist/version.d.cts +4 -4
- package/dist/version.d.ts +4 -4
- package/dist/worker.cjs +1 -0
- package/dist/worker.d.cts +17 -0
- package/dist/worker.d.ts +17 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.development.cjs +1 -0
- package/dist/worker.development.js +253 -0
- package/dist/worker.js +253 -0
- package/package.json +363 -191
- package/dist/core/ecs/builtin_relations.d.cts +0 -43
- package/dist/core/ecs/builtin_relations.d.ts +0 -43
- package/dist/core/ecs/builtin_relations.d.ts.map +0 -1
- package/dist/core/ecs/event_registry.d.cts +0 -22
- package/dist/core/ecs/event_registry.d.ts +0 -22
- package/dist/core/ecs/event_registry.d.ts.map +0 -1
- package/dist/core/ecs/relation_service.d.ts.map +0 -1
- package/dist/core/ecs/resume.d.cts +0 -85
- package/dist/core/ecs/resume.d.ts +0 -85
- package/dist/core/ecs/resume.d.ts.map +0 -1
- package/dist/core/ecs/snapshot_service.d.cts +0 -94
- package/dist/core/ecs/snapshot_service.d.ts +0 -94
- package/dist/core/ecs/snapshot_service.d.ts.map +0 -1
- package/dist/extensions/editor/editor.d.ts.map +0 -1
- package/dist/extensions/editor/field_handle.d.ts.map +0 -1
- package/dist/extensions/editor/index.cjs +0 -1
- package/dist/extensions/editor/index.d.cts +0 -21
- package/dist/extensions/editor/index.d.ts +0 -21
- package/dist/extensions/editor/index.d.ts.map +0 -1
- package/dist/extensions/editor/index.development.cjs +0 -1
- package/dist/extensions/reactive/ecs_sync.d.cts +0 -214
- package/dist/extensions/reactive/ecs_sync.d.ts +0 -214
- package/dist/extensions/reactive/ecs_sync.d.ts.map +0 -1
- package/dist/extensions/reactive/index.cjs +0 -1
- package/dist/extensions/reactive/index.d.cts +0 -23
- package/dist/extensions/reactive/index.d.ts +0 -23
- package/dist/extensions/reactive/index.d.ts.map +0 -1
- package/dist/extensions/reactive/index.development.cjs +0 -1
- package/dist/extensions/reactive/index.development.js +0 -215
- package/dist/extensions/reactive/index.js +0 -215
- package/dist/extensions/solid/index.cjs +0 -1
- package/dist/extensions/solid/index.d.cts +0 -6
- package/dist/extensions/solid/index.d.ts +0 -6
- package/dist/extensions/solid/index.d.ts.map +0 -1
- package/dist/extensions/solid/index.development.cjs +0 -1
- package/dist/extensions/solid/index.development.js +0 -40
- package/dist/extensions/solid/index.js +0 -36
- package/dist/extensions/solid/kernel_solid.d.cts +0 -45
- package/dist/extensions/solid/kernel_solid.d.ts +0 -45
- package/dist/extensions/solid/kernel_solid.d.ts.map +0 -1
- package/dist/host_commands-3jyFfWNg.js +0 -2326
- package/dist/host_commands-DNf0f6ko.cjs +0 -1
- package/dist/internal-C9jjL90H.js +0 -2485
- package/dist/internal-RZN14uMw.cjs +0 -2
- package/dist/interop-CT-REx0W.cjs +0 -1
- package/dist/interop-CcY6ASQc.js +0 -18
- package/dist/interop-DqxleBo7.js +0 -18
- package/dist/interop-T1HZ-Dpa.cjs +0 -1
- package/dist/kernel-Cz2Kxlwl.js +0 -240
- package/dist/kernel-DgyrLFjW.js +0 -227
- package/dist/kernel-sw7ScKWu.cjs +0 -1
- package/dist/kernel-yWV3XnAb.cjs +0 -1
- package/dist/reactive/array.d.cts +0 -24
- package/dist/reactive/array.d.ts +0 -24
- package/dist/reactive/array.d.ts.map +0 -1
- package/dist/reactive/index.cjs +0 -1
- package/dist/reactive/index.d.cts +0 -11
- package/dist/reactive/index.d.ts +0 -11
- package/dist/reactive/index.d.ts.map +0 -1
- package/dist/reactive/index.development.cjs +0 -1
- package/dist/reactive/index.development.js +0 -18
- package/dist/reactive/index.js +0 -18
- package/dist/reactive/interop.d.cts +0 -19
- package/dist/reactive/interop.d.ts +0 -19
- package/dist/reactive/interop.d.ts.map +0 -1
- package/dist/reactive/kernel.d.cts +0 -73
- package/dist/reactive/kernel.d.ts +0 -73
- package/dist/reactive/kernel.d.ts.map +0 -1
- package/dist/reactive/map.d.cts +0 -16
- package/dist/reactive/map.d.ts +0 -16
- package/dist/reactive/map.d.ts.map +0 -1
- package/dist/reactive/shallow.d.cts +0 -11
- package/dist/reactive/shallow.d.ts +0 -11
- package/dist/reactive/shallow.d.ts.map +0 -1
- package/dist/reactive/struct.d.cts +0 -10
- package/dist/reactive/struct.d.ts +0 -10
- package/dist/reactive/struct.d.ts.map +0 -1
- package/dist/shallow-9S1pY_Iw.js +0 -159
- package/dist/shallow-C5YmTbtb.cjs +0 -1
- package/dist/shallow-DQ8aG51_.cjs +0 -1
- package/dist/shallow-DfnAm9vG.js +0 -150
- package/dist/shared-BMXh9hxm.cjs +0 -1
- package/dist/shared-DQKK0i-E.js +0 -109
- package/dist/topological_sort-BDvEyb9W.cjs +0 -1
- package/dist/topological_sort-DK6EjpWa.js +0 -135
- package/dist/typed_arrays-BcXcnzB4.js +0 -258
- package/dist/typed_arrays-D3NmwtCG.cjs +0 -1
package/dist/core/ecs/store.d.ts
CHANGED
|
@@ -1,66 +1,30 @@
|
|
|
1
1
|
import { EntityID } from './entity.js';
|
|
2
|
+
import { AccessorColumns, CursorBinder, SparseCursorCheck } from './ref.js';
|
|
2
3
|
import { FrameTraceSink } from './frame_trace.js';
|
|
3
4
|
import { ComponentDef, ComponentHandle, ComponentSchema, FieldValues } from './component.js';
|
|
4
|
-
import { SparseComponentDef, SparseComponentID } from './sparse_store.js';
|
|
5
|
-
import { RelationDef,
|
|
6
|
-
import {
|
|
5
|
+
import { SparseComponentStore, SparseComponentDef, SparseComponentID } from './sparse_store.js';
|
|
6
|
+
import { RelationDef, RelationHooks, RelationServiceHost } from './relation.js';
|
|
7
|
+
import { EventHooks } from './event.js';
|
|
8
|
+
import { ResourceRegistry } from './resource_registry.js';
|
|
7
9
|
import { BitSet, TypedArrayTag } from '../../type_primitives/index.js';
|
|
8
10
|
import { Archetype, ArchetypeID } from './archetype.js';
|
|
9
|
-
import { Query
|
|
11
|
+
import { Query } from './query.js';
|
|
12
|
+
import { QueryHost } from './query_cache.js';
|
|
13
|
+
import { QueryTerms } from './query_terms.js';
|
|
14
|
+
import { DrainResult, ObservationFlags, StructuralObserverEvents, Template } from './store_types.js';
|
|
10
15
|
import { ObserverHost } from './observer.js';
|
|
16
|
+
import { ChangeFeed } from './change_feed.js';
|
|
17
|
+
import { EntityAllocator } from './entity_allocator.js';
|
|
18
|
+
import { SnapshotHooks, SnapshotHost } from './snapshot.js';
|
|
11
19
|
import { InPlaceBufferAllocator, ColumnStoreRegionHandle, StoreRegionSpec, ColumnStore } from '../store/index.js';
|
|
12
20
|
import { ECSMemoryCapContext } from './ecs_memory.js';
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/** Has an onAdd observer — collect effective adds for this component. */
|
|
21
|
-
obsAdd: boolean;
|
|
22
|
-
/** Has an onRemove observer — collect effective removes for this component. */
|
|
23
|
-
obsRem: boolean;
|
|
24
|
-
/** Has an onDisable observer (#677) — collect effective disables for this
|
|
25
|
-
* component at the toggle drain. */
|
|
26
|
-
obsDisable: boolean;
|
|
27
|
-
/** Has an onEnable observer (#677) — collect effective enables for this
|
|
28
|
-
* component at the toggle drain. */
|
|
29
|
-
obsEnable: boolean;
|
|
30
|
-
/** Has a per-entity onSet observer — record dirty rows on the write path
|
|
31
|
-
* (the ADR-0012 opt-in dirty list). */
|
|
32
|
-
trackDirty: boolean;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Effective `(component, entity)` structural events for one fixed-point round,
|
|
36
|
-
* collected during `_flushAdds` / `_flushRemoves` and handed to the observer
|
|
37
|
-
* dispatch hook. Flat parallel arrays, count-bounded (`*_len`), reused across
|
|
38
|
-
* rounds — never reallocated in the flush. This is a scheduling artifact: it is
|
|
39
|
-
* NOT part of `stateHash` or snapshot. See `observer.ts`.
|
|
40
|
-
*/
|
|
41
|
-
export interface StructuralObserverEvents {
|
|
42
|
-
addComp: number[];
|
|
43
|
-
addEid: number[];
|
|
44
|
-
addLen: number;
|
|
45
|
-
remComp: number[];
|
|
46
|
-
remEid: number[];
|
|
47
|
-
remLen: number;
|
|
48
|
-
/** Effective disable events (#677) — collected during the toggle drain
|
|
49
|
-
* (`_flushToggles`), one per `(component, entity)` of each net-disabled
|
|
50
|
-
* entity's mask. Empty on a structural (add/remove/destroy) round. */
|
|
51
|
-
disComp: number[];
|
|
52
|
-
disEid: number[];
|
|
53
|
-
disLen: number;
|
|
54
|
-
/** Effective enable events (#677), symmetric with the disable arrays. */
|
|
55
|
-
enaComp: number[];
|
|
56
|
-
enaEid: number[];
|
|
57
|
-
enaLen: number;
|
|
58
|
-
}
|
|
59
|
-
/** Runtime shape of one template / `addComponents` entry: a def plus optional
|
|
60
|
-
* field values (omitted fields zero-fill). The public authoring surface is
|
|
61
|
-
* callable-bundle varargs, schema-checked per item by `StrictBundles`
|
|
62
|
-
* (component.ts); the `ECS` facade normalizes those bundles into this erased
|
|
63
|
-
* array before calling the store, so the store stays schema-agnostic. */
|
|
21
|
+
export type { ComponentMeta, DrainResult, ObservationFlags, StructuralObserverEvents, Template } from './store_types.js';
|
|
22
|
+
/** Runtime shape of one template entry or `addComponents` entry: a def plus
|
|
23
|
+
* optional field values (an omitted field zero-fills). The public authoring
|
|
24
|
+
* surface is callable-bundle varargs, schema-checked for each item by
|
|
25
|
+
* `StrictBundles` in `component.ts`. The `ECS` facade normalizes those bundles
|
|
26
|
+
* into this erased array before it calls the store, so the store stays
|
|
27
|
+
* schema-agnostic. */
|
|
64
28
|
type TemplateEntryData = {
|
|
65
29
|
readonly def: ComponentDef;
|
|
66
30
|
readonly values?: Readonly<Record<string, number>>;
|
|
@@ -74,201 +38,259 @@ type TemplateFieldNames<Defs extends readonly ComponentDef[]> = Defs[number] ext
|
|
|
74
38
|
export type TemplateOverrides<Defs extends readonly ComponentDef[]> = {
|
|
75
39
|
readonly [K in TemplateFieldNames<Defs>]?: number;
|
|
76
40
|
};
|
|
77
|
-
declare const __templateDefs: unique symbol;
|
|
78
|
-
/** A resolved template (#462) — an archetype template produced by
|
|
79
|
-
* `ECS.template(...)`. **Opaque** apart from `defs`: callers hold it and pass
|
|
80
|
-
* it to `ECS.spawn` / `ECS.spawnMany` (and may reference it in a system's
|
|
81
|
-
* `spawns` / `despawns` access declaration — the scheduler expands it to
|
|
82
|
-
* `defs`); the remaining fields are engine-internal and may change. `spawn`
|
|
83
|
-
* lands an entity directly in `archetype_id` with zero archetype transitions,
|
|
84
|
-
* writing `flatValues` (defaults in `_flatColumns` order) in one append
|
|
85
|
-
* pass. See ADR-0010. */
|
|
86
|
-
export interface Template<Defs extends readonly ComponentDef[] = readonly ComponentDef[]> {
|
|
87
|
-
readonly archetypeId: ArchetypeID;
|
|
88
|
-
readonly flatValues: number[];
|
|
89
|
-
readonly overrideIndex: Map<string, number>;
|
|
90
|
-
/** The component set this template spawns into, in entry order. */
|
|
91
|
-
readonly defs: readonly ComponentDef[];
|
|
92
|
-
readonly [__templateDefs]?: Defs;
|
|
93
|
-
}
|
|
94
41
|
export interface StoreOptions {
|
|
95
42
|
initialCapacity?: number;
|
|
96
43
|
/** Pluggable SAB buffer source. When provided, `createColumnStore`,
|
|
97
44
|
* `extendColumnStore`, and `growColumnStore` route through it. Default is
|
|
98
|
-
* `growableSabAllocator
|
|
45
|
+
* `growableSabAllocator`. Typed `InPlaceBufferAllocator`: a live
|
|
99
46
|
* Store's flush loops hoist entity-index views across grows, so only
|
|
100
|
-
* in-place allocators may back one
|
|
47
|
+
* in-place allocators may back one, the constructor also
|
|
101
48
|
* runtime-asserts the marker for untyped JS callers. Consumers normally
|
|
102
|
-
* don't touch this directly
|
|
49
|
+
* don't touch this directly. `ECSOptions.memory` resolves to it. */
|
|
103
50
|
bufferAllocator?: InPlaceBufferAllocator;
|
|
104
|
-
/** Sizing intent the world was constructed with
|
|
51
|
+
/** Sizing intent the world was constructed with, used to phrase
|
|
105
52
|
* allocator-cap and entity-index-overflow errors in the caller's own
|
|
106
|
-
* terms (
|
|
107
|
-
* `ECS` from `resolveECSMemory
|
|
53
|
+
* terms (a multiple of the declared budget) instead of raw bytes. Wired by
|
|
54
|
+
* `ECS` from `resolveECSMemory`. Absent for bare test Stores. */
|
|
108
55
|
capContext?: ECSMemoryCapContext;
|
|
109
56
|
/** Fired after every SAB resize (extend or grow). The new SAB has
|
|
110
57
|
* already been built and archetypes have already refreshed their
|
|
111
|
-
* views by the time this fires.
|
|
112
|
-
*
|
|
113
|
-
|
|
114
|
-
/** Max live entities the SAB entity-index region holds
|
|
115
|
-
* Default `ENTITY_INDEX_DEFAULT_CAPACITY` (`1 << 20
|
|
58
|
+
* views by the time this fires. `ECS` uses it to call `setLayout` on every
|
|
59
|
+
* subscribed layout listener, so a WASM-side cached pointer re-walks. */
|
|
60
|
+
onBufferReplaced?: () => void;
|
|
61
|
+
/** Max live entities the SAB entity-index region holds.
|
|
62
|
+
* Default `ENTITY_INDEX_DEFAULT_CAPACITY` (`1 << 20`, the full EntityID
|
|
116
63
|
* index space). Exceeding this at runtime throws `EID_MAX_INDEX_OVERFLOW`.
|
|
117
|
-
* Tests with small entity counts may set lower to
|
|
118
|
-
*
|
|
119
|
-
* comfortably in the default. */
|
|
64
|
+
* Tests with small entity counts may set lower to shrink the SAB region or
|
|
65
|
+
* to make index exhaustion reachable. */
|
|
120
66
|
entityIndexCapacity?: number;
|
|
121
|
-
/**
|
|
67
|
+
/** Byte offset inside the backing where the store header goes. Default 0.
|
|
68
|
+
* Forwarded verbatim to `createColumnStore`, and every offset in the bytes
|
|
69
|
+
* is relative to it. `ECSOptions.memory.storeBase` resolves to it. */
|
|
70
|
+
storeBase?: number;
|
|
71
|
+
/** Consumer-declared SAB regions, forwarded verbatim to
|
|
122
72
|
* `createColumnStore`. Each `StoreRegionSpec` carries an opaque `region_id`,
|
|
123
|
-
* a precomputed byte size, and an `init` closure
|
|
124
|
-
* generically and exposes them
|
|
125
|
-
* A
|
|
126
|
-
*
|
|
73
|
+
* a precomputed byte size, and an `init` closure. The engine lays them out
|
|
74
|
+
* generically and exposes them through `regionHandle(id)` and
|
|
75
|
+
* `regionOffset(id)`. A consumer supplies the specs. The engine ships no
|
|
76
|
+
* region of its own. Omitted ⇒ none. */
|
|
127
77
|
regions?: readonly StoreRegionSpec[];
|
|
128
|
-
/** Byte size of the opt-in sim-bindings region
|
|
78
|
+
/** Byte size of the opt-in sim-bindings region, forwarded verbatim to
|
|
129
79
|
* `createColumnStore`. A consumer that attaches a WASM backend passes its own
|
|
130
|
-
* size
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* drift an engine golden. */
|
|
80
|
+
* size, computed from its own binding manifest. The host then writes the
|
|
81
|
+
* `(component_id, field_id)` ids into the region. Omitted or 0 ⇒ no region,
|
|
82
|
+
* so a pure-TS world pays nothing for the WASM seam. The size stays out of
|
|
83
|
+
* the engine ABI, so a manifest edit does not drift an engine golden. */
|
|
135
84
|
bindingsRegionBytes?: number;
|
|
136
|
-
/** Opt into the **determinism surface
|
|
137
|
-
* Gates the three methods that fold
|
|
85
|
+
/** Opt into the **determinism surface**. Default `false`.
|
|
86
|
+
* Gates the three methods that fold and serialize state in canonical (sorted)
|
|
138
87
|
* order: `stateHash`, `snapshotSparse`, `restoreSparse`. When `false`
|
|
139
|
-
* those throw `DETERMINISM_DISABLED
|
|
88
|
+
* those throw `DETERMINISM_DISABLED`, the canonical-ordering tax (sparse
|
|
140
89
|
* `canonicalIndices` sort + relation target-set sort) is never paid, and a
|
|
141
90
|
* consumer can't accidentally read a non-canonical digest. When `true`,
|
|
142
|
-
* today's behavior is reproduced bit-for-bit. This is the
|
|
91
|
+
* today's behavior is reproduced bit-for-bit. This is the only effect of the
|
|
143
92
|
* flag: it does not touch the per-tick path, the in-place-allocator invariant
|
|
144
|
-
* (
|
|
93
|
+
* (a memory-safety requirement that holds regardless), or the
|
|
145
94
|
* always-on `enabled_count` partition maintenance. The flag's value is a
|
|
146
|
-
*
|
|
147
|
-
* called per tick. */
|
|
95
|
+
* plugin gate, not a hot-path switch. `stateHash` and the snapshot are
|
|
96
|
+
* never called per tick. */
|
|
148
97
|
deterministic?: boolean;
|
|
149
98
|
}
|
|
150
|
-
export declare class Store implements ObserverHost, QueryHost {
|
|
151
|
-
private readonly
|
|
152
|
-
private readonly
|
|
153
|
-
private
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
private readonly
|
|
159
|
-
private
|
|
160
|
-
private
|
|
161
|
-
private
|
|
162
|
-
private
|
|
163
|
-
private
|
|
99
|
+
export declare class Store implements ChangeFeed, ObserverHost, QueryHost {
|
|
100
|
+
private readonly _entityAllocator;
|
|
101
|
+
private readonly _componentMetas;
|
|
102
|
+
private readonly _sparseStores;
|
|
103
|
+
/** Debug names parallel to `_sparseStores`, diagnostics only. */
|
|
104
|
+
private readonly _sparseNames;
|
|
105
|
+
/** The reused result of `drainSparseSet`, parallel to `_sparseStores`,
|
|
106
|
+
* allocated for a sparse component with an entity-level onSet. */
|
|
107
|
+
private readonly _sparseDrains;
|
|
108
|
+
private _relations;
|
|
109
|
+
private _events;
|
|
110
|
+
private readonly _archGraph;
|
|
111
|
+
private _emptyArchetypeId;
|
|
112
|
+
private _entityArchetypes;
|
|
113
|
+
private _entityRows;
|
|
114
|
+
private readonly _queries;
|
|
164
115
|
private readonly _deferred;
|
|
165
|
-
private
|
|
166
|
-
|
|
167
|
-
/**
|
|
116
|
+
private _snapshots;
|
|
117
|
+
get relations(): RelationHooks;
|
|
118
|
+
/** The relation service, for a caller that is not `ecs.relations`.
|
|
119
|
+
*
|
|
120
|
+
* The getter above names `ecs.relations` in its fault. That is the wrong
|
|
121
|
+
* remedy when a system context or a query term reached the service. Every
|
|
122
|
+
* seam a user can reach passes its own name here. Error path only. The
|
|
123
|
+
* successful read is the same field read the getter makes. */
|
|
124
|
+
requireRelations(api: string): RelationHooks;
|
|
125
|
+
/** Build the host the relation service needs. Closures, not field
|
|
126
|
+
* references: `generations`, `entityArchetypes` and `entityRows` are
|
|
127
|
+
* reallocated when capacity grows, so each accessor re-reads the live
|
|
128
|
+
* field. */
|
|
129
|
+
relationHost(): RelationServiceHost;
|
|
130
|
+
/** Install the relations plugin. Called once, by the plugin. */
|
|
131
|
+
installRelations(service: RelationHooks): void;
|
|
132
|
+
get events(): EventHooks;
|
|
133
|
+
/** The event registry, for a caller that is not `ecs.events`. Names the
|
|
134
|
+
* seam the user reached, the way `requireRelations` does. */
|
|
135
|
+
requireEvents(api: string): EventHooks;
|
|
136
|
+
/** Whether the events plugin is installed. Read once per frame by the
|
|
137
|
+
* tick-tail clear, which must be a no-op on a world without channels. */
|
|
138
|
+
get hasEvents(): boolean;
|
|
139
|
+
/** Install the events plugin. Called once, by the plugin. */
|
|
140
|
+
installEvents(registry: EventHooks): void;
|
|
141
|
+
/** Build the host a snapshot plugin needs. Only the store can reach
|
|
142
|
+
* these fields, so it builds the record and the plugin owns the service.
|
|
143
|
+
* The accessors re-read live fields per call: the column store and the
|
|
144
|
+
* entity-index views are replaced on a restore. All cold path. */
|
|
145
|
+
snapshotHost(): SnapshotHost;
|
|
146
|
+
/** The allocator, which is its own snapshot seam. Handed to the plugin
|
|
147
|
+
* so the store need not construct the service itself. */
|
|
148
|
+
get entityAllocator(): EntityAllocator;
|
|
149
|
+
/** Install the snapshot plugin. Called once, by the plugin. */
|
|
150
|
+
installSnapshots(service: SnapshotHooks): void;
|
|
151
|
+
get snapshots(): SnapshotHooks;
|
|
152
|
+
get resources(): ResourceRegistry;
|
|
153
|
+
tick: number;
|
|
154
|
+
/** The change tick. A monotonic counter that the schedule advances before
|
|
155
|
+
* each system run, before each phase flush, before the onSet dispatch and
|
|
156
|
+
* at the end of each update. Every write stamps it on the archetype column
|
|
157
|
+
* it touches, and a consumer compares against the value of its own last
|
|
158
|
+
* run. The frame tick above cannot order a writer and a reader inside one
|
|
159
|
+
* frame, so a write by an earlier system was reported on two frames. This
|
|
160
|
+
* counter orders them. It starts above the initial column stamp, so the
|
|
161
|
+
* first run of a system sees every row that exists. A scheduling artifact:
|
|
162
|
+
* not in `stateHash` or the snapshot, and monotonic across a restore. */
|
|
163
|
+
changeTick: number;
|
|
164
|
+
/** Advance the change tick and return the new value, the stamp for the
|
|
165
|
+
* run that follows. Hot path: one increment per system run. */
|
|
166
|
+
advanceChangeTick(): number;
|
|
167
|
+
/** Per-world frame-trace sink, installed via `ECS.setTrace`.
|
|
168
168
|
* `null` unless a consumer attaches a recorder. Every call site is
|
|
169
|
-
* `if (DEV) store.
|
|
170
|
-
* the seam and pay only this one nullable field. The sink observes
|
|
171
|
-
* folds into `stateHash
|
|
172
|
-
* observer state below
|
|
173
|
-
|
|
169
|
+
* `if (DEV) store.trace?.…`, so production builds dead-code-eliminate
|
|
170
|
+
* the seam and pay only this one nullable field. The sink observes. It never
|
|
171
|
+
* folds into `stateHash`. It is a scheduling artifact, like `_changedTick`
|
|
172
|
+
* and like the observer state below. */
|
|
173
|
+
trace: FrameTraceSink | null;
|
|
174
174
|
private _structuralObserverCount;
|
|
175
|
-
/** Count of components with any onDisable
|
|
175
|
+
/** Count of components with any onDisable and onEnable observer. While 0
|
|
176
176
|
* (with `_structuralObserverCount` also 0), `flushStructural` takes the
|
|
177
177
|
* byte-for-byte fast path and the toggle drain skips event collection. */
|
|
178
178
|
private _toggleObserverCount;
|
|
179
|
+
/** One flag record for each consumer of the change feed, keyed by the
|
|
180
|
+
* plugin name, each array indexed by component id and sparse. The
|
|
181
|
+
* store merges by OR across the records, so the flags a component carries
|
|
182
|
+
* are the union of what every consumer asked for. Cold: written when a
|
|
183
|
+
* consumer gains or loses an observer, read on the same call. */
|
|
184
|
+
private readonly _observationFlags;
|
|
185
|
+
/** The sparse half of the record above, one boolean for each sparse
|
|
186
|
+
* component a consumer wants the entity grain of. */
|
|
187
|
+
private readonly _sparseObservationFlags;
|
|
179
188
|
/** Reused effective-event scratch for the current flush round. */
|
|
180
189
|
private readonly _obsEvents;
|
|
181
|
-
/** Installed via `
|
|
182
|
-
* events to
|
|
183
|
-
* further structural ops.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
*
|
|
188
|
-
|
|
190
|
+
/** Installed via `addStructuralHook`, each one dispatches a round's
|
|
191
|
+
* collected events to one consumer of the change feed, which may enqueue
|
|
192
|
+
* further structural ops. Empty on a world that installed no consumer, and
|
|
193
|
+
* the flush reads it once per round. */
|
|
194
|
+
private readonly _structuralHooks;
|
|
195
|
+
/** Add a consumer of the structural event batches. Each round of the
|
|
196
|
+
* observed flush hands its effective events to every hook, in install
|
|
197
|
+
* order. The events scratch is store-owned and reused, so a hook reads it
|
|
198
|
+
* during the call and copies whatever it keeps. Cold path. */
|
|
199
|
+
addStructuralHook(fn: (ev: StructuralObserverEvents) => void): void;
|
|
189
200
|
private _collectDestroyEid;
|
|
190
201
|
private readonly _collectDestroyRemoveBit;
|
|
191
202
|
private _collectToggleEid;
|
|
192
203
|
private readonly _collectDisableBit;
|
|
193
204
|
private readonly _collectEnableBit;
|
|
194
|
-
/** Net-transition snapshot for the toggle drain
|
|
195
|
-
* state at the
|
|
205
|
+
/** Net-transition snapshot for the toggle drain: entity → its disabled
|
|
206
|
+
* state at the start of the drain. Reused, cleared each drain. Lets
|
|
196
207
|
* `_flushToggles` emit one event per *net* transition (disable→enable→disable
|
|
197
|
-
* within a tick = a single onDisable) instead of one per buffered op
|
|
208
|
+
* within a tick = a single onDisable) instead of one per buffered op, required
|
|
198
209
|
* because the radix canonical-order pass would otherwise reorder duplicate eids
|
|
199
|
-
* and mis-sequence a consumer's delete
|
|
210
|
+
* and mis-sequence a consumer's delete and republish. */
|
|
200
211
|
private readonly _toggleInitial;
|
|
201
|
-
|
|
212
|
+
/** True once any component has row ticks. Gates the record at every write
|
|
213
|
+
* site, so a world without one pays a load and a branch there. */
|
|
214
|
+
anyDirtyTracked: boolean;
|
|
215
|
+
/** The components with row ticks, for the archetypes born later. */
|
|
202
216
|
private readonly _dirtyTrackedCids;
|
|
203
217
|
private readonly _dirtyLists;
|
|
204
|
-
private readonly
|
|
218
|
+
private readonly _drainResults;
|
|
205
219
|
/** Set by any path that changes a SAB-backed archetype's live row count
|
|
206
|
-
* (`flushStructural
|
|
220
|
+
* (`flushStructural` and `flushDestroys` when they did work, immediate
|
|
207
221
|
* `destroyEntity`, `addComponent(s)`, `removeComponent(s)` on the
|
|
208
|
-
* Store). Cleared by `
|
|
209
|
-
* phases' `ctx.flush
|
|
222
|
+
* Store). Cleared by `publishRowCounts`. Lets read-only
|
|
223
|
+
* phases' `ctx.flush` skip the descriptor walk entirely. */
|
|
210
224
|
private _rowCountsDirty;
|
|
211
225
|
/** Monotonic counter bumped by every membership-changing path (immediate
|
|
212
226
|
* `addComponent(s)`, `removeComponent(s)`, `destroyEntity`,
|
|
213
227
|
* `batchAddComponent`, `batchRemoveComponent`, `flushStructural`,
|
|
214
|
-
* `
|
|
215
|
-
* Read by `Query.
|
|
216
|
-
*
|
|
217
|
-
* reuses its cached non-empty list
|
|
218
|
-
* over `
|
|
219
|
-
* mutation
|
|
220
|
-
*
|
|
221
|
-
* its `QueryResolver` impl
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
private readonly _scratchTargetMask;
|
|
228
|
+
* `flushDestroys`, and new-archetype installs in `ArchetypeGraph.install`).
|
|
229
|
+
* Read by `Query.nonEmptyArchs()` via `QueryResolver.getQueryDirtyEpoch`.
|
|
230
|
+
* A query whose stored `_lastSeenEpoch` matches the current epoch
|
|
231
|
+
* reuses its cached non-empty list. Replaces the previous walk
|
|
232
|
+
* over `_registeredQueries` that wrote one dirty bit per query per
|
|
233
|
+
* mutation. A startup that adds many rows used to write one bit for each
|
|
234
|
+
* query at each mutation. Now it writes one integer increment. Public, so
|
|
235
|
+
* ECS can forward through its `QueryResolver` impl. It is not part of the
|
|
236
|
+
* user-facing API. */
|
|
237
|
+
queryDirtyEpoch: number;
|
|
225
238
|
private _columnStore;
|
|
239
|
+
/** Row capacity every new archetype's columns start with. Taken from
|
|
240
|
+
* `StoreOptions.initialCapacity` at construction, read again by the
|
|
241
|
+
* archetype graph's `initialCapacity` host closure whenever it plants a
|
|
242
|
+
* new archetype. It sat under the row-grain banner, which owns the change
|
|
243
|
+
* detection state, and it sizes the backing instead. */
|
|
244
|
+
private readonly _initialCapacity;
|
|
226
245
|
/** Installed on every SAB-backed Archetype so the Archetype can
|
|
227
246
|
* request a SAB grow when an insertion would exceed its column
|
|
228
247
|
* capacity. Doubles the offending archetype's row capacity (or jumps
|
|
229
248
|
* to whatever fits `arch.length + additional`, whichever is larger),
|
|
230
249
|
* reallocs the SAB via `growColumnStore` (live rows of every archetype
|
|
231
250
|
* are carried forward), and republishes column views to every
|
|
232
|
-
* SAB-backed archetype.
|
|
251
|
+
* SAB-backed archetype. */
|
|
233
252
|
private readonly _growHandler;
|
|
234
|
-
/** Build the intent-aware fatal for an allocator cap hit
|
|
235
|
-
* allocator can only name raw bytes
|
|
253
|
+
/** Build the intent-aware fatal for an allocator cap hit. The
|
|
254
|
+
* allocator can only name raw bytes. The Store knows what the caller
|
|
236
255
|
* declared (`capContext`) and how many entities are live, so the error
|
|
237
|
-
*
|
|
256
|
+
* names the live count as a multiple of the declared budget instead
|
|
238
257
|
* of leaving the caller to reverse-engineer byte counts. Fatality is
|
|
239
|
-
* unchanged (
|
|
258
|
+
* unchanged (no grow-beyond-cap fallback). */
|
|
240
259
|
private _capExceededError;
|
|
241
260
|
private readonly _bufferAllocator;
|
|
242
261
|
private readonly _capContext;
|
|
243
262
|
private readonly _onBufferResized;
|
|
244
|
-
/** Construct with an `
|
|
245
|
-
* options object (
|
|
246
|
-
* `
|
|
263
|
+
/** Construct with an `_initialCapacity` number (legacy form) or an
|
|
264
|
+
* options object (adds `bufferAllocator` and
|
|
265
|
+
* `onBufferReplaced` callback). Both signatures coexist so test fixtures
|
|
247
266
|
* that pass `new Store(4)` keep working. */
|
|
248
267
|
constructor(arg?: number | StoreOptions);
|
|
249
268
|
/** Capacity of the entity-index SAB region (max slots ≈ max live
|
|
250
|
-
* entities). Fixed at construction
|
|
269
|
+
* entities). Fixed at construction. A future
|
|
251
270
|
* follow-up will grow it via `growColumnStore` when `entityHighWater`
|
|
252
271
|
* hits the cap. */
|
|
253
272
|
private readonly _entityIndexCapacity;
|
|
254
|
-
/**
|
|
273
|
+
/** Byte offset of the store header inside the backing. Fixed at
|
|
274
|
+
* construction, and every offset in the bytes is relative to it. */
|
|
275
|
+
private readonly _storeBase;
|
|
276
|
+
/** Consumer-declared SAB regions, captured so the realloc path
|
|
255
277
|
* re-lays them out. `undefined` when no consumer regions were declared.
|
|
256
278
|
* The region contents survive a grow via the self-describing region table
|
|
257
|
-
* (`extend.ts` snapshot
|
|
279
|
+
* (`extend.ts` snapshot and restore), so this is only the layout recipe. */
|
|
258
280
|
private readonly _regions;
|
|
259
|
-
/** Byte size of the opt-in sim-bindings region
|
|
260
|
-
* pure-TS default). Captured so the initial `createColumnStore` reserves it
|
|
261
|
-
*
|
|
262
|
-
* `optionsFromOld`, so it is not threaded through the grow
|
|
281
|
+
/** Byte size of the opt-in sim-bindings region. 0 ⇒ no region (the
|
|
282
|
+
* pure-TS default). Captured so the initial `createColumnStore` reserves it.
|
|
283
|
+
* Across a realloc the size is re-derived from the old header by
|
|
284
|
+
* `optionsFromOld`, so it is not threaded through the grow and extend path. */
|
|
263
285
|
private readonly _bindingsRegionBytes;
|
|
264
|
-
/** Determinism opt-in
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
* `enabled_count` partition
|
|
286
|
+
/** Determinism opt-in. When `false` (the default), the canonical-ordering
|
|
287
|
+
* determinism surface throws `DETERMINISM_DISABLED` rather than run its
|
|
288
|
+
* sort. That surface is `stateHash`, `snapshotSparse` and `restoreSparse`.
|
|
289
|
+
* The memory-safety invariant of the in-place allocator and the
|
|
290
|
+
* `enabled_count` partition hold regardless. */
|
|
269
291
|
private readonly _deterministic;
|
|
270
|
-
/** Whether the determinism surface is enabled
|
|
271
|
-
*
|
|
292
|
+
/** Whether the determinism surface is enabled. `false` ⇒ `stateHash`,
|
|
293
|
+
* `snapshotSparse` and `restoreSparse` throw `DETERMINISM_DISABLED`. */
|
|
272
294
|
get deterministic(): boolean;
|
|
273
295
|
/** Guard the canonical-ordering determinism surface. Throws
|
|
274
296
|
* `DETERMINISM_DISABLED` when determinism wasn't opted into, naming the
|
|
@@ -276,183 +298,167 @@ export declare class Store implements ObserverHost, QueryHost {
|
|
|
276
298
|
* (not `DEV`-gated): the surface is cold (never per-tick) so one boolean
|
|
277
299
|
* check is free, and a silent non-canonical digest is the failure mode we're
|
|
278
300
|
* preventing. */
|
|
279
|
-
private
|
|
280
|
-
/** Reject `f32`/`f64` fields on a `deterministic: true` world at registration
|
|
281
|
-
* (#777). IEEE-754 rounds differently across V8 / Bun / Zig at the 1-ULP
|
|
282
|
-
* level, so a float column in a fixed-update path is a silent per-tick
|
|
283
|
-
* `stateHash` divergence between client and server — the one thing the
|
|
284
|
-
* determinism opt-in (ADR-0020) exists to prevent. Non-deterministic worlds
|
|
285
|
-
* skip this entirely (floats stay allowed), so it costs the default path
|
|
286
|
-
* nothing. `kind` names the storage class in the error ("component" /
|
|
287
|
-
* "sparse component"); the array shorthand's `f64` default lands here too, so
|
|
288
|
-
* a deterministic world must pass an explicit integer type. */
|
|
289
|
-
private _rejectNonDeterministicFields;
|
|
301
|
+
private _assertDeterministic;
|
|
290
302
|
/** Rebuild the Int32Array views over the SAB entity-index region
|
|
291
|
-
* after a host-side SAB realloc (extend
|
|
292
|
-
* `_handleBufferResized
|
|
293
|
-
* callback fires so any downstream reader sees coherent views. */
|
|
303
|
+
* after a host-side SAB realloc (extend and grow). Called from
|
|
304
|
+
* `_handleBufferResized`, before the user-supplied `onBufferReplaced`
|
|
305
|
+
* callback fires, so any downstream reader sees coherent views. */
|
|
294
306
|
private _refreshEntityIndexViews;
|
|
295
|
-
/** Centralised "SAB was
|
|
296
|
-
* Int32Array views
|
|
307
|
+
/** Centralised "SAB was only reallocated" handler. Refreshes the
|
|
308
|
+
* Int32Array views first (so user callbacks observe valid views),
|
|
297
309
|
* then mirrors `entityHighWater` into the region's length header,
|
|
298
310
|
* then fires the user-supplied callback. */
|
|
299
311
|
private _handleBufferResized;
|
|
300
|
-
/** SAB backing every archetype's column views. Read-only handle
|
|
301
|
-
* live mutation happens through `
|
|
302
|
-
* Exposed for tests, snapshot
|
|
303
|
-
*
|
|
312
|
+
/** SAB backing every archetype's column views. Read-only handle. The
|
|
313
|
+
* live mutation happens through `_archGetOrCreateFromMask`.
|
|
314
|
+
* Exposed for tests, snapshot and restore, and `columnStoreStateHash`.
|
|
315
|
+
* Production reads of column data should
|
|
304
316
|
* still go through `Archetype.getColumnRead` (which sources from this
|
|
305
317
|
* SAB under the hood). */
|
|
306
318
|
get columnStore(): ColumnStore;
|
|
307
319
|
/** Resolve a consumer-declared SAB region's byte offset by `region_id`, or
|
|
308
320
|
* 0 when the region is absent (no region was declared with that id). The
|
|
309
|
-
* generic
|
|
310
|
-
*
|
|
311
|
-
*
|
|
312
|
-
*
|
|
321
|
+
* generic replacement for the removed game-named accessors, such as
|
|
322
|
+
* `terrain_view` and `spatial_grid_view`. A consumer pairs this with its own
|
|
323
|
+
* region module to materialise a typed view. It is the TS twin of the
|
|
324
|
+
* `abi.find_region` a WASM backend calls. */
|
|
313
325
|
regionOffset(regionId: number): number;
|
|
314
326
|
/** A handle to a consumer-declared SAB region resolved by `region_id`, or
|
|
315
|
-
* `null` when absent. Carries the live `buffer
|
|
327
|
+
* `null` when absent. Carries the live `buffer` and `view` plus the region's byte
|
|
316
328
|
* `offset` and `bytes`, so a consumer's region module can build a TypedArray
|
|
317
329
|
* view over exactly the region's span without re-reading the directory.
|
|
318
|
-
* Re-fetch after a SAB grow (the offset
|
|
330
|
+
* Re-fetch after a SAB grow (the offset and view may have moved). */
|
|
319
331
|
regionHandle(regionId: number): ColumnStoreRegionHandle | null;
|
|
320
332
|
/**
|
|
321
333
|
* Stamp every SAB-backed archetype's live `length` into its descriptor's
|
|
322
|
-
* `row_count` field
|
|
334
|
+
* `row_count` field. `extendColumnStore` and
|
|
323
335
|
* `growColumnStore` are the only other writers of `row_count`, and they
|
|
324
|
-
* record the count at the moment of the resize
|
|
336
|
+
* record the count at the moment of the resize, `Archetype.addEntity`
|
|
325
337
|
* does not update it, so any insertion after the most recent resize
|
|
326
|
-
* leaves the descriptor stale.
|
|
327
|
-
* off `
|
|
328
|
-
*
|
|
338
|
+
* leaves the descriptor stale. A compute backend that drives its per-row
|
|
339
|
+
* loop off the descriptor's `row_count` reads those stale bytes and
|
|
340
|
+
* silently skips the newly spawned rows.
|
|
329
341
|
*
|
|
330
342
|
* Lockstep walk: SAB descriptors are written by `extendColumnStore` in
|
|
331
343
|
* the order non-SAB archetypes are promoted, which is the same id-order
|
|
332
|
-
* those archetypes occupy in `this.
|
|
344
|
+
* those archetypes occupy in `this._archGraph.archetypes`. Iterating that array
|
|
333
345
|
* once, skipping non-SAB entries, and advancing an `archAddr` cursor
|
|
334
346
|
* by the descriptor's `column_count` lets us write `row_count` without
|
|
335
347
|
* the throwaway `Map<archId, length>` the previous version allocated
|
|
336
|
-
* on every call
|
|
348
|
+
* on every call. Cheap: descriptor-region seeks only, no column
|
|
337
349
|
* I/O.
|
|
338
350
|
*
|
|
339
|
-
* Gated by `_rowCountsDirty
|
|
340
|
-
* (`flushStructural`, `
|
|
341
|
-
* `addComponent(s)`, `removeComponent(s)`) set the flag
|
|
351
|
+
* Gated by `_rowCountsDirty`, mutation paths
|
|
352
|
+
* (`flushStructural`, `flushDestroys`, immediate `destroyEntity`,
|
|
353
|
+
* `addComponent(s)`, `removeComponent(s)`) set the flag. This method
|
|
342
354
|
* clears it. Read-only phases that flush only to drain empty buffers
|
|
343
355
|
* pay nothing. */
|
|
344
|
-
|
|
356
|
+
publishRowCounts(): void;
|
|
345
357
|
/** FNV-1a-style 32-bit digest over (archetype_id, live_row_count, live
|
|
346
358
|
* column bytes) for each archetype in id order, followed by the sparse
|
|
347
|
-
* stores (out-of-identity components
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
* for cross-replay determinism.
|
|
359
|
+
* stores (out-of-identity components) in registration order.
|
|
360
|
+
* This is the canonical "live ECS state digest" for cross-replay
|
|
361
|
+
* determinism. It replaces the earlier per-networked-component fold.
|
|
351
362
|
*
|
|
352
|
-
* **Sparse coverage
|
|
353
|
-
* graph, so it is folded separately after the archetype loop
|
|
363
|
+
* **Sparse coverage.** Sparse data lives outside the archetype
|
|
364
|
+
* graph, so it is folded separately after the archetype loop, per store:
|
|
354
365
|
* the sparse-component id, the member count, then each member's source
|
|
355
|
-
* entity index + f64 field words, walked in
|
|
366
|
+
* entity index + f64 field words, walked in canonical ascending-index order
|
|
356
367
|
* (`SparseComponentStore.canonicalIndices`). Canonical order is what makes
|
|
357
368
|
* the digest insertion-order-independent: two worlds with identical sparse
|
|
358
|
-
* contents built by different add
|
|
369
|
+
* contents built by different add and remove sequences agree. Keyed by entity
|
|
359
370
|
* index, and destruction purges the slot, so a recycled index never carries
|
|
360
371
|
* a stale occupant's data into the hash.
|
|
361
372
|
*
|
|
362
373
|
* It is strictly broader than the prior per-networked-component fold
|
|
363
|
-
* (covers every column, not
|
|
374
|
+
* (covers every column, not only a hand-picked subset of networked
|
|
364
375
|
* components), and strictly tighter than `columnStoreStateHash(...)`
|
|
365
376
|
* which scans the full SAB including trailing unused capacity.
|
|
366
377
|
*
|
|
367
|
-
* **Per-word fold
|
|
368
|
-
* word at a time using FNV-1a's `xor + imul(PRIME)` step. This is
|
|
369
|
-
* byte-for-byte FNV-1a-32 of the column bytes
|
|
370
|
-
* digest with the same equality semantics,
|
|
371
|
-
* byte loop it replaces. Trailing 0
|
|
372
|
-
* u8
|
|
378
|
+
* **Per-word fold.** The inner column loop folds one 32-bit
|
|
379
|
+
* word at a time using FNV-1a's `xor + imul(PRIME)` step. This is not
|
|
380
|
+
* byte-for-byte FNV-1a-32 of the column bytes. It's a deterministic
|
|
381
|
+
* digest with the same equality semantics, and much quicker than the
|
|
382
|
+
* per-byte loop it replaces. Trailing 0 to 3 tail bytes (only possible for
|
|
383
|
+
* u8 or u16 columns at odd row counts) are folded together as a single
|
|
373
384
|
* little-endian word so the algorithm stays branch-free in the inner
|
|
374
385
|
* loop. The 4-byte `id` and `len` headers are folded as words for the
|
|
375
386
|
* same reason. Byte order is little-endian to match the platform's
|
|
376
|
-
* native TypedArray layout
|
|
387
|
+
* native TypedArray layout. The digest is opaque (no consumer compares
|
|
377
388
|
* against a literal value), so endianness is an implementation detail
|
|
378
389
|
* rather than wire contract.
|
|
379
390
|
*
|
|
380
391
|
* Determinism: same store ⇒ same digest within a process, and across
|
|
381
|
-
* processes on the same architecture
|
|
382
|
-
* needs
|
|
392
|
+
* processes on the same architecture, which is all a lockstep replay
|
|
393
|
+
* needs, because both replays run the same algorithm on the same words.
|
|
383
394
|
*
|
|
384
|
-
* **Opt-in
|
|
395
|
+
* **Opt-in.** Throws `DETERMINISM_DISABLED` unless the
|
|
385
396
|
* Store was constructed with `{ deterministic: true }`. The canonical
|
|
386
397
|
* ordering this fold relies on (sparse `canonicalIndices`, sorted relation
|
|
387
398
|
* target sets) is the determinism tax the flag gates. */
|
|
388
399
|
stateHash(): number;
|
|
389
|
-
private
|
|
390
|
-
/** Look up the `EntityID` at `row` in archetype `archetype_id`.
|
|
391
|
-
*
|
|
392
|
-
* `
|
|
393
|
-
* `(
|
|
394
|
-
* and TS bridges it back through `ctx.emit(...)` via this method. (#250 /
|
|
395
|
-
* Phase 4 PR 4D)
|
|
400
|
+
private _archGet;
|
|
401
|
+
/** Look up the `EntityID` at `row` in archetype `archetype_id`. A compute
|
|
402
|
+
* backend resolves an `EntityID` from an event-ring payload through it. The
|
|
403
|
+
* backend writes `(archId, row, …)` to the event ring, and TS bridges it
|
|
404
|
+
* back through `ctx.emit(...)` via this method.
|
|
396
405
|
*
|
|
397
406
|
* Throws `ECSError` if `archetype_id` is out of range or `row` is
|
|
398
|
-
* past the archetype's live row count
|
|
399
|
-
* ring
|
|
400
|
-
*
|
|
401
|
-
* surface. */
|
|
407
|
+
* past the archetype's live row count. Either one means a corrupt
|
|
408
|
+
* ring payload or a stale row index (an extend or a grow happened
|
|
409
|
+
* mid-tick), and both are defects. */
|
|
402
410
|
entityIdAtRow(archetypeId: number, row: number): EntityID;
|
|
403
|
-
/** Find or create an archetype for the given component mask
|
|
411
|
+
/** Find or create an archetype for the given component mask, see
|
|
404
412
|
* `ArchetypeGraph.getOrCreateFromMask`. */
|
|
405
|
-
private
|
|
406
|
-
/** Bulk variant of `
|
|
407
|
-
* call for the whole batch (
|
|
408
|
-
* `ArchetypeGraph.
|
|
413
|
+
private _archGetOrCreateFromMask;
|
|
414
|
+
/** Bulk variant of `_archGetOrCreateFromMask`, one `extendColumnStore`
|
|
415
|
+
* call for the whole batch (the prewarm pass). See
|
|
416
|
+
* `ArchetypeGraph.getOrCreateFromMasks`. */
|
|
409
417
|
archCreateManyFromMasks(masks: readonly BitSet[]): ArchetypeID[];
|
|
410
|
-
/**
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
+
/** Call `extendColumnStore` once with `newSpecs`, then refresh every
|
|
419
|
+
* pre-existing SAB-backed Archetype's TypedArray views when the extend had
|
|
420
|
+
* to realloc. The `existing` row counts are built only when that realloc
|
|
421
|
+
* path runs: the in-place path moves no rows and reads no list, and to walk
|
|
422
|
+
* every archetype on every extend made the cost of the N-th archetype grow
|
|
423
|
+
* with N. The bulk variant makes one extend for many archetypes, so the
|
|
424
|
+
* list is built at most one time for them all. */
|
|
425
|
+
private _extendStore;
|
|
426
|
+
/** Materialise the `Archetype` object for a freshly-minted graph node,
|
|
427
|
+
* binds the graph's topology to this store's column backing and grow
|
|
418
428
|
* handler (`ArchetypeGraphHost.materialize`). Store-owned so the graph
|
|
419
429
|
* never touches `_columnStore`. */
|
|
420
430
|
private _materializeArchetype;
|
|
421
|
-
/** Push a newly-installed archetype into every registered query whose masks
|
|
422
|
-
* it satisfies (`ArchetypeGraphHost.fanIntoQueries`; the query registry
|
|
423
|
-
* stays on Store). No epoch bump — see the note in `ArchetypeGraph.install`. */
|
|
424
|
-
private _fanIntoQueries;
|
|
425
431
|
/** Resolve "add component_id to archetype_id" → target ArchetypeID (edge-cached). */
|
|
426
|
-
private
|
|
432
|
+
private _archResolveAdd;
|
|
427
433
|
/** Resolve "remove component_id from archetype_id" → target ArchetypeID (edge-cached). */
|
|
428
|
-
private
|
|
434
|
+
private _archResolveRemove;
|
|
429
435
|
createEntity(): EntityID;
|
|
430
|
-
/** Allocate an entity slot
|
|
431
|
-
* the template spawn paths. Returns the packed `EntityID
|
|
432
|
-
* is left in `
|
|
436
|
+
/** Allocate an entity slot without placing it in the empty archetype, for
|
|
437
|
+
* the template spawn paths. Returns the packed `EntityID`. The slot index
|
|
438
|
+
* is left in `_entityAllocator.lastIndex`. Skips the empty-archetype
|
|
433
439
|
* membership write `createEntity` performs (the caller installs the real
|
|
434
440
|
* archetype + row). This *commits* the slot (bumps counts, stamps the
|
|
435
|
-
* generation so `isAlive` is already true), so the caller
|
|
441
|
+
* generation so `isAlive` is already true), so the caller must have
|
|
436
442
|
* reserved the column capacity for the row first
|
|
437
|
-
* (`Archetype.
|
|
438
|
-
* subsequent append leaves the slot phantom-alive
|
|
443
|
+
* (`Archetype.reserveRows`), otherwise a cap throw from the
|
|
444
|
+
* subsequent append leaves the slot phantom-alive. */
|
|
439
445
|
private _allocEntity;
|
|
440
446
|
/** Pre-check that `count` fresh entity slots can be allocated without
|
|
441
|
-
* exhausting the entity-index space, so `spawnMany` commits all-or-nothing
|
|
442
|
-
*
|
|
447
|
+
* exhausting the entity-index space, so `spawnMany` commits all-or-nothing.
|
|
448
|
+
* `_allocEntity`'s own per-call high-water guard would otherwise
|
|
443
449
|
* throw `EID_MAX_INDEX_OVERFLOW` partway through the alloc loop, leaving the
|
|
444
450
|
* slots it already committed phantom-alive. Free-list reuse covers the first
|
|
445
|
-
* `entityFreeIndices.length` slots
|
|
451
|
+
* `entityFreeIndices.length` slots. Only the remainder draws down the
|
|
446
452
|
* high-water headroom. */
|
|
447
|
-
private
|
|
448
|
-
/**
|
|
453
|
+
private _assertEntityIndexCapacity;
|
|
454
|
+
/** Build a template: compute the target archetype (creating it if absent,
|
|
449
455
|
* fits the prewarm model), pre-flatten default field values into
|
|
450
456
|
* `_flatColumns` order, and build the override index (field name → flat
|
|
451
|
-
* column index
|
|
457
|
+
* column index. `TEMPLATE_OVERRIDE_AMBIGUOUS` for a name shared by more than
|
|
452
458
|
* one component, which a flat override cannot target). */
|
|
453
|
-
|
|
459
|
+
createTemplate(entries: readonly TemplateEntryData[]): Template;
|
|
454
460
|
/** Resolve an override key to its flat column index, with the DEV guards
|
|
455
|
-
* for unknown and ambiguous field names
|
|
461
|
+
* for unknown and ambiguous field names. `-1` means skip (the production
|
|
456
462
|
* fallback where DEV would have thrown). */
|
|
457
463
|
private _resolveOverrideColumn;
|
|
458
464
|
/** Apply per-instance overrides to the freshly-spawned row. Each key is a
|
|
@@ -466,20 +472,20 @@ export declare class Store implements ObserverHost, QueryHost {
|
|
|
466
472
|
* applies any per-instance overrides. */
|
|
467
473
|
spawn(p: Template, overrides?: Record<string, number | undefined>): EntityID;
|
|
468
474
|
/** Bulk-spawn `count` identical entities into the template's archetype. The
|
|
469
|
-
* field writes are O(columns)
|
|
470
|
-
* `addEntitiesWithValues
|
|
475
|
+
* field writes are O(columns), one `TypedArray.fill` per column via
|
|
476
|
+
* `addEntitiesWithValues`, not O(count×columns). Returns the new ids in
|
|
471
477
|
* spawn order. */
|
|
472
478
|
spawnMany(p: Template, count: number, overrides?: Record<string, number | undefined>): EntityID[];
|
|
473
479
|
/** Immediately destroy an entity, removing it from its archetype.
|
|
474
480
|
*
|
|
475
|
-
* With no `delete
|
|
476
|
-
* case) this tears the one entity down and returns
|
|
481
|
+
* With no `delete` or `clear` target-cleanup policy registered (the common
|
|
482
|
+
* case) this tears the one entity down and returns, no allocation. When a
|
|
477
483
|
* policy is in play, a `delete`-target's sources are appended to a local
|
|
478
|
-
* work-list this method then drains in the same iterative pass
|
|
484
|
+
* work-list this method then drains in the same iterative pass:
|
|
479
485
|
* the `work.length` re-read drives chains and trees out without recursion, so
|
|
480
486
|
* depth is bounded by entity count, not tree depth. This mirrors the deferred
|
|
481
|
-
* `
|
|
482
|
-
* identical end state
|
|
487
|
+
* `flushDestroys` buffer mechanism, both paths are iterative and reach the
|
|
488
|
+
* identical end state. The only difference is the shared `pendingDestroy`
|
|
483
489
|
* buffer there vs. a local work-list here. `isAlive` dedups a source reached
|
|
484
490
|
* twice (diamonds) and terminates cycles, exactly as the generation guard does
|
|
485
491
|
* in the deferred loop. */
|
|
@@ -488,71 +494,99 @@ export declare class Store implements ObserverHost, QueryHost {
|
|
|
488
494
|
* then recycle (or retire) its slot. Shared by both immediate-destroy entry
|
|
489
495
|
* points (the fast no-cascade path and the work-list driver in
|
|
490
496
|
* `destroyEntity`). When `cascade` is non-null, a `delete`-policy target's
|
|
491
|
-
* surviving sources are appended to it for the driver to drain
|
|
497
|
+
* surviving sources are appended to it for the driver to drain.
|
|
492
498
|
* `null` skips that collection for callers that cannot cascade. The caller
|
|
493
499
|
* must have already confirmed `id` is alive. */
|
|
494
500
|
private _destroyOne;
|
|
495
501
|
/**
|
|
496
|
-
* Liveness check, **fail-closed** against forged
|
|
497
|
-
* handles
|
|
498
|
-
* serialization,
|
|
502
|
+
* Liveness check, **fail-closed** against a forged, retired or out-of-bounds
|
|
503
|
+
* handles. For a general-purpose engine that may receive a handle from
|
|
504
|
+
* serialization, ipc, or any untrusted caller, three malformed inputs must read
|
|
499
505
|
* dead rather than alias a slot:
|
|
500
|
-
* - **Out of range
|
|
506
|
+
* - **Out of range**, an `id` outside the 31-bit packed space (`< 0` or
|
|
501
507
|
* `> MAX_ENTITY_ID`). Without this, the 20-bit index mask below silently
|
|
502
|
-
* folds garbage high bits onto a valid slot. (
|
|
503
|
-
* postMessage decode
|
|
504
|
-
* - **Tombstone generation
|
|
508
|
+
* folds garbage high bits onto a valid slot. (The snapshot decode and the
|
|
509
|
+
* `postMessage` decode apply the same bound.)
|
|
510
|
+
* - **Tombstone generation**, a handle carrying `RETIRED_GENERATION`, which
|
|
505
511
|
* the allocator stamps into a retired slot and never issues to a live
|
|
506
512
|
* entity, would otherwise match a retired slot's parked generation and read
|
|
507
513
|
* alive (the ABA tombstone, previously documented as a known gap).
|
|
508
514
|
* Both guards are comparisons predicted not-taken on the live path, so a
|
|
509
|
-
* well-formed handle pays two branches and nothing else
|
|
515
|
+
* well-formed handle pays two branches and nothing else.
|
|
510
516
|
*/
|
|
511
517
|
isAlive(id: EntityID): boolean;
|
|
518
|
+
/**
|
|
519
|
+
* Liveness and the packed index in one result: the entity index if `id` is
|
|
520
|
+
* live, else `-1`. Same three fail-closed guards as `isAlive` (documented
|
|
521
|
+
* above), and the sole implementation of them, `isAlive` is a comparison on
|
|
522
|
+
* top of this.
|
|
523
|
+
*
|
|
524
|
+
* Why it returns the index instead of a boolean: every by-id caller needs
|
|
525
|
+
* both answers, and the pair used to cost two derivations of the same index.
|
|
526
|
+
* `hasComponent` called `isAlive(id)`, which computed `getEntityIndex(id)`
|
|
527
|
+
* internally, and then computed `getEntityIndex(id)` again to reach
|
|
528
|
+
* `_entityArchetypes`. The generational check has already touched the index. A
|
|
529
|
+
* caller that is about to index a parallel array with it should be handed the
|
|
530
|
+
* one that was computed, not re-derive it. The generations read is inlined
|
|
531
|
+
* here for the same reason, rather than delegated to
|
|
532
|
+
* `_entityAllocator.isAliveIndex`, which stays as the index-domain entry point
|
|
533
|
+
* for callers that already hold an index.
|
|
534
|
+
*
|
|
535
|
+
* **This gives much less than it appears to give.** The estimate for this
|
|
536
|
+
* change was a large decrease. The measurement shows that `has` and `isAlive`
|
|
537
|
+
* are only a little faster. The decrease is real, but it is much smaller than
|
|
538
|
+
* the estimate. The work that we removed was truly not necessary. But V8
|
|
539
|
+
* already made both one-line functions inline, and it already removed most of
|
|
540
|
+
* the duplicated calculation. Therefore "the work is plainly not necessary" is
|
|
541
|
+
* not an argument about performance for a jit compiler. This note stops the
|
|
542
|
+
* next reader from making the same estimate again.
|
|
543
|
+
*/
|
|
544
|
+
private _liveIndex;
|
|
512
545
|
get entityCount(): number;
|
|
513
546
|
/** An archetype's row count moved from `preLen` to its current
|
|
514
547
|
* `arch.length` on a **shrink** (rows removed: the source of a transition, a
|
|
515
|
-
* destroy, a batch-source drain). Always marks SAB row counts dirty
|
|
516
|
-
* the descriptor walk
|
|
517
|
-
* epoch
|
|
518
|
-
* `Query._nonEmptyArchetypes` can change on a shrink
|
|
548
|
+
* destroy, a batch-source drain). Always marks SAB row counts dirty
|
|
549
|
+
* (the descriptor walk only needs "something moved"). It bumps the
|
|
550
|
+
* query-dirty epoch only when `length` crosses zero, the only case where
|
|
551
|
+
* `Query._nonEmptyArchetypes` can change on a shrink. Mutations that
|
|
519
552
|
* move row counts within the same side (6→5) leave the non-empty set unchanged
|
|
520
553
|
* and skip the bump.
|
|
521
554
|
*
|
|
522
|
-
* A shrink does **not** need the `enabledCount` crossing test
|
|
523
|
-
*
|
|
524
|
-
* archetype that keeps disabled rows
|
|
525
|
-
* query's non-empty list as a harmless stale *inclusion
|
|
526
|
-
* bound on `enabledCount` (now 0) iterate it zero times.
|
|
555
|
+
* A shrink does **not** need the `enabledCount` crossing test. The only
|
|
556
|
+
* enabled-count move it can make is 1→0, when the last enabled row leaves an
|
|
557
|
+
* archetype that keeps disabled rows. That leaves the archetype in a default
|
|
558
|
+
* query's non-empty list as a harmless stale *inclusion*. `count` and
|
|
559
|
+
* `forEach` bound on `enabledCount` (now 0), so they iterate it zero times.
|
|
560
|
+
* Only a **grow** into
|
|
527
561
|
* an all-disabled archetype can stale-*exclude* a live row, so the enabled
|
|
528
562
|
* crossing lives in `_onArchGrow`, off this path.
|
|
529
563
|
*
|
|
530
|
-
*
|
|
531
|
-
* once or twice
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
* gate to skip the bump for no-query workloads
|
|
535
|
-
*
|
|
536
|
-
*
|
|
537
|
-
*
|
|
538
|
-
*
|
|
539
|
-
private
|
|
540
|
-
/** An archetype **grew
|
|
541
|
-
* spawn, a batch-target fill). Like `
|
|
564
|
+
* Inlining-sensitive, keep the body tiny. Each immediate-mode `addComponent`
|
|
565
|
+
* or `removeComponent` calls it once or twice. The mutation hot path depends
|
|
566
|
+
* on the inline at every call site.
|
|
567
|
+
* An earlier change added an `if (_registeredQueries.length === 0) return;`
|
|
568
|
+
* gate to skip the bump for no-query workloads. The mutation churn loop got
|
|
569
|
+
* far slower, because the extra statement pushed the function past V8's
|
|
570
|
+
* per-call inlining budget. The gate is no longer in the code. Measure the
|
|
571
|
+
* mutation hot path before you merge a change here. Code review alone is
|
|
572
|
+
* not sufficient. */
|
|
573
|
+
private _onArchShrink;
|
|
574
|
+
/** An archetype **grew**, rows were appended (the target of a transition, a
|
|
575
|
+
* spawn, a batch-target fill). Like `_onArchShrink` it marks row counts
|
|
542
576
|
* dirty and bumps the query-dirty epoch on a `length` 0/non-zero crossing
|
|
543
577
|
* (`includeDisabled` membership), but it *also* bumps on an `enabledCount`
|
|
544
|
-
* 0→1 crossing
|
|
578
|
+
* 0→1 crossing. The non-empty filter is field-split: a default
|
|
545
579
|
* query keeps archetypes with `enabledCount > 0`. An enabled row appended to
|
|
546
580
|
* an archetype that is non-empty but all-disabled (`length > 0,
|
|
547
581
|
* enabledCount == 0`) crosses `enabledCount` 0→1 without touching `length`,
|
|
548
|
-
* so the `preLen` test alone (the valid
|
|
549
|
-
*
|
|
550
|
-
* `
|
|
582
|
+
* so the `preLen` test alone (the earlier proxy, valid only while
|
|
583
|
+
* `enabledCount === length`) misses it and a cached default query keeps a stale
|
|
584
|
+
* `nonEmptyArchs` list. Only grows can do this, so only grow sites carry the test.
|
|
551
585
|
*
|
|
552
586
|
* **Precondition: ≥1 row was appended** (every caller adds at least one row),
|
|
553
|
-
* so `arch.length > 0` afterward
|
|
554
|
-
* body stays inlinable (the inlining caveat on `
|
|
555
|
-
* here too
|
|
587
|
+
* so `arch.length > 0` afterward, which is why the crossings simplify and the
|
|
588
|
+
* body stays inlinable (the inlining caveat on `_onArchShrink` applies
|
|
589
|
+
* here too). The general
|
|
556
590
|
* `(pre === 0) !== (post === 0)` boundary test collapses given the post side:
|
|
557
591
|
* - `length`: post > 0 always ⇒ a crossing iff `preLen === 0`.
|
|
558
592
|
* - `enabledCount`: non-decreasing on a grow ⇒ a 0-crossing iff it was 0
|
|
@@ -561,17 +595,17 @@ export declare class Store implements ObserverHost, QueryHost {
|
|
|
561
595
|
* read is short-circuited away on the hot path (`preLen` or `preEnabled`
|
|
562
596
|
* non-zero), so a no-disabled workload pays only two scalar compares. */
|
|
563
597
|
private _onArchGrow;
|
|
564
|
-
/** Dirty bookkeeping for an enable
|
|
565
|
-
* unchanged (no row added
|
|
566
|
-
* row counts
|
|
567
|
-
* snapshot see the new partition
|
|
568
|
-
* *enabled* count crossed 0
|
|
569
|
-
* a query's non-empty set
|
|
570
|
-
* is now `enabled_count
|
|
598
|
+
/** Dirty bookkeeping for an enable and disable toggle. `length` is
|
|
599
|
+
* unchanged (no row added or removed) but `enabled_count` moved, so: republish
|
|
600
|
+
* row counts, because the descriptor's `enabled_count` changed and a compute
|
|
601
|
+
* backend and the snapshot must see the new partition. Bump the query epoch only when
|
|
602
|
+
* the *enabled* count crossed 0. That is the boundary at which an archetype
|
|
603
|
+
* enters and leaves a query's non-empty set, because `Query.nonEmptyArchs`
|
|
604
|
+
* filters on `entityCount`, which is now `enabled_count`. */
|
|
571
605
|
private _onArchEnabledChange;
|
|
572
606
|
/** Immediately disable an entity (idempotent). The entity must hold at least
|
|
573
|
-
* one component
|
|
574
|
-
* cannot be partitioned (a `DEV` error
|
|
607
|
+
* one component, a component-less entity occupies no archetype row, so it
|
|
608
|
+
* cannot be partitioned (a `DEV` error, prod no-op). */
|
|
575
609
|
disableEntity(id: EntityID): void;
|
|
576
610
|
/** Immediately enable an entity (idempotent). */
|
|
577
611
|
enableEntity(id: EntityID): void;
|
|
@@ -579,250 +613,306 @@ export declare class Store implements ObserverHost, QueryHost {
|
|
|
579
613
|
* disabled (it has no row to partition). */
|
|
580
614
|
isDisabled(id: EntityID): boolean;
|
|
581
615
|
/** 0-crossing detection for the per-entity flush paths (`_flushAdds`,
|
|
582
|
-
* `_flushRemoves`) without per-entity Map traffic
|
|
583
|
-
* destroy drain
|
|
616
|
+
* `_flushRemoves`) without per-entity Map traffic, the same cost the
|
|
617
|
+
* destroy drain also avoids. Each touched archetype is stamped with the
|
|
584
618
|
* current flush epoch (`Archetype._flushSeenEpoch`), its pre-length and
|
|
585
|
-
* pre-enabled-count recorded on first sight (`_flushPreLen`
|
|
586
|
-
* `_flushPreEnabled`), and pushed onto this scratch list
|
|
619
|
+
* pre-enabled-count recorded on first sight (`_flushPreLen` and
|
|
620
|
+
* `_flushPreEnabled`), and pushed onto this scratch list
|
|
587
621
|
* `_settleFlushDirty` walks the list once after the loop. The field
|
|
588
622
|
* accesses per entity replace a `Map.has` + `Map.set` hash probe pair. The
|
|
589
623
|
* epoch is bumped at settle so the next flush re-records. */
|
|
590
624
|
private _flushEpoch;
|
|
591
625
|
private readonly _flushTouched;
|
|
592
626
|
/** Resolve dirty flags for a per-entity batch flush from the captured
|
|
593
|
-
* pre-counts. Marks row counts dirty if any archetype was touched
|
|
627
|
+
* pre-counts. Marks row counts dirty if any archetype was touched. Bumps
|
|
594
628
|
* the query epoch once if any touched archetype crossed the 0 boundary on
|
|
595
629
|
* *either* `length` (includeDisabled membership) or `enabledCount`
|
|
596
|
-
* (default-query membership)
|
|
597
|
-
* `
|
|
630
|
+
* (default-query membership), the deferred analog of the immediate
|
|
631
|
+
* `_onArchShrink` two-field check. A single bump is sufficient
|
|
598
632
|
* (queries only need to know "something changed"). Clears the touched list
|
|
599
633
|
* and advances the flush epoch on exit. */
|
|
600
634
|
private _settleFlushDirty;
|
|
601
635
|
destroyEntityDeferred(id: EntityID): void;
|
|
602
|
-
/** Buffer an enable
|
|
636
|
+
/** Buffer an enable and disable toggle for the phase flush. The row swap a
|
|
603
637
|
* toggle performs would corrupt a `forEach` over that archetype if applied
|
|
604
|
-
* mid-system, so it is deferred like add
|
|
638
|
+
* mid-system, so it is deferred like an add and a remove. */
|
|
605
639
|
disableEntityDeferred(id: EntityID): void;
|
|
606
640
|
enableEntityDeferred(id: EntityID): void;
|
|
607
|
-
/** Drain buffered enable
|
|
641
|
+
/** Drain buffered enable and disable toggles, applying each in operation order via
|
|
608
642
|
* the immediate path (which is idempotent and updates dirty flags). Called at
|
|
609
|
-
* the flush boundary after structural adds
|
|
643
|
+
* the flush boundary after structural adds and removes settle, so a toggle sees the
|
|
610
644
|
* entity's final archetype placement for the tick.
|
|
611
645
|
*
|
|
612
|
-
* When an onDisable
|
|
613
|
-
* 0
|
|
646
|
+
* When an onDisable and onEnable observer is registered (`_toggleObserverCount >
|
|
647
|
+
* 0`) this also collects effective toggle events into `_obsEvents` for
|
|
614
648
|
* the dispatch hook, collapsed to one event per *net* transition across the
|
|
615
649
|
* drain (see `_toggleInitial`). The no-observer path is byte-for-byte the
|
|
616
|
-
*
|
|
650
|
+
* earlier drain. */
|
|
617
651
|
private _flushToggles;
|
|
618
|
-
/** Fan one entity's net toggle transition out to an onDisable
|
|
619
|
-
* per carried component
|
|
620
|
-
* matching pre-bound bit visitor
|
|
652
|
+
/** Fan one entity's net toggle transition out to an onDisable and onEnable event
|
|
653
|
+
* per carried component. Walks the entity's archetype mask through the
|
|
654
|
+
* matching pre-bound bit visitor. A component-less entity (no row) carries
|
|
621
655
|
* nothing and is skipped. */
|
|
622
656
|
private _collectToggle;
|
|
623
657
|
get pendingToggleCount(): number;
|
|
624
658
|
/** Flush all buffered entity destructions in batch.
|
|
625
659
|
*
|
|
626
660
|
* When onRemove observers are registered (`_structuralObserverCount > 0`),
|
|
627
|
-
* a destroy fires onRemove for every component the entity carried
|
|
628
|
-
* *is* a remove of the whole mask
|
|
661
|
+
* a destroy fires onRemove for every component the entity carried, a destroy
|
|
662
|
+
* *is* a remove of the whole mask, collected here and dispatched by the
|
|
629
663
|
* `flushStructural` fixed-point loop, the only caller in that mode (it drains
|
|
630
664
|
* `pendingDestroy` each round so the trailing `ctx.flush()` call is a no-op).
|
|
631
665
|
* Same commit-then-observe discipline as `_flushRemoves`: the entity is fully
|
|
632
666
|
* freed before the callback runs, so onRemove receives the (now dead) eid as
|
|
633
667
|
* the identity of what was destroyed, not a live handle to read. The
|
|
634
|
-
* no-observer path is byte-for-byte unchanged (`collecting` gate).
|
|
668
|
+
* no-observer path is byte-for-byte unchanged (`collecting` gate).
|
|
635
669
|
*
|
|
636
670
|
* Re-entrancy: while the observed fixed point owns the flush, the loop
|
|
637
|
-
* drains destroys itself via `
|
|
671
|
+
* drains destroys itself via `_flushDestroys`, so a re-entrant
|
|
638
672
|
* `ctx.flush()` from a callback no-ops (the guard lives in
|
|
639
|
-
* `DeferredCommandBuffer.
|
|
673
|
+
* `DeferredCommandBuffer.flushDestroys`), otherwise it would collect
|
|
640
674
|
* into the shared `_obsEvents` scratch mid-dispatch and corrupt it. */
|
|
641
|
-
|
|
642
|
-
private
|
|
675
|
+
flushDestroys(): void;
|
|
676
|
+
private _flushDestroys;
|
|
643
677
|
get pendingDestroyCount(): number;
|
|
644
678
|
addComponentDeferred(entityId: EntityID, def: ComponentDef<Record<string, never>>): void;
|
|
645
679
|
addComponentDeferred<S extends ComponentSchema>(entityId: EntityID, def: ComponentDef<S>, values: FieldValues<S>): void;
|
|
646
680
|
removeComponentDeferred(entityId: EntityID, def: ComponentDef): void;
|
|
647
|
-
/** Phase-boundary structural flush. The drain policy
|
|
648
|
-
* path, observed fixed point (adds
|
|
649
|
-
* convergence guard, re-entrancy
|
|
650
|
-
*
|
|
651
|
-
* `
|
|
681
|
+
/** Phase-boundary structural flush. The drain policy, no-observer fast
|
|
682
|
+
* path, observed fixed point (adds and removes → destroys → toggles),
|
|
683
|
+
* convergence guard, re-entrancy, lives in `DeferredCommandBuffer`.
|
|
684
|
+
* The batch appliers it drives are the `_flush*` and
|
|
685
|
+
* `_flushDestroys` methods below. */
|
|
652
686
|
flushStructural(): void;
|
|
653
687
|
/** Batch-apply all deferred component additions. */
|
|
654
688
|
private _flushAdds;
|
|
655
689
|
/** Batch-apply all deferred component removals. */
|
|
656
690
|
private _flushRemoves;
|
|
657
691
|
get pendingStructuralCount(): number;
|
|
658
|
-
/**
|
|
659
|
-
*
|
|
660
|
-
*
|
|
692
|
+
/** Record what one consumer of the change feed asks the store to collect
|
|
693
|
+
* for `cid`, then apply the OR of every consumer's ask.
|
|
694
|
+
*
|
|
695
|
+
* `consumer` is the plugin name. The observer registry passes
|
|
696
|
+
* `"observers"`. One consumer dropping a flag never takes that flag away
|
|
697
|
+
* from another, which is why the merge sits in front of the transition
|
|
698
|
+
* logic. All-false is the same as absent. Cold path: a consumer calls this
|
|
699
|
+
* when it gains or loses an observer. */
|
|
700
|
+
configureObservation(consumer: string, cid: number, flags: ObservationFlags): void;
|
|
701
|
+
/** Set the per-component observation flags from the merged ask of every
|
|
702
|
+
* consumer for `cid`. Maintains `_structuralObserverCount` and
|
|
703
|
+
* `_toggleObserverCount` (the fast-path gates) and lazily allocates the
|
|
661
704
|
* dirty list when per-entity onSet tracking turns on. */
|
|
662
|
-
|
|
663
|
-
/**
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
*
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
*
|
|
677
|
-
|
|
705
|
+
private _applyObservation;
|
|
706
|
+
/** Give `cid` a row tick plane: one change tick for each row of every
|
|
707
|
+
* archetype that holds it, stamped by every write path. The row grain of
|
|
708
|
+
* change detection. An entity-level onSet observer calls this. Idempotent,
|
|
709
|
+
* and never undone. Cold. */
|
|
710
|
+
trackRows(def: ComponentHandle | SparseComponentDef): void;
|
|
711
|
+
/** Record what one consumer of the change feed asks the store to collect
|
|
712
|
+
* for sparse component `sid`, then apply the OR of every consumer's ask.
|
|
713
|
+
* Same merge rule as the dense form. Cold path. */
|
|
714
|
+
configureSparseObservation(consumer: string, sid: number, hasSet: boolean): void;
|
|
715
|
+
/** Row ticks on for sparse component `sid`, or the pending records dropped
|
|
716
|
+
* when the last consumer of its change feed leaves. */
|
|
717
|
+
private _applySparseObservation;
|
|
718
|
+
/** Collect the members of sparse component `sid` recorded since the last
|
|
719
|
+
* drain, for the entity-level onSet dispatch. `run` is the change tick of
|
|
720
|
+
* the dispatch. A member is alive by construction, because a destroy purges
|
|
721
|
+
* it, and the walk skips a disabled one, which default queries hide. The
|
|
722
|
+
* component-level change tick gates the walk, so an idle component costs
|
|
723
|
+
* one compare. Member order. The result array is reused. */
|
|
724
|
+
drainSparseSet(sid: number, run: number): EntityID[];
|
|
725
|
+
/** The row tick of `entityId`'s sparse component, 0 for a non-member, for
|
|
726
|
+
* `ctx.sparseChanged`. Throws when the component keeps no row ticks. */
|
|
727
|
+
sparseTickOf(def: SparseComponentDef, entityId: EntityID): number;
|
|
728
|
+
/** The store a mutable sparse cursor stamps through on each `at()`
|
|
729
|
+
* (ref.ts `createSparseCursor`). */
|
|
730
|
+
sparseTickPlane(def: SparseComponentDef): SparseComponentStore;
|
|
731
|
+
/** `trackRows` by component id, for the observer registry, which holds
|
|
732
|
+
* the id alone. */
|
|
733
|
+
private _trackRowsById;
|
|
734
|
+
/** The list length past which a frame switches to the scan. A list entry
|
|
735
|
+
* costs a push, three checks and a sort slot. A scan costs one compare per
|
|
736
|
+
* row of each stamped archetype, and those rows are at most the live
|
|
737
|
+
* entities, so a cap of a fraction of the live count keeps the list cost
|
|
738
|
+
* paid below the scan cost that replaces it, whatever the world's size. */
|
|
739
|
+
private _listCap;
|
|
740
|
+
/** Record a row for the entity-level onSet of `cid`, from a path that has
|
|
741
|
+
* resolved the archetype and the row (`setField`, `ref`, a cursor). Gated
|
|
742
|
+
* by the caller on `anyDirtyTracked`. Stamps the row tick, and pushes the
|
|
743
|
+
* entity onto the dirty list when the row's previous stamp lay at or below
|
|
744
|
+
* the last drain: the row is then new to the list. Hot path on the by-id
|
|
745
|
+
* writes of a tracked component: two loads, one compare, one store. */
|
|
746
|
+
noteSet(cid: number, arch: Archetype, row: number, eid: EntityID): void;
|
|
747
|
+
/** `noteSet` for a caller that holds the entity alone (`ctx.markChanged`).
|
|
748
|
+
* A dead entity, and one that does not hold the component, record nothing. */
|
|
749
|
+
noteSetEntity(def: ComponentHandle, eid: EntityID): void;
|
|
750
|
+
/** A chunk loop took the row tick column of `cid` (`cols.ticks`). Its
|
|
751
|
+
* stores make no list entry, so the next drain scans the plane. */
|
|
752
|
+
noteScan(cid: number): void;
|
|
753
|
+
/** Collect the rows recorded for `cid` since the last drain, for the
|
|
754
|
+
* entity-level onSet dispatch. `run` is the change tick of the dispatch.
|
|
755
|
+
*
|
|
756
|
+
* Two sources. `scanned` comes from a walk of the tick plane of every
|
|
757
|
+
* archetype stamped since the last drain, taken only in a frame where a
|
|
758
|
+
* chunk loop took `cols.ticks(def)`. A row inside the enabled partition is
|
|
759
|
+
* alive, a member and enabled by construction, so the dispatch fires these
|
|
760
|
+
* with no check, and the walk costs one compare per row of each stamped
|
|
761
|
+
* archetype. `listed` comes from the dirty list the by-id paths keep, minus
|
|
762
|
+
* the entries a scan covers. A listed entity may have died, lost the
|
|
763
|
+
* component or been disabled since its record, so the dispatch checks
|
|
764
|
+
* each. Both come back in row order, and the result object is reused. */
|
|
765
|
+
drainSet(cid: number, run: number): DrainResult;
|
|
678
766
|
/** Visit every non-empty archetype containing `cid` whose component-column
|
|
679
|
-
* changed
|
|
680
|
-
*
|
|
681
|
-
* per-archetype change tick (free
|
|
682
|
-
|
|
767
|
+
* changed after `baseline`, in canonical (ascending archetype-id) order,
|
|
768
|
+
* the archetype-granular onSet detection point. Reuses the existing
|
|
769
|
+
* per-archetype change tick (free, no write-path cost). `baseline` is the
|
|
770
|
+
* change tick of the consumer's previous visit, so a stamp equal to it was
|
|
771
|
+
* made by that visit and is not reported again. */
|
|
772
|
+
forEachChangedArchetype(cid: number, baseline: number, cb: (arch: Archetype) => void): void;
|
|
683
773
|
/** Enabled live entities currently carrying `cid`, used by `yieldExisting` to
|
|
684
|
-
* replay onAdd on registration. Bounded by `enabled_count
|
|
774
|
+
* replay onAdd on registration. Bounded by `enabled_count`: a disabled
|
|
685
775
|
* entity is excluded from default queries, so seeding it via onAdd would
|
|
686
|
-
* publish a row that an immediate onDisable should have removed
|
|
687
|
-
* absent at seed (the "delete on disable" semantics). Unordered here
|
|
776
|
+
* publish a row that an immediate onDisable should have removed. It is
|
|
777
|
+
* absent at seed (the "delete on disable" semantics). Unordered here, the
|
|
688
778
|
* registry radix-sorts. */
|
|
689
|
-
|
|
779
|
+
collectEnabledWith(cid: number): EntityID[];
|
|
690
780
|
registerComponent<S extends Record<string, TypedArrayTag>>(schema: S, name?: string): ComponentDef<S>;
|
|
691
781
|
/** `'Pos' (component 5)` when the component was registered with a debug
|
|
692
|
-
* name, else `component 5
|
|
782
|
+
* name, else `component 5`, the label diagnostics interpolate. */
|
|
693
783
|
componentLabel(cid: number): string;
|
|
694
|
-
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
* field_id)` pairs across the WASM FFI (PR 3C / #231). */
|
|
784
|
+
/** The field index assigned to `(def, fieldName)` at registration. A system
|
|
785
|
+
* that passes `(component_id, field_id)` pairs across the WASM FFI resolves
|
|
786
|
+
* them once, at setup. */
|
|
698
787
|
fieldIdOf(def: ComponentHandle, fieldName: string): number;
|
|
699
788
|
/** Register a sparse component or tag. Unlike `registerComponent`, this
|
|
700
789
|
* allocates from a separate id space and never touches the archetype mask,
|
|
701
790
|
* so it does **not** count against `STORE_DESCRIPTOR_COMPONENT_LIMIT`. See
|
|
702
|
-
*
|
|
791
|
+
* `sparse_store.ts`. */
|
|
703
792
|
registerSparseComponent<S extends Record<string, TypedArrayTag>>(schema: S, name?: string): SparseComponentDef<S>;
|
|
704
|
-
/** Sparse sibling of `componentLabel
|
|
793
|
+
/** Sparse sibling of `componentLabel`, sparse ids are a separate id space. */
|
|
705
794
|
sparseLabel(sid: number): string;
|
|
706
|
-
/** Allocate the backing sparse store
|
|
707
|
-
* engine-internal backings whose `f64` holds an
|
|
795
|
+
/** Allocate the backing sparse store without the float guard, for
|
|
796
|
+
* engine-internal backings whose `f64` holds an exact integer rather than a
|
|
708
797
|
* user quantity: the exclusive-relation `{ target }` slot stores an `EntityID`
|
|
709
|
-
* (≤ 2^53, so f64 is bit-exact and cross-host identical
|
|
798
|
+
* (≤ 2^53, so f64 is bit-exact and cross-host identical, the ban targets float
|
|
710
799
|
* *arithmetic* rounding, which a target slot never undergoes). User schemas go
|
|
711
800
|
* through `registerSparseComponent`, which guards first. */
|
|
712
801
|
private _pushSparseStore;
|
|
713
|
-
private
|
|
802
|
+
private _sparseStoreOf;
|
|
714
803
|
/** Add (or overwrite) a sparse component on an entity. No archetype
|
|
715
|
-
* transition, no row copy
|
|
804
|
+
* transition, no row copy, the entity's `archetype_id` is unchanged. */
|
|
716
805
|
addSparse(entityId: EntityID, def: SparseComponentDef, values?: Record<string, number>): void;
|
|
717
806
|
/** Remove a sparse component from an entity. No-op if absent. */
|
|
718
807
|
removeSparse(entityId: EntityID, def: SparseComponentDef): void;
|
|
719
|
-
/** Total, like `hasComponent
|
|
808
|
+
/** Total, like `hasComponent`, `false` for a dead entity, never a throw. */
|
|
720
809
|
hasSparse(entityId: EntityID, def: SparseComponentDef): boolean;
|
|
721
810
|
getSparseField(entityId: EntityID, def: SparseComponentDef, field: string): number;
|
|
722
811
|
setSparseField(entityId: EntityID, def: SparseComponentDef, field: string, value: number): void;
|
|
723
812
|
/** Drop all sparse data for a destroyed entity index so a recycled slot
|
|
724
|
-
* can't inherit it. Gated by the caller on `
|
|
813
|
+
* can't inherit it. Gated by the caller on `_sparseStores.length > 0` to
|
|
725
814
|
* keep the destroy hot path free when sparse storage is unused. */
|
|
726
815
|
private _purgeSparse;
|
|
816
|
+
/** A sparse component's field names in schema order, the cursor's reserved
|
|
817
|
+
* name check (ref.ts). */
|
|
818
|
+
sparseFieldNames(def: SparseComponentDef): readonly string[];
|
|
819
|
+
/** A sparse component's accessor column array (ref.ts). Its identity is
|
|
820
|
+
* stable for the life of the store, so a cursor holds it from creation. */
|
|
821
|
+
sparseAccessorColumns(def: SparseComponentDef): AccessorColumns;
|
|
822
|
+
/**
|
|
823
|
+
* The DEV-only check a sparse cursor runs on every `at()` (ref.ts
|
|
824
|
+
* `createSparseCursor`): the access declaration (the same reason
|
|
825
|
+
* `cursorBinder` checks on `at()`), the liveness of the entity, and its
|
|
826
|
+
* membership. Production code never calls it, so a read of a non-member
|
|
827
|
+
* there gives whatever the column holds at that index, test with
|
|
828
|
+
* `hasSparse` first when the component can be absent, as `getSparseField`
|
|
829
|
+
* asks too.
|
|
830
|
+
*/
|
|
831
|
+
sparseCursorCheck(def: SparseComponentDef, write: boolean): SparseCursorCheck;
|
|
727
832
|
/** Serialize the sparse stores **and** relation side data to a self-contained
|
|
728
|
-
* byte buffer
|
|
729
|
-
* SAB snapshot). Two framed sections: the sparse stores
|
|
730
|
-
*
|
|
731
|
-
*
|
|
833
|
+
* byte buffer, the sparse half of a world snapshot (the dense half is the
|
|
834
|
+
* SAB snapshot). Two framed sections: the sparse stores
|
|
835
|
+
* (`snapshotSparseStores`, exclusive relation targets and multi membership
|
|
836
|
+
* ride here) followed
|
|
837
|
+
* by the relation side data (`snapshotRelations`, multi forward target
|
|
732
838
|
* sets, which live outside the sparse store). Both are written in canonical
|
|
733
839
|
* entity-index order, so two worlds with identical contents inserted in
|
|
734
|
-
* different orders snapshot byte-for-byte the same
|
|
735
|
-
* is derived and never serialized
|
|
840
|
+
* different orders snapshot byte-for-byte the same. The reverse index
|
|
841
|
+
* is derived and never serialized, `restoreSparse` rebuilds it. Pairs with
|
|
736
842
|
* `restoreSparse`.
|
|
737
843
|
*
|
|
738
|
-
* **Opt-in
|
|
739
|
-
* Store was constructed with `{ deterministic: true }
|
|
844
|
+
* **Opt-in.** Throws `DETERMINISM_DISABLED` unless the
|
|
845
|
+
* Store was constructed with `{ deterministic: true }`, the canonical
|
|
740
846
|
* entity-index ordering is the determinism tax the flag gates. */
|
|
741
847
|
snapshotSparse(): Uint8Array;
|
|
742
848
|
/** Repopulate the sparse stores from `snapshotSparse` bytes, replacing all
|
|
743
849
|
* current sparse data (full-equality round-trip of membership + data), then
|
|
744
850
|
* rebuild every relation's derived side indices: multi forward sets from the
|
|
745
851
|
* relation section, and the reverse index for both cardinalities (exclusive
|
|
746
|
-
* from the
|
|
852
|
+
* from the newly restored sparse target field, multi from the rebuilt forward
|
|
747
853
|
* sets). The sparse components and relations must already be registered in
|
|
748
|
-
* the same order
|
|
854
|
+
* the same order, restore carries data, not the registration (which is
|
|
749
855
|
* code). Throws `SparseRestoreError` if the snapshot's shape, field identity,
|
|
750
856
|
* entity-index bounds, or frame length don't validate.
|
|
751
857
|
*
|
|
752
|
-
* **Opt-in
|
|
753
|
-
* Store was constructed with `{ deterministic: true }
|
|
858
|
+
* **Opt-in.** Throws `DETERMINISM_DISABLED` unless the
|
|
859
|
+
* Store was constructed with `{ deterministic: true }`. Paired with
|
|
754
860
|
* `snapshotSparse`, which produces the canonical bytes restore consumes. */
|
|
755
861
|
restoreSparse(bytes: Uint8Array): void;
|
|
862
|
+
/** The members and their values are the snapshot's now, so no record made
|
|
863
|
+
* before the restore names a write of theirs. */
|
|
864
|
+
private _resetSparseTicks;
|
|
756
865
|
/**
|
|
757
866
|
* Capture the full live world to one self-contained byte buffer that
|
|
758
|
-
* `
|
|
759
|
-
* world and keep ticking"). Three sections
|
|
760
|
-
*
|
|
761
|
-
* (`
|
|
762
|
-
*
|
|
867
|
+
* `restore` can mount back onto a live, ticking world ("rewind a running
|
|
868
|
+
* world and keep ticking"). Three sections, which the snapshots plugin
|
|
869
|
+
* frames: the dense SAB
|
|
870
|
+
* column bytes (`columnStoreBytesView`), the sparse + relation bytes
|
|
871
|
+
* (`snapshotSparse`), and the host-side bookkeeping the SAB omits, the world
|
|
872
|
+
* tick, the entity recycle free-list (in live order, no byte source, and its
|
|
763
873
|
* order is load-bearing for byte-identical resume), the alive count, and each
|
|
764
|
-
* archetype's `length`
|
|
874
|
+
* archetype's `length` and `enabledCount`.
|
|
765
875
|
*
|
|
766
|
-
* **Opt-in
|
|
767
|
-
* `{ deterministic: true }
|
|
876
|
+
* **Opt-in.** Throws `DETERMINISM_DISABLED` unless constructed with
|
|
877
|
+
* `{ deterministic: true }`, the sparse section rides the canonical-ordering
|
|
768
878
|
* surface and byte-identical resume is a determinism property. Pairs with
|
|
769
|
-
* `
|
|
879
|
+
* `restore`.
|
|
770
880
|
*
|
|
771
|
-
*
|
|
772
|
-
* resource-free per-tick state
|
|
773
|
-
*
|
|
774
|
-
* are scheduling artifacts, never
|
|
775
|
-
* at a tick boundary
|
|
881
|
+
* Scope. It captures no resource and no event, because resume requires
|
|
882
|
+
* resource-free per-tick state and the tick clears events. It captures no
|
|
883
|
+
* change-detection baseline either, so a `changed()` query starts fresh.
|
|
884
|
+
* Those are scheduling artifacts, and never fold into `stateHash`. Take the
|
|
885
|
+
* snapshot at a tick boundary, between two `update()` calls. */
|
|
776
886
|
snapshot(): Uint8Array;
|
|
777
887
|
/**
|
|
778
888
|
* Mount a `snapshot()` buffer onto this live world and leave it ready to keep
|
|
779
889
|
* ticking. Fails closed on a malformed frame or a registration mismatch
|
|
780
|
-
*
|
|
781
|
-
* from code, not the snapshot
|
|
890
|
+
* before any live state is touched (the archetype and component graph is rebuilt
|
|
891
|
+
* from code, not the snapshot, same contract as `restoreSparse`). On
|
|
782
892
|
* success the world's dense + sparse state, entity allocator, and tick are
|
|
783
893
|
* exactly the captured world's.
|
|
784
894
|
*
|
|
785
895
|
* Requires a world whose SAB-backed archetype set + column layout match the
|
|
786
896
|
* snapshot's exactly (prewarm so the archetype set is stable) and the same
|
|
787
|
-
* entity-index capacity. **Opt-in
|
|
897
|
+
* entity-index capacity. **Opt-in:** throws `DETERMINISM_DISABLED`
|
|
788
898
|
* unless `{ deterministic: true }`. See `snapshot()` for the v1 scope. */
|
|
789
|
-
|
|
790
|
-
/** Adopt a restored dense store (
|
|
899
|
+
restore(bytes: Uint8Array): void;
|
|
900
|
+
/** Adopt a restored dense store (the snapshot service's restore mount
|
|
791
901
|
* step): swap the live backing, refresh every buffer-backed archetype's
|
|
792
902
|
* views, recover the allocator high-water from the restored region, and
|
|
793
903
|
* republish (the grow tail). Store-owned because it assigns
|
|
794
|
-
* `_columnStore
|
|
904
|
+
* `_columnStore`, the service never writes Store fields. */
|
|
795
905
|
private _mountRestoredDense;
|
|
796
|
-
/** Rebuild each SAB-backed archetype's host-side `length
|
|
797
|
-
* `_entityIds` after the dense backing
|
|
798
|
-
*
|
|
799
|
-
* entity-index region
|
|
800
|
-
* from the captured host
|
|
801
|
-
*
|
|
906
|
+
/** Rebuild each SAB-backed archetype's host-side `length`, `enabledCount`
|
|
907
|
+
* and `_entityIds` after `restore` swapped the dense backing. `length` and
|
|
908
|
+
* the per-row entity-id back-reference come from a scan of the restored
|
|
909
|
+
* entity-index region, which says what entity occupies what row.
|
|
910
|
+
* `enabledCount` comes from the captured host state, because the partition
|
|
911
|
+
* boundary is positional and has no per-entity byte source. */
|
|
802
912
|
private _reconstructHostRows;
|
|
803
|
-
|
|
804
|
-
/** Number of registered relations. Visible to tests asserting the
|
|
805
|
-
* no-transition invariant alongside `archetype_count`. */
|
|
806
|
-
get relationCount(): number;
|
|
807
|
-
addRelation(src: EntityID, def: RelationDef, tgt: EntityID): void;
|
|
808
|
-
removeRelation(src: EntityID, def: RelationDef, tgt?: EntityID): void;
|
|
809
|
-
targetOf(src: EntityID, def: RelationDef): EntityID | undefined;
|
|
810
|
-
targetsOf(src: EntityID, def: RelationDef): EntityID[];
|
|
811
|
-
sourcesOf(tgt: EntityID, def: RelationDef): EntityID[];
|
|
812
|
-
hasRelation(src: EntityID, def: RelationDef): boolean;
|
|
813
|
-
pairsOf(def: RelationDef): readonly (readonly [EntityID, EntityID])[];
|
|
814
|
-
sourcesOfAny(tgt: EntityID): readonly (readonly [RelationDef, EntityID])[];
|
|
815
|
-
relationBackingSparseId(def: RelationDef): SparseComponentID;
|
|
816
|
-
/** Drive a `(*, T)` wildcard query (`Query.forEachRelatedTo`) — see
|
|
817
|
-
* `RelationService.forEachRelationTargetMatch`. */
|
|
818
|
-
_forEachRelationTargetMatch(target: EntityID, include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, sparseInclude: readonly SparseComponentID[], sparseExclude: readonly SparseComponentID[], includeDisabled: boolean, cb: (entityId: EntityID) => void): void;
|
|
819
|
-
compactRelations(): number;
|
|
820
|
-
ancestorsOf(src: EntityID, def: RelationDef): EntityID[];
|
|
821
|
-
rootOf(src: EntityID, def: RelationDef): EntityID;
|
|
822
|
-
cascadeOf(root: EntityID, def: RelationDef): EntityID[];
|
|
823
|
-
/** Second query-match path (#469 / ADR-0011): iterate entities matching a
|
|
913
|
+
/** Second query-match path: iterate entities matching a
|
|
824
914
|
* dense mask **and** sparse-membership terms, invoking `cb` per entity.
|
|
825
|
-
* Yields `EntityID`s, not archetype spans
|
|
915
|
+
* Yields `EntityID`s, not archetype spans, sparse members are scattered
|
|
826
916
|
* across archetypes, so there is no SoA column to hand back. Driven by the
|
|
827
917
|
* cheapest candidate set:
|
|
828
918
|
*
|
|
@@ -835,65 +925,83 @@ export declare class Store implements ObserverHost, QueryHost {
|
|
|
835
925
|
* store.
|
|
836
926
|
* - **neither** → walk `denseArchetypes`' entity ids (dense-only fallback).
|
|
837
927
|
*
|
|
838
|
-
* Only reached via `Query.forEachEntity
|
|
839
|
-
* the sparse stores, so dense-only queries are unaffected
|
|
840
|
-
|
|
841
|
-
/** Fourth query-match path
|
|
842
|
-
* (parents before children)
|
|
843
|
-
|
|
928
|
+
* Only reached via `Query.forEachEntity`. Dense `forEach` never consults
|
|
929
|
+
* the sparse stores, so dense-only queries are unaffected. */
|
|
930
|
+
forEachSparseMatch(include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, terms: QueryTerms, denseArchetypes: readonly Archetype[], cb: (entityId: EntityID) => void): void;
|
|
931
|
+
/** Fourth query-match path: the matched set in hierarchy depth order
|
|
932
|
+
* (parents before children), see `forEachHierarchyMatch` on the relation service. */
|
|
933
|
+
forEachHierarchyMatch(include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, terms: QueryTerms, denseArchetypes: readonly Archetype[], relation: RelationDef, maxDepth: number, cb: (entityId: EntityID) => void): void;
|
|
934
|
+
/** Third query-match path: the `(*, T)` wildcard, every source pointing at
|
|
935
|
+
* one target. `Query.forEachRelatedTo` is the seam the user reaches. */
|
|
936
|
+
forEachTargetMatch(target: EntityID, include: BitSet, exclude: BitSet | null, anyOf: BitSet | null, terms: QueryTerms, cb: (entityId: EntityID) => void): void;
|
|
937
|
+
/** The sparse membership store a relation rides, which is how a query turns
|
|
938
|
+
* a `(R, *)` term into a sparse term. `api` names the query verb the user
|
|
939
|
+
* called, because `andRelation` and `notRelation` both land here. */
|
|
940
|
+
relationBackingSparseId(def: RelationDef, api: string): SparseComponentID;
|
|
941
|
+
private readonly _scratchTargetMask;
|
|
844
942
|
addComponent(entityId: EntityID, def: ComponentDef<Record<string, never>>): void;
|
|
845
943
|
addComponent<S extends ComponentSchema>(entityId: EntityID, def: ComponentDef<S>, values: FieldValues<S>): void;
|
|
944
|
+
/** @internal, cold tail of `addComponent`: the entity already holds `def`
|
|
945
|
+
* (overwrite in place, no transition), or the (source, component) add edge
|
|
946
|
+
* has not been cached yet (first time this pair is seen, resolve, which
|
|
947
|
+
* plants the edge, then re-enter the hot body). Split out so the edge-hit
|
|
948
|
+
* path above stays a straight line with one holey-array probe. This runs at
|
|
949
|
+
* most once per (archetype, component) pair plus on in-place overwrites. */
|
|
950
|
+
private _addComponentCold;
|
|
846
951
|
/** Add multiple components in one transition (resolves final archetype, then moves once).
|
|
847
952
|
*
|
|
848
953
|
* Final-mask resolve, not graph walk. The previous implementation called
|
|
849
|
-
* `
|
|
850
|
-
* intermediate archetype on the path
|
|
954
|
+
* `_archResolveAdd` once per entry, which threaded through every
|
|
955
|
+
* intermediate archetype on the path, and each unseen intermediate
|
|
851
956
|
* triggered a fresh `extendColumnStore` even though no entity ever lived
|
|
852
957
|
* there. Computing the union mask up front and resolving once via
|
|
853
|
-
* `
|
|
854
|
-
* creations into zero for the batched case
|
|
855
|
-
* single-mask path remains the same
|
|
958
|
+
* `_archGetOrCreateFromMask` collapses N-1 intermediate-archetype
|
|
959
|
+
* creations into zero for the batched case. The lazy
|
|
960
|
+
* single-mask path remains the same. This only avoids feeding it
|
|
856
961
|
* archetypes the entity never visits.
|
|
857
962
|
*
|
|
858
|
-
*
|
|
963
|
+
* Composite-add edge cache. The final-mask resolve, unlike the
|
|
859
964
|
* single-add `edges[]` walk, re-pays a per-call `mask.hash()`, `ArchetypeGraph.lookup`
|
|
860
|
-
* (the Map-of-buckets + `equals` scan), and `
|
|
861
|
-
* every call
|
|
862
|
-
*
|
|
863
|
-
* set) add now resolves through `currentArch`'s composite-add cache: one
|
|
965
|
+
* (the Map-of-buckets + `equals` scan), and `transitionMapTo` on
|
|
966
|
+
* every call. That is much slower than a cached edge walk, and a probe put the
|
|
967
|
+
* cost on the two `Map.get` calls, and not on the hash. So a repeated (source,
|
|
968
|
+
* added-set) add now resolves through `currentArch`'s composite-add cache: one
|
|
864
969
|
* `Map.get` on an exact packed key yields the target + transition map, and we
|
|
865
970
|
* skip the union-mask build entirely. First call per key still resolves via
|
|
866
|
-
* the final-mask path below (no intermediate planting) and plants the edge.
|
|
867
|
-
* See docs/reports/bench/regressions/add-components-composite-edge.md. */
|
|
971
|
+
* the final-mask path below (no intermediate planting) and plants the edge. */
|
|
868
972
|
addComponents(entityId: EntityID, entries: readonly TemplateEntryData[]): void;
|
|
869
|
-
/** Shared move+write tail of `addComponents
|
|
870
|
-
* the already-resolved `targetArch
|
|
973
|
+
/** Shared move+write tail of `addComponents`: place the entity into
|
|
974
|
+
* the already-resolved `targetArch`, a `moveEntityFrom` along the cached
|
|
871
975
|
* `map` when it has a row, else a fresh append (the rowless empty-archetype
|
|
872
|
-
* source ignores `map`)
|
|
976
|
+
* source ignores `map`), then write every entry's fields. Both the
|
|
873
977
|
* composite-edge-cache hit and the final-mask cold path funnel through here so
|
|
874
978
|
* the placement logic lives once. */
|
|
875
979
|
private _addComponentsInto;
|
|
876
980
|
removeComponent(entityId: EntityID, def: ComponentDef): void;
|
|
981
|
+
/** @internal, cold tail of `removeComponent`: the entity doesn't hold `def`
|
|
982
|
+
* (no-op), or the (source, component) remove edge has not been cached yet.
|
|
983
|
+
* Mirror of `_addComponentCold`. Same depth-1 re-entry argument. */
|
|
984
|
+
private _removeComponentCold;
|
|
877
985
|
/** Remove multiple components in one transition (resolves final archetype, then moves once).
|
|
878
986
|
*
|
|
879
987
|
* Final-mask resolve, not graph walk. Same rationale as `addComponents`
|
|
880
|
-
* above
|
|
988
|
+
* above, the previous per-step path threaded `_archResolveRemove`
|
|
881
989
|
* once per def, which materialised every intermediate archetype on the
|
|
882
990
|
* removal path. Computing the difference mask up front and resolving
|
|
883
991
|
* once avoids planting N-1 intermediates the entity never lives in. */
|
|
884
992
|
removeComponents(entityId: EntityID, defs: ComponentDef[]): void;
|
|
885
|
-
/** Total
|
|
886
|
-
* than throwing
|
|
993
|
+
/** Total: a dead or stale `entityId` returns `false` rather
|
|
994
|
+
* than throwing, a "has" probe is exactly what callers reach for to avoid
|
|
887
995
|
* touching dead entities, so it must be safe to ask. */
|
|
888
996
|
hasComponent(entityId: EntityID, def: ComponentHandle): boolean;
|
|
889
997
|
/**
|
|
890
|
-
* Bulk add a component to
|
|
998
|
+
* Bulk add a component to all entities in the given archetype.
|
|
891
999
|
* Uses TypedArray.set() for O(columns) instead of O(N×columns).
|
|
892
1000
|
* The archetype must not already contain this component.
|
|
893
1001
|
*/
|
|
894
1002
|
batchAddComponent(src: ArchetypeID, def: ComponentDef, values?: Record<string, number>): void;
|
|
895
1003
|
/**
|
|
896
|
-
* Bulk remove a component from
|
|
1004
|
+
* Bulk remove a component from all entities in the given archetype.
|
|
897
1005
|
* Uses TypedArray.set() for O(columns) instead of O(N×columns).
|
|
898
1006
|
* The archetype must contain this component.
|
|
899
1007
|
*/
|
|
@@ -901,37 +1009,62 @@ export declare class Store implements ObserverHost, QueryHost {
|
|
|
901
1009
|
getEntityArchetype(entityId: EntityID): Archetype;
|
|
902
1010
|
getEntityRow(entityId: EntityID): number;
|
|
903
1011
|
/**
|
|
904
|
-
*
|
|
905
|
-
*
|
|
906
|
-
*
|
|
1012
|
+
* The row `resolveEntity` placed the entity at, the alloc-free second
|
|
1013
|
+
* return value of a resolve, read immediately after the call. Same out-param
|
|
1014
|
+
* pattern as `EntityAllocator.lastIndex` and `_moveResult`. Returning a
|
|
1015
|
+
* `{ arch, row }` pair instead would allocate on every by-id read.
|
|
1016
|
+
*/
|
|
1017
|
+
resolvedRow: number;
|
|
1018
|
+
/**
|
|
1019
|
+
* (archetype, row) for a by-id access, derived from one index computation.
|
|
1020
|
+
*
|
|
1021
|
+
* `getEntityArchetype` and `getEntityRow` are each one line, and every by-id
|
|
1022
|
+
* caller needs both, so the pair cost two derivations of the same packed
|
|
1023
|
+
* index and two call frames to read two elements of two parallel arrays
|
|
1024
|
+
* addressed identically. This is that pair, fused: index once, publish the
|
|
1025
|
+
* row on `resolvedRow`, return the archetype.
|
|
1026
|
+
*
|
|
1027
|
+
* The two single-purpose accessors stay above, tests reach for one half at a
|
|
1028
|
+
* time, but no runtime path uses them in a pair any more.
|
|
1029
|
+
*/
|
|
1030
|
+
resolveEntity(entityId: EntityID): Archetype;
|
|
1031
|
+
/**
|
|
1032
|
+
* Build the `at(entity)` binder a `ComponentCursor` repoints itself through
|
|
1033
|
+
* (ref.ts). Handing the cursor a closure rather than the Store keeps the
|
|
1034
|
+
* mutation surface out of a value that user code holds onto.
|
|
1035
|
+
*
|
|
1036
|
+
* `stampTick` distinguishes the two variants once, here, instead of per
|
|
1037
|
+
* `at()`: a mutable cursor bumps the component's change tick on every
|
|
1038
|
+
* repoint (matching `ctx.ref`), a read-only one never does.
|
|
1039
|
+
*
|
|
1040
|
+
* The access check lives here, in the binder, and not only at the call that
|
|
1041
|
+
* creates the cursor. A cursor is made one time and then kept, so it outlives
|
|
1042
|
+
* the span that made it. A cursor made at host level writes an undeclared
|
|
1043
|
+
* component when a system body uses it, because no system is active at
|
|
1044
|
+
* creation and the check there passes. A `ctx.cursor` that a system stores in
|
|
1045
|
+
* an outer variable does the same in the next system. Both slip
|
|
1046
|
+
* past a check that only runs at creation. `at()` is the point of use, so the
|
|
1047
|
+
* check belongs on it. The creation-site check stays as well: it fails early,
|
|
1048
|
+
* and its stack names the line that made the cursor.
|
|
1049
|
+
*/
|
|
1050
|
+
cursorBinder(def: ComponentHandle, stampTick: boolean): CursorBinder;
|
|
1051
|
+
/** A component's field names in schema order, the cursor prototype key and
|
|
1052
|
+
* ordinal source (ref.ts). One array per component, owned by its meta. */
|
|
1053
|
+
componentFieldNames(def: ComponentHandle): readonly string[];
|
|
1054
|
+
/**
|
|
1055
|
+
* Find all archetypes matching the given masks. Delegates to
|
|
1056
|
+
* `QueryRegistry`, which starts the superset scan from the smallest
|
|
1057
|
+
* component bucket. Query mint path only.
|
|
907
1058
|
*/
|
|
908
1059
|
getMatchingArchetypes(required: BitSet, excluded?: BitSet, anyOf?: BitSet): readonly Archetype[];
|
|
909
1060
|
/**
|
|
910
|
-
* Register a live query. Returns a mutable Archetype[] that
|
|
911
|
-
*
|
|
1061
|
+
* Register a live query. Returns a mutable Archetype[] that the registry
|
|
1062
|
+
* pushes newly-created matching archetypes into, keeping the query always
|
|
1063
|
+
* up-to-date.
|
|
912
1064
|
*/
|
|
913
1065
|
registerQuery(include: BitSet, exclude?: BitSet, anyOf?: BitSet): Archetype[];
|
|
914
1066
|
updateQueryRef(result: Archetype[], query: Query<any>): void;
|
|
915
1067
|
get archetypeCount(): number;
|
|
916
|
-
|
|
917
|
-
emitEvent(def: EventDef<any>, values: Record<string, number>): void;
|
|
918
|
-
emitSignal(def: EventDef<EmptyEventSchema>): void;
|
|
919
|
-
getEventReader<S extends EventShape<S>>(def: EventDef<S>): EventReader<S>;
|
|
920
|
-
clearEvents(): void;
|
|
921
|
-
/** `DEV`-only mid-update emit detection — see
|
|
922
|
-
* `EventRegistry.devBufferedEventCount`. */
|
|
923
|
-
_devBufferedEventCount(): number;
|
|
924
|
-
registerEventByKey<S extends EventShape<S>>(key: symbol, fields: readonly (keyof S & string)[]): EventDef<S>;
|
|
925
|
-
getEventDefByKey(key: symbol): EventDef<any>;
|
|
926
|
-
hasEventKey(key: symbol): boolean;
|
|
927
|
-
private readonly resources;
|
|
928
|
-
registerResource(key: symbol, value: unknown): void;
|
|
929
|
-
getResource(key: symbol): unknown;
|
|
930
|
-
setResource(key: symbol, value: unknown): void;
|
|
931
|
-
/** Fails closed on a missing key; the present → absent → present
|
|
932
|
-
* lifecycle (#798) — see `ResourceRegistry.remove`. */
|
|
933
|
-
removeResource(key: symbol): void;
|
|
934
|
-
hasResource(key: symbol): boolean;
|
|
1068
|
+
private readonly _resources;
|
|
935
1069
|
}
|
|
936
|
-
export {};
|
|
937
1070
|
//# sourceMappingURL=store.d.ts.map
|