murow 0.0.60 → 0.0.71
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/README.md +52 -37
- package/dist/cjs/core/binary-codec/binary-codec.js +1 -0
- package/dist/cjs/core/binary-codec/index.js +1 -0
- package/dist/cjs/core/driver/driver.js +1 -0
- package/dist/cjs/core/driver/drivers/immediate.js +1 -0
- package/dist/cjs/core/driver/drivers/index.js +1 -0
- package/dist/cjs/core/driver/drivers/raf.js +1 -0
- package/dist/cjs/core/driver/drivers/timeout.js +1 -0
- package/dist/cjs/core/driver/index.js +1 -0
- package/dist/cjs/core/events/event-system.js +1 -0
- package/dist/cjs/core/events/index.js +1 -0
- package/dist/cjs/core/fixed-ticker/fixed-ticker.js +1 -0
- package/dist/cjs/core/fixed-ticker/index.js +1 -0
- package/dist/cjs/core/free-list/free-list.js +1 -0
- package/dist/cjs/core/free-list/index.js +1 -0
- package/dist/cjs/core/generate-id/generate-id.js +1 -0
- package/dist/cjs/core/generate-id/index.js +1 -0
- package/dist/cjs/core/index.js +1 -0
- package/dist/cjs/core/input/index.js +1 -0
- package/dist/cjs/core/input/manager.js +1 -0
- package/dist/cjs/core/input/sources/browser.js +1 -0
- package/dist/cjs/core/input/sources/index.js +1 -0
- package/dist/cjs/core/input/types.js +1 -0
- package/dist/cjs/core/lerp/index.js +1 -0
- package/dist/cjs/core/lerp/lerp.js +1 -0
- package/dist/cjs/core/navmesh/index.js +1 -0
- package/dist/cjs/core/navmesh/navmesh-worker-pool.js +1 -0
- package/dist/cjs/core/navmesh/navmesh.js +1 -0
- package/dist/cjs/core/navmesh/navmesh.worker.js +1 -0
- package/dist/cjs/core/pooled-codec/index.js +1 -0
- package/dist/cjs/core/pooled-codec/pooled-codec.js +1 -0
- package/dist/cjs/core/prediction/index.js +1 -0
- package/dist/cjs/core/prediction/prediction.js +1 -0
- package/dist/cjs/core/ray/index.js +1 -0
- package/dist/cjs/core/ray/ray-2d.js +1 -0
- package/dist/cjs/core/ray/ray-3d.js +1 -0
- package/dist/cjs/core/simple-rng/index.js +1 -0
- package/dist/cjs/core/simple-rng/simple-rng.js +1 -0
- package/dist/cjs/core/sparse-batcher/index.js +1 -0
- package/dist/cjs/core/sparse-batcher/sparse-batcher.js +1 -0
- package/dist/cjs/ecs/component-store.js +1 -0
- package/dist/cjs/ecs/component.js +1 -0
- package/dist/cjs/ecs/entity-handle.js +1 -0
- package/dist/cjs/ecs/index.js +1 -0
- package/dist/cjs/ecs/system-builder.js +1 -0
- package/dist/cjs/ecs/world-systems.js +1 -0
- package/dist/cjs/ecs/world.js +1 -0
- package/dist/cjs/game/index.js +1 -0
- package/dist/cjs/game/loop/index.js +1 -0
- package/dist/cjs/game/loop/loop.js +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/net/adapters/browser-websocket.js +1 -0
- package/dist/cjs/net/adapters/bun-websocket.js +1 -0
- package/dist/cjs/net/buffer-pool.js +1 -0
- package/dist/cjs/net/client.js +1 -0
- package/dist/cjs/net/index.js +1 -0
- package/dist/cjs/net/server.js +1 -0
- package/dist/cjs/net/types.js +1 -0
- package/dist/cjs/net/validators.js +1 -0
- package/dist/cjs/protocol/index.js +1 -0
- package/dist/cjs/protocol/intent/define-intent.js +1 -0
- package/dist/cjs/protocol/intent/index.js +1 -0
- package/dist/cjs/protocol/intent/intent-registry.js +1 -0
- package/dist/cjs/protocol/intent/intent.js +1 -0
- package/dist/cjs/protocol/rpc/define-rpc.js +1 -0
- package/dist/cjs/protocol/rpc/index.js +1 -0
- package/dist/cjs/protocol/rpc/rpc-registry.js +1 -0
- package/dist/cjs/protocol/rpc/rpc.js +1 -0
- package/dist/cjs/protocol/snapshot/index.js +1 -0
- package/dist/cjs/protocol/snapshot/snapshot-codec.js +1 -0
- package/dist/cjs/protocol/snapshot/snapshot-registry.js +1 -0
- package/dist/cjs/protocol/snapshot/snapshot.js +1 -0
- package/dist/cjs/renderer/base-2d-renderer.js +1 -0
- package/dist/cjs/renderer/base-3d-renderer.js +1 -0
- package/dist/cjs/renderer/base-renderer.js +1 -0
- package/dist/cjs/renderer/index.js +1 -0
- package/dist/cjs/renderer/types.js +1 -0
- package/dist/esm/core/binary-codec/binary-codec.js +1 -0
- package/dist/esm/core/binary-codec/index.js +1 -0
- package/dist/esm/core/driver/driver.js +1 -0
- package/dist/esm/core/driver/drivers/immediate.js +1 -0
- package/dist/esm/core/driver/drivers/index.js +1 -0
- package/dist/esm/core/driver/drivers/raf.js +1 -0
- package/dist/esm/core/driver/drivers/timeout.js +1 -0
- package/dist/esm/core/driver/index.js +1 -0
- package/dist/esm/core/events/event-system.js +1 -0
- package/dist/esm/core/events/index.js +1 -0
- package/dist/esm/core/fixed-ticker/fixed-ticker.js +1 -0
- package/dist/esm/core/fixed-ticker/index.js +1 -0
- package/dist/esm/core/free-list/free-list.js +1 -0
- package/dist/esm/core/free-list/index.js +1 -0
- package/dist/esm/core/generate-id/generate-id.js +1 -0
- package/dist/esm/core/generate-id/index.js +1 -0
- package/dist/esm/core/index.js +1 -0
- package/dist/esm/core/input/index.js +1 -0
- package/dist/esm/core/input/manager.js +1 -0
- package/dist/esm/core/input/sources/browser.js +1 -0
- package/dist/esm/core/input/sources/index.js +1 -0
- package/dist/esm/core/input/types.js +0 -0
- package/dist/esm/core/lerp/index.js +1 -0
- package/dist/esm/core/lerp/lerp.js +1 -0
- package/dist/esm/core/navmesh/index.js +1 -0
- package/dist/esm/core/navmesh/navmesh-worker-pool.js +1 -0
- package/dist/esm/core/navmesh/navmesh.js +1 -0
- package/dist/esm/core/navmesh/navmesh.worker.js +1 -0
- package/dist/esm/core/pooled-codec/index.js +1 -0
- package/dist/esm/core/pooled-codec/pooled-codec.js +1 -0
- package/dist/esm/core/prediction/index.js +1 -0
- package/dist/esm/core/prediction/prediction.js +1 -0
- package/dist/esm/core/ray/index.js +1 -0
- package/dist/esm/core/ray/ray-2d.js +1 -0
- package/dist/esm/core/ray/ray-3d.js +1 -0
- package/dist/esm/core/simple-rng/index.js +1 -0
- package/dist/esm/core/simple-rng/simple-rng.js +1 -0
- package/dist/esm/core/sparse-batcher/index.js +1 -0
- package/dist/esm/core/sparse-batcher/sparse-batcher.js +1 -0
- package/dist/esm/ecs/component-store.js +1 -0
- package/dist/esm/ecs/component.js +1 -0
- package/dist/esm/ecs/entity-handle.js +1 -0
- package/dist/esm/ecs/index.js +1 -0
- package/dist/esm/ecs/system-builder.js +1 -0
- package/dist/esm/ecs/world-systems.js +1 -0
- package/dist/esm/ecs/world.js +1 -0
- package/dist/esm/game/index.js +1 -0
- package/dist/esm/game/loop/index.js +1 -0
- package/dist/esm/game/loop/loop.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/net/adapters/browser-websocket.js +1 -0
- package/dist/esm/net/adapters/bun-websocket.js +1 -0
- package/dist/esm/net/buffer-pool.js +1 -0
- package/dist/esm/net/client.js +1 -0
- package/dist/esm/net/index.js +1 -0
- package/dist/esm/net/server.js +1 -0
- package/dist/esm/net/types.js +1 -0
- package/dist/esm/net/validators.js +1 -0
- package/dist/esm/protocol/index.js +1 -0
- package/dist/esm/protocol/intent/define-intent.js +1 -0
- package/dist/esm/protocol/intent/index.js +1 -0
- package/dist/esm/protocol/intent/intent-registry.js +1 -0
- package/dist/esm/protocol/intent/intent.js +0 -0
- package/dist/esm/protocol/rpc/define-rpc.js +1 -0
- package/dist/esm/protocol/rpc/index.js +1 -0
- package/dist/esm/protocol/rpc/rpc-registry.js +1 -0
- package/dist/esm/protocol/rpc/rpc.js +0 -0
- package/dist/esm/protocol/snapshot/index.js +1 -0
- package/dist/esm/protocol/snapshot/snapshot-codec.js +1 -0
- package/dist/esm/protocol/snapshot/snapshot-registry.js +1 -0
- package/dist/esm/protocol/snapshot/snapshot.js +1 -0
- package/dist/esm/renderer/base-2d-renderer.js +1 -0
- package/dist/esm/renderer/base-3d-renderer.js +1 -0
- package/dist/esm/renderer/base-renderer.js +1 -0
- package/dist/esm/renderer/index.js +1 -0
- package/dist/esm/renderer/types.js +0 -0
- package/dist/{core → types/core}/binary-codec/binary-codec.d.ts +4 -0
- package/dist/{core/loop → types/core/driver}/drivers/immediate.d.ts +1 -1
- package/dist/{core/loop → types/core/driver}/drivers/raf.d.ts +1 -1
- package/dist/{core/loop → types/core/driver}/drivers/timeout.d.ts +1 -1
- package/dist/{core/loop → types/core/driver}/index.d.ts +1 -1
- package/dist/{core → types/core}/events/event-system.d.ts +14 -33
- package/dist/{core → types/core}/fixed-ticker/fixed-ticker.d.ts +1 -1
- package/dist/types/core/free-list/free-list.d.ts +31 -0
- package/dist/types/core/free-list/index.d.ts +1 -0
- package/dist/{core → types/core}/index.d.ts +7 -1
- package/dist/types/core/input/index.d.ts +3 -0
- package/dist/types/core/input/manager.d.ts +56 -0
- package/dist/types/core/input/sources/browser.d.ts +9 -0
- package/dist/types/core/input/sources/index.d.ts +1 -0
- package/dist/types/core/input/types.d.ts +36 -0
- package/dist/{core → types/core}/navmesh/navmesh.d.ts +1 -21
- package/dist/types/core/ray/index.d.ts +2 -0
- package/dist/types/core/ray/ray-2d.d.ts +37 -0
- package/dist/types/core/ray/ray-3d.d.ts +42 -0
- package/dist/types/core/simple-rng/index.d.ts +1 -0
- package/dist/types/core/simple-rng/simple-rng.d.ts +36 -0
- package/dist/types/core/sparse-batcher/index.d.ts +1 -0
- package/dist/types/core/sparse-batcher/sparse-batcher.d.ts +55 -0
- package/dist/{ecs → types/ecs}/system-builder.d.ts +20 -9
- package/dist/{ecs → types/ecs}/world.d.ts +11 -0
- package/dist/types/game/index.d.ts +1 -0
- package/dist/types/game/loop/index.d.ts +1 -0
- package/dist/types/game/loop/loop.d.ts +175 -0
- package/dist/{index.d.ts → types/index.d.ts} +2 -0
- package/dist/{net → types/net}/index.d.ts +2 -2
- package/dist/{net → types/net}/server.d.ts +39 -19
- package/dist/{protocol → types/protocol}/intent/define-intent.d.ts +15 -0
- package/dist/{protocol → types/protocol}/intent/index.d.ts +1 -1
- package/dist/types/renderer/base-2d-renderer.d.ts +13 -0
- package/dist/types/renderer/base-3d-renderer.d.ts +10 -0
- package/dist/types/renderer/base-renderer.d.ts +21 -0
- package/dist/types/renderer/index.d.ts +4 -0
- package/dist/types/renderer/types.d.ts +79 -0
- package/dist/webgpu/cjs/index.js +6004 -0
- package/dist/webgpu/esm/index.js +5972 -0
- package/dist/webgpu/types/2d/animation.d.ts +97 -0
- package/dist/webgpu/types/2d/renderer.d.ts +55 -0
- package/dist/webgpu/types/2d/shader.d.ts +61 -0
- package/dist/webgpu/types/2d/sprite-accessor.d.ts +47 -0
- package/dist/webgpu/types/2d/sprite-accessor.test.d.ts +1 -0
- package/dist/webgpu/types/3d/gltf-skin-parser.d.ts +101 -0
- package/dist/webgpu/types/3d/morph-animation.d.ts +69 -0
- package/dist/webgpu/types/3d/morph-animation.test.d.ts +1 -0
- package/dist/webgpu/types/3d/renderer.d.ts +216 -0
- package/dist/webgpu/types/3d/shader.d.ts +136 -0
- package/dist/webgpu/types/3d/skeletal-animation-compute/index.d.ts +2 -0
- package/dist/webgpu/types/3d/skeletal-animation-compute/kernel.d.ts +8 -0
- package/dist/webgpu/types/3d/skeletal-animation-compute/packer.d.ts +32 -0
- package/dist/webgpu/types/3d/skeletal-animation.d.ts +90 -0
- package/dist/webgpu/types/camera/camera-2d.d.ts +53 -0
- package/dist/webgpu/types/camera/camera-2d.test.d.ts +1 -0
- package/dist/webgpu/types/camera/camera-3d.d.ts +81 -0
- package/dist/webgpu/types/camera/camera-3d.test.d.ts +1 -0
- package/dist/webgpu/types/camera/index.d.ts +2 -0
- package/dist/webgpu/types/compute/compute-builder.d.ts +123 -0
- package/dist/webgpu/types/compute/compute-builder.test.d.ts +1 -0
- package/dist/webgpu/types/core/constants.d.ts +59 -0
- package/dist/webgpu/types/core/constants.test.d.ts +1 -0
- package/dist/webgpu/types/core/index.d.ts +2 -0
- package/dist/webgpu/types/core/math.d.ts +37 -0
- package/dist/webgpu/types/core/types.d.ts +125 -0
- package/dist/webgpu/types/core/types.test.d.ts +1 -0
- package/dist/webgpu/types/geometry/built-in.d.ts +58 -0
- package/dist/webgpu/types/geometry/built-in.test.d.ts +1 -0
- package/dist/webgpu/types/geometry/geometry-builder.d.ts +281 -0
- package/dist/webgpu/types/geometry/geometry-builder.test.d.ts +1 -0
- package/dist/webgpu/types/geometry/index.d.ts +2 -0
- package/dist/webgpu/types/index.d.ts +32 -0
- package/dist/webgpu/types/particle/emitter.d.ts +36 -0
- package/dist/webgpu/types/shaders/index.d.ts +2 -0
- package/dist/webgpu/types/shaders/runtime-transpile.d.ts +18 -0
- package/dist/webgpu/types/shaders/sprite-2d.wgsl.d.ts +10 -0
- package/dist/webgpu/types/shaders/typegpu.d.ts +9 -0
- package/dist/webgpu/types/shaders/utils.d.ts +28 -0
- package/dist/webgpu/types/shaders/utils.test.d.ts +1 -0
- package/dist/webgpu/types/spritesheet/index.d.ts +1 -0
- package/dist/webgpu/types/spritesheet/spritesheet.d.ts +57 -0
- package/dist/webgpu/types/spritesheet/spritesheet.test.d.ts +1 -0
- package/package.json +96 -26
- package/dist/core/binary-codec/binary-codec.js +0 -354
- package/dist/core/binary-codec/index.js +0 -1
- package/dist/core/events/event-system.js +0 -88
- package/dist/core/events/index.js +0 -1
- package/dist/core/fixed-ticker/fixed-ticker.js +0 -101
- package/dist/core/fixed-ticker/index.js +0 -1
- package/dist/core/generate-id/generate-id.js +0 -25
- package/dist/core/generate-id/index.js +0 -1
- package/dist/core/index.js +0 -9
- package/dist/core/lerp/index.js +0 -1
- package/dist/core/lerp/lerp.js +0 -42
- package/dist/core/loop/drivers/immediate.js +0 -61
- package/dist/core/loop/drivers/index.js +0 -3
- package/dist/core/loop/drivers/raf.js +0 -62
- package/dist/core/loop/drivers/timeout.js +0 -71
- package/dist/core/loop/index.js +0 -2
- package/dist/core/loop/loop.js +0 -47
- package/dist/core/navmesh/index.js +0 -1
- package/dist/core/navmesh/navmesh-worker-pool.js +0 -180
- package/dist/core/navmesh/navmesh.js +0 -799
- package/dist/core/navmesh/navmesh.worker.js +0 -79
- package/dist/core/pooled-codec/index.js +0 -1
- package/dist/core/pooled-codec/pooled-codec.js +0 -410
- package/dist/core/prediction/index.js +0 -1
- package/dist/core/prediction/prediction.js +0 -99
- package/dist/core.esm.js +0 -1
- package/dist/core.js +0 -1
- package/dist/ecs/component-store.js +0 -175
- package/dist/ecs/component.js +0 -43
- package/dist/ecs/entity-handle.js +0 -515
- package/dist/ecs/example.js +0 -125
- package/dist/ecs/index.js +0 -4
- package/dist/ecs/system-builder.js +0 -180
- package/dist/ecs/system.d.ts +0 -63
- package/dist/ecs/system.js +0 -92
- package/dist/ecs/world-systems.js +0 -79
- package/dist/ecs/world.js +0 -684
- package/dist/index.js +0 -24
- package/dist/net/adapters/browser-websocket.js +0 -74
- package/dist/net/adapters/bun-websocket.js +0 -245
- package/dist/net/buffer-pool.js +0 -89
- package/dist/net/client.js +0 -586
- package/dist/net/index.js +0 -58
- package/dist/net/server.js +0 -938
- package/dist/net/types.js +0 -31
- package/dist/net/validators.js +0 -88
- package/dist/protocol/index.js +0 -92
- package/dist/protocol/intent/define-intent.js +0 -125
- package/dist/protocol/intent/index.js +0 -91
- package/dist/protocol/intent/intent-registry.js +0 -91
- package/dist/protocol/rpc/define-rpc.js +0 -84
- package/dist/protocol/rpc/index.js +0 -3
- package/dist/protocol/rpc/rpc-registry.js +0 -159
- package/dist/protocol/rpc/rpc.js +0 -12
- package/dist/protocol/snapshot/index.js +0 -43
- package/dist/protocol/snapshot/snapshot-codec.js +0 -67
- package/dist/protocol/snapshot/snapshot-registry.js +0 -168
- package/dist/protocol/snapshot/snapshot.js +0 -30
- package/src/core/binary-codec/README.md +0 -60
- package/src/core/binary-codec/binary-codec.test.ts +0 -300
- package/src/core/binary-codec/binary-codec.ts +0 -448
- package/src/core/binary-codec/index.ts +0 -1
- package/src/core/events/README.md +0 -47
- package/src/core/events/event-system.test.ts +0 -243
- package/src/core/events/event-system.ts +0 -140
- package/src/core/events/index.ts +0 -1
- package/src/core/fixed-ticker/README.md +0 -77
- package/src/core/fixed-ticker/fixed-ticker.test.ts +0 -151
- package/src/core/fixed-ticker/fixed-ticker.ts +0 -169
- package/src/core/fixed-ticker/index.ts +0 -1
- package/src/core/generate-id/README.md +0 -18
- package/src/core/generate-id/generate-id.test.ts +0 -79
- package/src/core/generate-id/generate-id.ts +0 -37
- package/src/core/generate-id/index.ts +0 -1
- package/src/core/index.ts +0 -9
- package/src/core/lerp/README.md +0 -79
- package/src/core/lerp/index.ts +0 -1
- package/src/core/lerp/lerp.test.ts +0 -90
- package/src/core/lerp/lerp.ts +0 -42
- package/src/core/loop/README.md +0 -97
- package/src/core/loop/drivers/immediate.ts +0 -66
- package/src/core/loop/drivers/index.ts +0 -3
- package/src/core/loop/drivers/raf.ts +0 -67
- package/src/core/loop/drivers/timeout.ts +0 -77
- package/src/core/loop/index.ts +0 -2
- package/src/core/loop/loop.test.ts +0 -414
- package/src/core/loop/loop.ts +0 -71
- package/src/core/navmesh/README.md +0 -164
- package/src/core/navmesh/index.ts +0 -1
- package/src/core/navmesh/navmesh-worker-pool.ts +0 -236
- package/src/core/navmesh/navmesh-workers.test.ts +0 -356
- package/src/core/navmesh/navmesh.test.ts +0 -344
- package/src/core/navmesh/navmesh.ts +0 -1047
- package/src/core/navmesh/navmesh.worker.ts +0 -147
- package/src/core/pooled-codec/README.md +0 -70
- package/src/core/pooled-codec/index.ts +0 -1
- package/src/core/pooled-codec/pooled-codec.test.ts +0 -862
- package/src/core/pooled-codec/pooled-codec.ts +0 -504
- package/src/core/prediction/README.md +0 -64
- package/src/core/prediction/index.ts +0 -1
- package/src/core/prediction/prediction.test.ts +0 -423
- package/src/core/prediction/prediction.ts +0 -112
- package/src/ecs/README.md +0 -427
- package/src/ecs/benchmark.test.ts +0 -1645
- package/src/ecs/component-store.ts +0 -198
- package/src/ecs/component.ts +0 -90
- package/src/ecs/entity-handle.test.ts +0 -393
- package/src/ecs/entity-handle.ts +0 -563
- package/src/ecs/example.ts +0 -152
- package/src/ecs/index.ts +0 -4
- package/src/ecs/system-builder.ts +0 -309
- package/src/ecs/system.ts +0 -111
- package/src/ecs/world-systems.ts +0 -83
- package/src/ecs/world.test.ts +0 -310
- package/src/ecs/world.ts +0 -828
- package/src/index.ts +0 -28
- package/src/net/README.md +0 -474
- package/src/net/adapters/browser-websocket.ts +0 -86
- package/src/net/adapters/bun-websocket.ts +0 -292
- package/src/net/buffer-pool.ts +0 -106
- package/src/net/client.test.ts +0 -807
- package/src/net/client.ts +0 -695
- package/src/net/index.ts +0 -60
- package/src/net/server.test.ts +0 -799
- package/src/net/server.ts +0 -1116
- package/src/net/types.ts +0 -228
- package/src/net/validators.ts +0 -104
- package/src/protocol/README.md +0 -469
- package/src/protocol/index.ts +0 -93
- package/src/protocol/intent/define-intent.test.ts +0 -397
- package/src/protocol/intent/define-intent.ts +0 -182
- package/src/protocol/intent/index.ts +0 -94
- package/src/protocol/intent/intent-registry.test.ts +0 -198
- package/src/protocol/intent/intent-registry.ts +0 -112
- package/src/protocol/intent/intent.ts +0 -12
- package/src/protocol/rpc/define-rpc.test.ts +0 -141
- package/src/protocol/rpc/define-rpc.ts +0 -113
- package/src/protocol/rpc/index.ts +0 -3
- package/src/protocol/rpc/rpc-registry.test.ts +0 -168
- package/src/protocol/rpc/rpc-registry.ts +0 -176
- package/src/protocol/rpc/rpc.ts +0 -37
- package/src/protocol/snapshot/index.ts +0 -45
- package/src/protocol/snapshot/snapshot-codec.test.ts +0 -138
- package/src/protocol/snapshot/snapshot-codec.ts +0 -87
- package/src/protocol/snapshot/snapshot-registry.test.ts +0 -310
- package/src/protocol/snapshot/snapshot-registry.ts +0 -201
- package/src/protocol/snapshot/snapshot.test.ts +0 -76
- package/src/protocol/snapshot/snapshot.ts +0 -41
- /package/dist/{core → types/core}/binary-codec/index.d.ts +0 -0
- /package/dist/{core/loop/loop.d.ts → types/core/driver/driver.d.ts} +0 -0
- /package/dist/{core/loop → types/core/driver}/drivers/index.d.ts +0 -0
- /package/dist/{core → types/core}/events/index.d.ts +0 -0
- /package/dist/{core → types/core}/fixed-ticker/index.d.ts +0 -0
- /package/dist/{core → types/core}/generate-id/generate-id.d.ts +0 -0
- /package/dist/{core → types/core}/generate-id/index.d.ts +0 -0
- /package/dist/{core → types/core}/lerp/index.d.ts +0 -0
- /package/dist/{core → types/core}/lerp/lerp.d.ts +0 -0
- /package/dist/{core → types/core}/navmesh/index.d.ts +0 -0
- /package/dist/{core → types/core}/navmesh/navmesh-worker-pool.d.ts +0 -0
- /package/dist/{core → types/core}/navmesh/navmesh.worker.d.ts +0 -0
- /package/dist/{core → types/core}/pooled-codec/index.d.ts +0 -0
- /package/dist/{core → types/core}/pooled-codec/pooled-codec.d.ts +0 -0
- /package/dist/{core → types/core}/prediction/index.d.ts +0 -0
- /package/dist/{core → types/core}/prediction/prediction.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/component-store.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/component.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/entity-handle.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/example.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/index.d.ts +0 -0
- /package/dist/{ecs → types/ecs}/world-systems.d.ts +0 -0
- /package/dist/{net → types/net}/adapters/browser-websocket.d.ts +0 -0
- /package/dist/{net → types/net}/adapters/bun-websocket.d.ts +0 -0
- /package/dist/{net → types/net}/buffer-pool.d.ts +0 -0
- /package/dist/{net → types/net}/client.d.ts +0 -0
- /package/dist/{net → types/net}/types.d.ts +0 -0
- /package/dist/{net → types/net}/validators.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/index.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/intent/intent-registry.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/intent/intent.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/rpc/define-rpc.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/rpc/index.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/rpc/rpc-registry.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/rpc/rpc.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/snapshot/index.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/snapshot/snapshot-codec.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/snapshot/snapshot-registry.d.ts +0 -0
- /package/dist/{protocol → types/protocol}/snapshot/snapshot.d.ts +0 -0
- /package/dist/{protocol/intent/intent.js → webgpu/types/2d/animation.test.d.ts} +0 -0
package/src/ecs/world.ts
DELETED
|
@@ -1,828 +0,0 @@
|
|
|
1
|
-
import { generateId } from "../core/generate-id";
|
|
2
|
-
import { Component } from "./component";
|
|
3
|
-
import { ComponentStore } from "./component-store";
|
|
4
|
-
import { EntityHandle } from "./entity-handle";
|
|
5
|
-
import { WorldSystems } from "./world-systems";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Configuration for creating a World
|
|
9
|
-
*/
|
|
10
|
-
export interface WorldConfig {
|
|
11
|
-
/** Maximum number of entities that can exist simultaneously */
|
|
12
|
-
maxEntities?: number;
|
|
13
|
-
|
|
14
|
-
/** Component types to register */
|
|
15
|
-
components: Component<any>[];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Entity ID type (just a number, indexing into component arrays)
|
|
20
|
-
*/
|
|
21
|
-
export type Entity = number;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* World manages entities and their components.
|
|
25
|
-
* Provides efficient ECS storage using typed arrays.
|
|
26
|
-
*
|
|
27
|
-
* Performance optimizations:
|
|
28
|
-
* - Array iteration instead of Set for 2-5x faster queries
|
|
29
|
-
* - Query bitmask caching for repeated queries
|
|
30
|
-
* - Array-indexed component stores for O(1) access
|
|
31
|
-
* - Pre-allocated ring buffer for entity ID reuse
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```typescript
|
|
35
|
-
* const world = new World({
|
|
36
|
-
* maxEntities: 10000,
|
|
37
|
-
* components: [Transform, Health, Velocity]
|
|
38
|
-
* });
|
|
39
|
-
*
|
|
40
|
-
* const entity = world.spawn();
|
|
41
|
-
* world.add(entity, Transform, { x: 100, y: 200, rotation: 0 });
|
|
42
|
-
* world.add(entity, Health, { current: 100, max: 100 });
|
|
43
|
-
*
|
|
44
|
-
* // Query entities
|
|
45
|
-
* for (const entity of world.query(Transform, Velocity)) {
|
|
46
|
-
* const transform = world.get(entity, Transform);
|
|
47
|
-
* const velocity = world.get(entity, Velocity);
|
|
48
|
-
* // transform is readonly, use update() to modify
|
|
49
|
-
* world.update(entity, Transform, {
|
|
50
|
-
* x: transform.x + velocity.vx,
|
|
51
|
-
* y: transform.y + velocity.vy
|
|
52
|
-
* });
|
|
53
|
-
* }
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
export class World extends WorldSystems {
|
|
57
|
-
private maxEntities: number;
|
|
58
|
-
private nextEntityId: number = 0;
|
|
59
|
-
|
|
60
|
-
// Entity ID reuse (ring buffer for O(1) push/pop)
|
|
61
|
-
private freeEntityIds: Uint32Array;
|
|
62
|
-
private freeEntityHead: number = 0;
|
|
63
|
-
private freeEntityTail: number = 0;
|
|
64
|
-
private freeEntityCount: number = 0;
|
|
65
|
-
private freeEntityMask: number = 0; // Bitwise AND mask for power-of-2 modulo
|
|
66
|
-
|
|
67
|
-
// Entity storage: Array for fast iteration, bitmask for O(1) alive checks
|
|
68
|
-
private aliveEntitiesArray: Entity[] = [];
|
|
69
|
-
private aliveEntitiesIndices: Uint32Array; // Index lookup for O(1) despawn
|
|
70
|
-
private aliveEntityFlags: Uint8Array; // 1 byte per entity for alive check
|
|
71
|
-
|
|
72
|
-
// Component system (array-indexed for O(1) access)
|
|
73
|
-
public componentStoresArray: (ComponentStore<any> | undefined)[];
|
|
74
|
-
private componentMasks: Uint32Array[]; // Dynamic array of bitmask words (32 components per word)
|
|
75
|
-
private componentMasks0!: Uint32Array; // Fast path: cached reference to first word (most common case)
|
|
76
|
-
private numMaskWords: number = 0; // Number of allocated mask words
|
|
77
|
-
|
|
78
|
-
// Component registry (direct index stored on component - zero lookup cost!)
|
|
79
|
-
private components: Component<any>[] = [];
|
|
80
|
-
|
|
81
|
-
// Query result cache (reusable buffers for zero allocations)
|
|
82
|
-
private queryResultBuffers: Record<string, Entity[]> = {}; // Now keyed by string hash
|
|
83
|
-
|
|
84
|
-
// Persistent query cache (invalidated only on archetype changes)
|
|
85
|
-
private archetypeVersion: number = 0; // Increments on spawn/despawn/add/remove
|
|
86
|
-
private queryCacheVersions: Record<string, number> = {}; // Keyed by mask hash
|
|
87
|
-
|
|
88
|
-
// Query mask cache (avoid recomputing masks for same component combinations)
|
|
89
|
-
private queryMaskCache: Record<string, number[]> = {};
|
|
90
|
-
|
|
91
|
-
// Debug ID
|
|
92
|
-
private worldId = generateId({ prefix: "world_" });
|
|
93
|
-
|
|
94
|
-
constructor(config: WorldConfig) {
|
|
95
|
-
super();
|
|
96
|
-
this.maxEntities = config.maxEntities ?? 10000;
|
|
97
|
-
|
|
98
|
-
// Calculate number of mask words needed (1 word per 32 components)
|
|
99
|
-
this.numMaskWords = Math.ceil(config.components.length / 32);
|
|
100
|
-
|
|
101
|
-
// Allocate separate Uint32Array for each mask word
|
|
102
|
-
this.componentMasks = [];
|
|
103
|
-
for (let i = 0; i < this.numMaskWords; i++) {
|
|
104
|
-
this.componentMasks.push(new Uint32Array(this.maxEntities));
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Cache first word for fast path (most games use <32 components)
|
|
108
|
-
if (this.numMaskWords > 0) {
|
|
109
|
-
this.componentMasks0 = this.componentMasks[0];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Round up to next power of 2 for ring buffer (enables bitwise modulo)
|
|
113
|
-
const ringBufferSize = Math.pow(2, Math.ceil(Math.log2(this.maxEntities)));
|
|
114
|
-
this.freeEntityIds = new Uint32Array(ringBufferSize);
|
|
115
|
-
this.freeEntityMask = ringBufferSize - 1; // For x % size → x & mask
|
|
116
|
-
|
|
117
|
-
// Pre-allocate index lookup for O(1) despawn
|
|
118
|
-
this.aliveEntitiesIndices = new Uint32Array(this.maxEntities);
|
|
119
|
-
|
|
120
|
-
// Pre-allocate alive flags for O(1) alive checks
|
|
121
|
-
this.aliveEntityFlags = new Uint8Array(this.maxEntities);
|
|
122
|
-
|
|
123
|
-
// Pre-allocate arrays for component stores
|
|
124
|
-
this.componentStoresArray = new Array(config.components.length);
|
|
125
|
-
|
|
126
|
-
// Register components
|
|
127
|
-
config.components.forEach((component, index) => {
|
|
128
|
-
this.components.push(component);
|
|
129
|
-
// Store index directly on component for O(1) access (no Map lookup!)
|
|
130
|
-
component.__worldIndex = index;
|
|
131
|
-
|
|
132
|
-
// Create component store with selected backend
|
|
133
|
-
const store = new ComponentStore(component, this.maxEntities);
|
|
134
|
-
this.componentStoresArray[index] = store;
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Get component index (O(1) - stored directly on component)
|
|
140
|
-
*/
|
|
141
|
-
private getComponentIndex(component: Component<any>): number {
|
|
142
|
-
const index = component.__worldIndex;
|
|
143
|
-
if (index === undefined) {
|
|
144
|
-
const registered = this.components.map((c) => c.name).join(", ");
|
|
145
|
-
throw new Error(
|
|
146
|
-
`Component ${component.name} not registered in World[${this.worldId}]. ` +
|
|
147
|
-
`Registered components: [${registered}]. ` +
|
|
148
|
-
`Did you forget to include it in the WorldConfig?`
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
return index;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Set a bit in the bitmask for an entity
|
|
156
|
-
*/
|
|
157
|
-
private setComponentBit(entity: Entity, componentIndex: number): void {
|
|
158
|
-
const wordIndex = componentIndex >>> 5; // Which word (div 32)
|
|
159
|
-
const bitIndex = componentIndex & 31; // Which bit in word (mod 32)
|
|
160
|
-
this.componentMasks[wordIndex][entity] |= 1 << bitIndex;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Clear a bit in the bitmask for an entity
|
|
165
|
-
*/
|
|
166
|
-
private clearComponentBit(entity: Entity, componentIndex: number): void {
|
|
167
|
-
const wordIndex = componentIndex >>> 5; // Which word (div 32)
|
|
168
|
-
const bitIndex = componentIndex & 31; // Which bit in word (mod 32)
|
|
169
|
-
this.componentMasks[wordIndex][entity] &= ~(1 << bitIndex);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Check if a bit is set in the bitmask for an entity
|
|
174
|
-
*/
|
|
175
|
-
private hasComponentBit(entity: Entity, componentIndex: number): boolean {
|
|
176
|
-
const wordIndex = componentIndex >>> 5; // Which word (div 32)
|
|
177
|
-
const bitIndex = componentIndex & 31; // Which bit in word (mod 32)
|
|
178
|
-
return (this.componentMasks[wordIndex][entity] & (1 << bitIndex)) !== 0;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Clear all component bits for an entity
|
|
183
|
-
*/
|
|
184
|
-
private clearAllComponentBits(entity: Entity): void {
|
|
185
|
-
for (let i = 0; i < this.numMaskWords; i++) {
|
|
186
|
-
this.componentMasks[i][entity] = 0;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Check if entity matches the required component mask
|
|
192
|
-
* Returns true if entity has all required components
|
|
193
|
-
*
|
|
194
|
-
* Optimized for common case: most games use <32 components,
|
|
195
|
-
* so we only need to check the first word
|
|
196
|
-
*/
|
|
197
|
-
private matchesComponentMask(entity: Entity, mask: number[]): boolean {
|
|
198
|
-
const len = mask.length;
|
|
199
|
-
|
|
200
|
-
// Fast path: single word (most common - <32 components)
|
|
201
|
-
if (len === 1) {
|
|
202
|
-
return (this.componentMasks0[entity] & mask[0]) === mask[0];
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Unrolled for 2 words (32-63 components)
|
|
206
|
-
if (len === 2) {
|
|
207
|
-
return (this.componentMasks0[entity] & mask[0]) === mask[0] &&
|
|
208
|
-
(this.componentMasks[1][entity] & mask[1]) === mask[1];
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Unrolled for 3 words (64-95 components)
|
|
212
|
-
if (len === 3) {
|
|
213
|
-
return (this.componentMasks0[entity] & mask[0]) === mask[0] &&
|
|
214
|
-
(this.componentMasks[1][entity] & mask[1]) === mask[1] &&
|
|
215
|
-
(this.componentMasks[2][entity] & mask[2]) === mask[2];
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// Unrolled for 4 words (96-127 components)
|
|
219
|
-
if (len === 4) {
|
|
220
|
-
return (this.componentMasks0[entity] & mask[0]) === mask[0] &&
|
|
221
|
-
(this.componentMasks[1][entity] & mask[1]) === mask[1] &&
|
|
222
|
-
(this.componentMasks[2][entity] & mask[2]) === mask[2] &&
|
|
223
|
-
(this.componentMasks[3][entity] & mask[3]) === mask[3];
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// General case for 5+ words (rare)
|
|
227
|
-
for (let i = 0; i < len; i++) {
|
|
228
|
-
if ((this.componentMasks[i][entity] & mask[i]) !== mask[i]) {
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
return true;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Get or compute query bitmask
|
|
237
|
-
* Returns array of numbers (one 32-bit mask per word)
|
|
238
|
-
*
|
|
239
|
-
* Caches masks to avoid recomputation for frequently used component combinations
|
|
240
|
-
*/
|
|
241
|
-
private getQueryMask(components: Component<any>[]): number[] | null {
|
|
242
|
-
// Create cache key from component names (sorted for consistency)
|
|
243
|
-
const cacheKey = components.map(c => c.name).sort().join(',');
|
|
244
|
-
|
|
245
|
-
// Check cache first
|
|
246
|
-
const cached = this.queryMaskCache[cacheKey];
|
|
247
|
-
if (cached) return cached;
|
|
248
|
-
|
|
249
|
-
// Find max component index to determine how many words we need
|
|
250
|
-
let maxIndex = -1;
|
|
251
|
-
const indices: number[] = [];
|
|
252
|
-
|
|
253
|
-
for (const component of components) {
|
|
254
|
-
const index = component.__worldIndex;
|
|
255
|
-
if (index === undefined) return null; // Invalid mask sentinel
|
|
256
|
-
indices.push(index);
|
|
257
|
-
if (index > maxIndex) maxIndex = index;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Calculate number of words needed
|
|
261
|
-
const numWords = Math.floor(maxIndex / 32) + 1;
|
|
262
|
-
const requiredMask: number[] = new Array(numWords).fill(0);
|
|
263
|
-
|
|
264
|
-
// Set bits for each component (direct index access - no lookups!)
|
|
265
|
-
for (const index of indices) {
|
|
266
|
-
const wordIndex = index >>> 5; // div 32
|
|
267
|
-
const bitIndex = index & 31; // mod 32
|
|
268
|
-
requiredMask[wordIndex] |= 1 << bitIndex;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Cache the mask for future queries
|
|
272
|
-
this.queryMaskCache[cacheKey] = requiredMask;
|
|
273
|
-
|
|
274
|
-
return requiredMask;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Convert mask array to a hash key for caching
|
|
279
|
-
*/
|
|
280
|
-
private maskToKey(mask: number[]): string {
|
|
281
|
-
let key = '';
|
|
282
|
-
for (let i = 0; i < mask.length; i++) {
|
|
283
|
-
if (mask[i] !== 0) {
|
|
284
|
-
key += `${i}:${mask[i].toString(36)},`;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return key;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Internal: Get query mask key for a set of components.
|
|
292
|
-
* Used by SystemBuilder for precomputing query keys.
|
|
293
|
-
* @internal
|
|
294
|
-
*/
|
|
295
|
-
private _getQueryMaskKey(components: Component<any>[]): string {
|
|
296
|
-
const mask = this.getQueryMask(components);
|
|
297
|
-
return mask ? this.maskToKey(mask) : '';
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Internal: Query entities by precomputed mask key and mask.
|
|
302
|
-
* Used by ExecutableSystem for fast queries without mask recomputation.
|
|
303
|
-
* @internal
|
|
304
|
-
*/
|
|
305
|
-
private _queryByMaskKey(maskKey: string, requiredMask: number[]): readonly Entity[] {
|
|
306
|
-
// Get or create reusable buffer for this query mask
|
|
307
|
-
let buffer = this.queryResultBuffers[maskKey];
|
|
308
|
-
if (!buffer) {
|
|
309
|
-
buffer = [];
|
|
310
|
-
this.queryResultBuffers[maskKey] = buffer;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Check if cache is valid (persistent query caching)
|
|
314
|
-
if (this.queryCacheVersions[maskKey] === this.archetypeVersion) {
|
|
315
|
-
// Cache is valid! Return cached result (FAST PATH - no iteration!)
|
|
316
|
-
return buffer;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Cache is stale - rebuild by iterating alive entities
|
|
320
|
-
const aliveEntities = this.aliveEntitiesArray;
|
|
321
|
-
const length = aliveEntities.length;
|
|
322
|
-
const numWords = requiredMask.length;
|
|
323
|
-
let writeIdx = 0;
|
|
324
|
-
|
|
325
|
-
// Fast path for single-word masks (most common case, <32 components)
|
|
326
|
-
if (numWords === 1) {
|
|
327
|
-
const mask0 = requiredMask[0];
|
|
328
|
-
const componentMasks0 = this.componentMasks0;
|
|
329
|
-
for (let i = 0; i < length; i++) {
|
|
330
|
-
const entity = aliveEntities[i]!;
|
|
331
|
-
if ((componentMasks0[entity] & mask0) === mask0) {
|
|
332
|
-
buffer[writeIdx++] = entity;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
} else {
|
|
336
|
-
// Multi-word mask path
|
|
337
|
-
const componentMasks = this.componentMasks;
|
|
338
|
-
outer: for (let i = 0; i < length; i++) {
|
|
339
|
-
const entity = aliveEntities[i]!;
|
|
340
|
-
for (let w = 0; w < numWords; w++) {
|
|
341
|
-
if ((componentMasks[w][entity] & requiredMask[w]) !== requiredMask[w]) {
|
|
342
|
-
continue outer;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
buffer[writeIdx++] = entity;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Truncate buffer to actual size (zero allocations)
|
|
350
|
-
buffer.length = writeIdx;
|
|
351
|
-
|
|
352
|
-
// Mark cache as valid for this archetype version
|
|
353
|
-
this.queryCacheVersions[maskKey] = this.archetypeVersion;
|
|
354
|
-
|
|
355
|
-
return buffer;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* Spawn a new entity.
|
|
360
|
-
* Returns the entity ID.
|
|
361
|
-
*/
|
|
362
|
-
spawn(): Entity {
|
|
363
|
-
// Hot path: allocate new ID (most common case, no branching)
|
|
364
|
-
let id = this.nextEntityId;
|
|
365
|
-
|
|
366
|
-
// Cold path: reuse freed ID if available
|
|
367
|
-
if (this.freeEntityCount > 0) {
|
|
368
|
-
id = this.freeEntityIds[this.freeEntityTail];
|
|
369
|
-
this.freeEntityTail = (this.freeEntityTail + 1) & this.freeEntityMask;
|
|
370
|
-
this.freeEntityCount--;
|
|
371
|
-
} else {
|
|
372
|
-
this.nextEntityId++;
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
// Bounds check (unlikely to fail in normal operation)
|
|
376
|
-
if (id >= this.maxEntities) {
|
|
377
|
-
throw new Error(
|
|
378
|
-
`Maximum entities (${this.maxEntities}) reached. ` +
|
|
379
|
-
`Current alive: ${this.aliveEntitiesArray.length}, ` +
|
|
380
|
-
`Free list: ${this.freeEntityCount}`
|
|
381
|
-
);
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
// Fast path: setup entity (no branches)
|
|
385
|
-
this.aliveEntityFlags[id] = 1;
|
|
386
|
-
this.aliveEntitiesIndices[id] = this.aliveEntitiesArray.length;
|
|
387
|
-
this.aliveEntitiesArray.push(id);
|
|
388
|
-
this.clearAllComponentBits(id);
|
|
389
|
-
|
|
390
|
-
// Invalidate query cache since entity count changed
|
|
391
|
-
this.invalidateQueryCache();
|
|
392
|
-
|
|
393
|
-
return id;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Despawn an entity, removing all its components.
|
|
398
|
-
* The entity ID will be reused.
|
|
399
|
-
*/
|
|
400
|
-
despawn(entity: Entity): void {
|
|
401
|
-
if (this.aliveEntityFlags[entity] === 0) {
|
|
402
|
-
return; // Already despawned
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
this.aliveEntityFlags[entity] = 0;
|
|
406
|
-
|
|
407
|
-
// Remove from array (swap with last for O(1) removal)
|
|
408
|
-
const idx = this.aliveEntitiesIndices[entity];
|
|
409
|
-
const last = this.aliveEntitiesArray.length - 1;
|
|
410
|
-
|
|
411
|
-
if (idx !== last) {
|
|
412
|
-
// Swap with last element
|
|
413
|
-
const lastEntity = this.aliveEntitiesArray[last];
|
|
414
|
-
this.aliveEntitiesArray[idx] = lastEntity;
|
|
415
|
-
this.aliveEntitiesIndices[lastEntity] = idx;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
this.aliveEntitiesArray.pop();
|
|
419
|
-
|
|
420
|
-
// Clear all components for this entity
|
|
421
|
-
for (let i = 0; i < this.components.length; i++) {
|
|
422
|
-
if (this.hasComponentBit(entity, i)) {
|
|
423
|
-
this.componentStoresArray[i]!.clear(entity);
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
this.clearAllComponentBits(entity);
|
|
428
|
-
|
|
429
|
-
// Push to free list
|
|
430
|
-
this.freeEntityIds[this.freeEntityHead] = entity;
|
|
431
|
-
this.freeEntityHead = (this.freeEntityHead + 1) & this.freeEntityMask; // Bitwise AND instead of modulo
|
|
432
|
-
this.freeEntityCount++;
|
|
433
|
-
|
|
434
|
-
// Invalidate query cache since entity count changed
|
|
435
|
-
this.invalidateQueryCache();
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/**
|
|
439
|
-
* Check if an entity is alive
|
|
440
|
-
*/
|
|
441
|
-
isAlive(entity: Entity): boolean {
|
|
442
|
-
return this.aliveEntityFlags[entity] === 1;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* Invalidate all query caches (called on archetype changes).
|
|
447
|
-
*/
|
|
448
|
-
private invalidateQueryCache(): void {
|
|
449
|
-
this.archetypeVersion++;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
* Add a component to an entity with initial data.
|
|
454
|
-
*/
|
|
455
|
-
add<T extends object>(entity: Entity, component: Component<T>, data: T): void {
|
|
456
|
-
if (this.aliveEntityFlags[entity] === 0) {
|
|
457
|
-
throw new Error(
|
|
458
|
-
`Cannot add component ${component.name} to entity ${entity}: ` +
|
|
459
|
-
`entity is not alive (was it despawned?). ` +
|
|
460
|
-
`Current alive entities: ${this.aliveEntitiesArray.length}`
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
const index = this.getComponentIndex(component);
|
|
465
|
-
const store = this.componentStoresArray[index]!;
|
|
466
|
-
|
|
467
|
-
this.setComponentBit(entity, index);
|
|
468
|
-
store.set(entity, data);
|
|
469
|
-
|
|
470
|
-
// Invalidate query cache since archetype changed
|
|
471
|
-
this.invalidateQueryCache();
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Remove a component from an entity.
|
|
476
|
-
*/
|
|
477
|
-
remove<T extends object>(entity: Entity, component: Component<T>): void {
|
|
478
|
-
const index = component.__worldIndex;
|
|
479
|
-
if (index === undefined) return;
|
|
480
|
-
|
|
481
|
-
this.clearComponentBit(entity, index);
|
|
482
|
-
|
|
483
|
-
const store = this.componentStoresArray[index];
|
|
484
|
-
if (store) {
|
|
485
|
-
store.clear(entity);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
// Invalidate query cache since archetype changed
|
|
489
|
-
this.invalidateQueryCache();
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Check if an entity has a component.
|
|
494
|
-
*/
|
|
495
|
-
has<T extends object>(entity: Entity, component: Component<T>): boolean {
|
|
496
|
-
const index = component.__worldIndex;
|
|
497
|
-
if (index === undefined) return false;
|
|
498
|
-
|
|
499
|
-
return this.hasComponentBit(entity, index);
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* Get a component's data for an entity.
|
|
504
|
-
* Returns a READONLY reusable object (zero allocations).
|
|
505
|
-
*
|
|
506
|
-
* ⚠️ IMPORTANT: The returned object is reused and will be overwritten on the next get().
|
|
507
|
-
* To modify, use set() or update() instead.
|
|
508
|
-
* To keep multiple components, use getMutable() or spread operator.
|
|
509
|
-
*
|
|
510
|
-
* @example
|
|
511
|
-
* // ✅ CORRECT: Use immediately
|
|
512
|
-
* const t = world.get(entity, Transform);
|
|
513
|
-
* console.log(t.x, t.y);
|
|
514
|
-
*
|
|
515
|
-
* // ❌ WRONG: Storing reference
|
|
516
|
-
* const t1 = world.get(entity1, Transform);
|
|
517
|
-
* const t2 = world.get(entity2, Transform); // t1 is now corrupted!
|
|
518
|
-
*
|
|
519
|
-
* // ✅ CORRECT: Copy if you need to keep
|
|
520
|
-
* const t1 = { ...world.get(entity1, Transform) };
|
|
521
|
-
* const t2 = { ...world.get(entity2, Transform) };
|
|
522
|
-
*/
|
|
523
|
-
get<T extends object>(entity: Entity, component: Component<T>): Readonly<T> {
|
|
524
|
-
const index = this.getComponentIndex(component);
|
|
525
|
-
|
|
526
|
-
if (!this.hasComponentBit(entity, index)) {
|
|
527
|
-
const entityComponents = this.getEntityComponentNames(entity);
|
|
528
|
-
throw new Error(
|
|
529
|
-
`Cannot get component ${component.name} from entity ${entity}: ` +
|
|
530
|
-
`entity does not have this component. ` +
|
|
531
|
-
`Entity has: [${entityComponents.join(", ")}]. ` +
|
|
532
|
-
`Did you forget to call world.add()?`
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
return this.componentStoresArray[index]!.get(entity);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
/**
|
|
540
|
-
* Get a mutable copy of component data.
|
|
541
|
-
* Use this when you need to modify and keep the data.
|
|
542
|
-
*
|
|
543
|
-
* Note: This allocates a new object. Use sparingly in hot paths.
|
|
544
|
-
*/
|
|
545
|
-
getMutable<T extends object>(entity: Entity, component: Component<T>): T {
|
|
546
|
-
const index = this.getComponentIndex(component);
|
|
547
|
-
|
|
548
|
-
if (!this.hasComponentBit(entity, index)) {
|
|
549
|
-
throw new Error(`Entity ${entity} does not have component ${component.name}`);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
return this.componentStoresArray[index]!.getMutable(entity);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* Set a component's data for an entity.
|
|
557
|
-
* Overwrites all fields.
|
|
558
|
-
*/
|
|
559
|
-
set<T extends object>(entity: Entity, component: Component<T>, data: T): void {
|
|
560
|
-
const index = this.getComponentIndex(component);
|
|
561
|
-
|
|
562
|
-
if (!this.hasComponentBit(entity, index)) {
|
|
563
|
-
throw new Error(
|
|
564
|
-
`Cannot set component ${component.name} on entity ${entity}: ` +
|
|
565
|
-
`entity does not have this component. Use add() first.`
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
this.componentStoresArray[index]!.set(entity, data);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
/**
|
|
573
|
-
* Update specific fields of a component.
|
|
574
|
-
* More efficient than get + modify + set.
|
|
575
|
-
*
|
|
576
|
-
* @example
|
|
577
|
-
* // ✅ GOOD: Partial update
|
|
578
|
-
* world.update(entity, Transform, { x: 150 });
|
|
579
|
-
*
|
|
580
|
-
* // ❌ BAD: Full get/set for single field
|
|
581
|
-
* const t = world.getMutable(entity, Transform);
|
|
582
|
-
* t.x = 150;
|
|
583
|
-
* world.set(entity, Transform, t);
|
|
584
|
-
*/
|
|
585
|
-
update<T extends object>(entity: Entity, component: Component<T>, partial: Partial<T>): void {
|
|
586
|
-
const index = this.getComponentIndex(component);
|
|
587
|
-
|
|
588
|
-
if (!this.hasComponentBit(entity, index)) {
|
|
589
|
-
throw new Error(`Entity ${entity} does not have component ${component.name}`);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
this.componentStoresArray[index]!.update(entity, partial);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* Query entities that have all specified components.
|
|
597
|
-
* Returns a readonly array for zero-allocation iteration.
|
|
598
|
-
*
|
|
599
|
-
* Uses reusable buffers and direct bitmask checks for maximum performance.
|
|
600
|
-
* The returned array is reused on subsequent queries with the same mask.
|
|
601
|
-
*
|
|
602
|
-
* @example
|
|
603
|
-
* ```typescript
|
|
604
|
-
* for (const entity of world.query(Transform, Velocity)) {
|
|
605
|
-
* const t = world.get(entity, Transform);
|
|
606
|
-
* const v = world.get(entity, Velocity);
|
|
607
|
-
* world.update(entity, Transform, {
|
|
608
|
-
* x: t.x + v.vx * dt,
|
|
609
|
-
* y: t.y + v.vy * dt
|
|
610
|
-
* });
|
|
611
|
-
* }
|
|
612
|
-
* ```
|
|
613
|
-
*/
|
|
614
|
-
query(...components: Component<any>[]): readonly Entity[] {
|
|
615
|
-
const requiredMask = this.getQueryMask(components);
|
|
616
|
-
if (requiredMask === null) return []; // Component not registered
|
|
617
|
-
|
|
618
|
-
const maskKey = this.maskToKey(requiredMask);
|
|
619
|
-
|
|
620
|
-
// Get or create reusable buffer for this query mask
|
|
621
|
-
let buffer = this.queryResultBuffers[maskKey];
|
|
622
|
-
if (!buffer) {
|
|
623
|
-
buffer = [];
|
|
624
|
-
this.queryResultBuffers[maskKey] = buffer;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// Check if cache is valid (persistent query caching)
|
|
628
|
-
if (this.queryCacheVersions[maskKey] === this.archetypeVersion) {
|
|
629
|
-
// Cache is valid! Return cached result (FAST PATH - no iteration!)
|
|
630
|
-
return buffer;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// Cache miss or stale - recompute query results
|
|
634
|
-
const entities = this.aliveEntitiesArray;
|
|
635
|
-
const length = entities.length;
|
|
636
|
-
|
|
637
|
-
// Use write cursor pattern instead of buffer.length = 0 + push
|
|
638
|
-
let writeIdx = 0;
|
|
639
|
-
|
|
640
|
-
// Process entities (checking 128-bit bitmask: 4 x 32-bit words)
|
|
641
|
-
for (let i = 0; i < length; i++) {
|
|
642
|
-
const entity = entities[i];
|
|
643
|
-
if (this.matchesComponentMask(entity, requiredMask)) {
|
|
644
|
-
buffer[writeIdx++] = entity;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
// Truncate buffer to actual size
|
|
649
|
-
buffer.length = writeIdx;
|
|
650
|
-
|
|
651
|
-
// Mark cache as valid for this archetype version
|
|
652
|
-
this.queryCacheVersions[maskKey] = this.archetypeVersion;
|
|
653
|
-
|
|
654
|
-
return buffer;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
* Get all alive entity IDs.
|
|
659
|
-
*
|
|
660
|
-
* ⚠️ WARNING: The returned array is a direct reference and should not be modified.
|
|
661
|
-
* For a safe copy, use [...world.getEntities()].
|
|
662
|
-
*/
|
|
663
|
-
getEntities(): readonly Entity[] {
|
|
664
|
-
return this.aliveEntitiesArray;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
/**
|
|
668
|
-
* Get the number of alive entities.
|
|
669
|
-
*/
|
|
670
|
-
getEntityCount(): number {
|
|
671
|
-
return this.aliveEntitiesArray.length;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* Get the maximum number of entities.
|
|
676
|
-
*/
|
|
677
|
-
getMaxEntities(): number {
|
|
678
|
-
return this.maxEntities;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* Get all registered components.
|
|
683
|
-
*/
|
|
684
|
-
getComponents(): readonly Component<any>[] {
|
|
685
|
-
return this.components;
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/**
|
|
689
|
-
* Get component names for an entity (for debugging)
|
|
690
|
-
*/
|
|
691
|
-
private getEntityComponentNames(entity: Entity): string[] {
|
|
692
|
-
const result: string[] = [];
|
|
693
|
-
|
|
694
|
-
for (let i = 0; i < this.components.length; i++) {
|
|
695
|
-
if (this.hasComponentBit(entity, i)) {
|
|
696
|
-
result.push(this.components[i].name);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
|
-
return result;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/**
|
|
704
|
-
* Serialize entities with specific components to binary.
|
|
705
|
-
* Uses PooledCodec internally for efficient encoding.
|
|
706
|
-
*
|
|
707
|
-
* @param components Components to include in the snapshot
|
|
708
|
-
* @param entities Optional list of entities to serialize (defaults to all)
|
|
709
|
-
* @returns Binary buffer with serialized data
|
|
710
|
-
*/
|
|
711
|
-
serialize(components: Component<any>[], entities?: Entity[]): Uint8Array {
|
|
712
|
-
const entityList = entities ?? Array.from(this.aliveEntitiesArray);
|
|
713
|
-
|
|
714
|
-
// Build data structure for each component
|
|
715
|
-
const componentArrays: any[] = [];
|
|
716
|
-
|
|
717
|
-
for (const component of components) {
|
|
718
|
-
const index = component.__worldIndex;
|
|
719
|
-
if (index === undefined) continue;
|
|
720
|
-
|
|
721
|
-
const store = this.componentStoresArray[index];
|
|
722
|
-
if (!store) continue;
|
|
723
|
-
|
|
724
|
-
const items: any[] = [];
|
|
725
|
-
|
|
726
|
-
for (const entity of entityList) {
|
|
727
|
-
if (this.has(entity, component)) {
|
|
728
|
-
items.push({
|
|
729
|
-
entity,
|
|
730
|
-
...store.getMutable(entity),
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
if (items.length > 0) {
|
|
736
|
-
// Use the component's arrayCodec (PooledCodec.array) to encode
|
|
737
|
-
const encoded = component.arrayCodec.encode(items);
|
|
738
|
-
componentArrays.push(encoded);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
// Combine all buffers
|
|
743
|
-
// TODO: Could optimize this with a proper multi-buffer format
|
|
744
|
-
const totalSize = componentArrays.reduce((sum, buf) => sum + buf.length, 0);
|
|
745
|
-
const result = new Uint8Array(totalSize);
|
|
746
|
-
let offset = 0;
|
|
747
|
-
for (const buf of componentArrays) {
|
|
748
|
-
result.set(buf, offset);
|
|
749
|
-
offset += buf.length;
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
return result;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
/**
|
|
756
|
-
* Deserialize binary data into entities.
|
|
757
|
-
* Uses PooledCodec internally for efficient decoding.
|
|
758
|
-
*
|
|
759
|
-
* Note: This is a basic implementation. For production use,
|
|
760
|
-
* you'd want a more sophisticated format with component IDs, etc.
|
|
761
|
-
*/
|
|
762
|
-
deserialize(components: Component<any>[], buffer: Uint8Array): void {
|
|
763
|
-
// TODO: Implement proper deserialization with component IDs
|
|
764
|
-
// For now, this is a placeholder
|
|
765
|
-
throw new Error("Deserialization not yet implemented");
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
/**
|
|
769
|
-
* Get direct access to a component field's TypedArray for maximum performance.
|
|
770
|
-
* This bypasses the get/update API for ~3-4x faster access in hot paths.
|
|
771
|
-
*
|
|
772
|
-
* ⚠️ ADVANCED API: Use with caution!
|
|
773
|
-
* - No bounds checking
|
|
774
|
-
* - No type safety
|
|
775
|
-
* - You must ensure entities have the component
|
|
776
|
-
* - Direct array mutation bypasses any safety mechanisms
|
|
777
|
-
*
|
|
778
|
-
* @example
|
|
779
|
-
* ```typescript
|
|
780
|
-
* // High-performance system (bitECS-style)
|
|
781
|
-
* const transformX = world.getFieldArray(Transform, 'x');
|
|
782
|
-
* const transformY = world.getFieldArray(Transform, 'y');
|
|
783
|
-
* const velocityVx = world.getFieldArray(Velocity, 'vx');
|
|
784
|
-
* const velocityVy = world.getFieldArray(Velocity, 'vy');
|
|
785
|
-
*
|
|
786
|
-
* for (const entity of world.query(Transform, Velocity)) {
|
|
787
|
-
* transformX[entity] += velocityVx[entity] * dt;
|
|
788
|
-
* transformY[entity] += velocityVy[entity] * dt;
|
|
789
|
-
* }
|
|
790
|
-
* ```
|
|
791
|
-
*/
|
|
792
|
-
getFieldArray<T extends object>(
|
|
793
|
-
component: Component<T>,
|
|
794
|
-
fieldName: keyof T
|
|
795
|
-
): Float32Array | Int32Array | Uint32Array | Uint16Array | Uint8Array {
|
|
796
|
-
const index = this.getComponentIndex(component);
|
|
797
|
-
return this.componentStoresArray[index]!.getFieldArray(fieldName);
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/**
|
|
801
|
-
* Create an EntityHandle wrapper for fluent API usage.
|
|
802
|
-
*
|
|
803
|
-
* EntityHandle provides a chainable interface for entity operations with zero runtime overhead.
|
|
804
|
-
* Modern JIT compilers inline these simple method calls, making them identical to raw World API.
|
|
805
|
-
*
|
|
806
|
-
* @param entityId - Entity ID to wrap
|
|
807
|
-
* @returns EntityHandle for fluent operations
|
|
808
|
-
*
|
|
809
|
-
* @example
|
|
810
|
-
* ```typescript
|
|
811
|
-
* // Fluent API with chaining
|
|
812
|
-
* const player = world.entity(world.spawn())
|
|
813
|
-
* .add(Transform, { x: 0, y: 0, rotation: 0 })
|
|
814
|
-
* .add(Health, { current: 100, max: 100 })
|
|
815
|
-
* .add(Velocity, { vx: 0, vy: 0 });
|
|
816
|
-
*
|
|
817
|
-
* // Use the handle
|
|
818
|
-
* player.update(Transform, { x: 10 });
|
|
819
|
-
* const health = player.get(Health);
|
|
820
|
-
*
|
|
821
|
-
* // Mix with raw API
|
|
822
|
-
* world.add(player.id, Armor, { value: 50 });
|
|
823
|
-
* ```
|
|
824
|
-
*/
|
|
825
|
-
entity(entityId: Entity): EntityHandle {
|
|
826
|
-
return new EntityHandle(this, entityId);
|
|
827
|
-
}
|
|
828
|
-
}
|