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/net/client.test.ts
DELETED
|
@@ -1,807 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test, beforeEach } from "bun:test";
|
|
2
|
-
import { ClientNetwork } from "./client";
|
|
3
|
-
import { IntentRegistry } from "../protocol/intent/intent-registry";
|
|
4
|
-
import { SnapshotRegistry } from "../protocol/snapshot/snapshot-registry";
|
|
5
|
-
import { RpcRegistry } from "../protocol/rpc/rpc-registry";
|
|
6
|
-
import { PooledCodec } from "../core/pooled-codec/pooled-codec";
|
|
7
|
-
import { BinaryPrimitives } from "../core/binary-codec";
|
|
8
|
-
import { defineIntent } from "../protocol/intent/define-intent";
|
|
9
|
-
import { defineRPC } from "../protocol/rpc/define-rpc";
|
|
10
|
-
import type { TransportAdapter } from "./types";
|
|
11
|
-
import { MessageType } from "./types";
|
|
12
|
-
import type { Snapshot } from "../protocol/snapshot/snapshot";
|
|
13
|
-
|
|
14
|
-
// Define move intent using defineIntent
|
|
15
|
-
const MoveIntent = defineIntent({
|
|
16
|
-
kind: 1 as const,
|
|
17
|
-
schema: {
|
|
18
|
-
dx: BinaryPrimitives.f32,
|
|
19
|
-
dy: BinaryPrimitives.f32,
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
type MoveIntent = typeof MoveIntent.type;
|
|
24
|
-
|
|
25
|
-
interface PlayerUpdate {
|
|
26
|
-
x: number;
|
|
27
|
-
y: number;
|
|
28
|
-
health: number;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface ScoreUpdate {
|
|
32
|
-
score: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
type GameSnapshots = PlayerUpdate | ScoreUpdate;
|
|
36
|
-
|
|
37
|
-
// Mock transport adapter
|
|
38
|
-
class MockTransportAdapter implements TransportAdapter {
|
|
39
|
-
messageHandler: ((data: Uint8Array) => void) | null = null;
|
|
40
|
-
closeHandler: (() => void) | null = null;
|
|
41
|
-
openHandler: (() => void) | null = null;
|
|
42
|
-
public sentMessages: Uint8Array[] = [];
|
|
43
|
-
public closed = false;
|
|
44
|
-
|
|
45
|
-
send(data: Uint8Array): void {
|
|
46
|
-
this.sentMessages.push(new Uint8Array(data)); // Copy to avoid mutation
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
onMessage(handler: (data: Uint8Array) => void): void {
|
|
50
|
-
this.messageHandler = handler;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
onClose(handler: () => void): void {
|
|
54
|
-
this.closeHandler = handler;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
onOpen(handler: () => void): void {
|
|
58
|
-
this.openHandler = handler;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
close(): void {
|
|
62
|
-
this.closed = true;
|
|
63
|
-
if (this.closeHandler) {
|
|
64
|
-
this.closeHandler();
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Test helper: simulate receiving a message
|
|
69
|
-
simulateMessage(data: Uint8Array): void {
|
|
70
|
-
if (this.messageHandler) {
|
|
71
|
-
this.messageHandler(data);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Test helper: simulate disconnection
|
|
76
|
-
simulateDisconnect(): void {
|
|
77
|
-
if (this.closeHandler) {
|
|
78
|
-
this.closeHandler();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
describe("ClientNetwork", () => {
|
|
84
|
-
let transport: MockTransportAdapter;
|
|
85
|
-
let intentRegistry: IntentRegistry;
|
|
86
|
-
let snapshotRegistry: SnapshotRegistry<GameSnapshots>;
|
|
87
|
-
let client: ClientNetwork<GameSnapshots>;
|
|
88
|
-
|
|
89
|
-
beforeEach(() => {
|
|
90
|
-
transport = new MockTransportAdapter();
|
|
91
|
-
intentRegistry = new IntentRegistry();
|
|
92
|
-
snapshotRegistry = new SnapshotRegistry<GameSnapshots>();
|
|
93
|
-
|
|
94
|
-
// Register move intent
|
|
95
|
-
intentRegistry.register(MoveIntent);
|
|
96
|
-
|
|
97
|
-
// Register snapshot codecs
|
|
98
|
-
const playerCodec = new PooledCodec({
|
|
99
|
-
x: BinaryPrimitives.f32,
|
|
100
|
-
y: BinaryPrimitives.f32,
|
|
101
|
-
health: BinaryPrimitives.u8,
|
|
102
|
-
});
|
|
103
|
-
snapshotRegistry.register("player", playerCodec);
|
|
104
|
-
|
|
105
|
-
const scoreCodec = new PooledCodec({
|
|
106
|
-
score: BinaryPrimitives.u32,
|
|
107
|
-
});
|
|
108
|
-
snapshotRegistry.register("score", scoreCodec);
|
|
109
|
-
|
|
110
|
-
client = new ClientNetwork<GameSnapshots>({
|
|
111
|
-
transport,
|
|
112
|
-
intentRegistry,
|
|
113
|
-
snapshotRegistry,
|
|
114
|
-
config: { debug: false },
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// Simulate connection opened
|
|
118
|
-
if (transport.openHandler) {
|
|
119
|
-
transport.openHandler();
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe("Construction", () => {
|
|
124
|
-
test("should initialize and mark as connected", () => {
|
|
125
|
-
expect(client.isConnected()).toBe(true);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
test("should setup transport handlers", () => {
|
|
129
|
-
expect(transport.messageHandler).not.toBeNull();
|
|
130
|
-
expect(transport.closeHandler).not.toBeNull();
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test("should trigger onConnect handlers", () => {
|
|
134
|
-
let connectCalled = false;
|
|
135
|
-
const newTransport = new MockTransportAdapter();
|
|
136
|
-
const newClient = new ClientNetwork<GameSnapshots>({
|
|
137
|
-
transport: newTransport,
|
|
138
|
-
intentRegistry,
|
|
139
|
-
snapshotRegistry,
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
newClient.onConnect(() => {
|
|
143
|
-
connectCalled = true;
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// Connection happens during constructor
|
|
147
|
-
expect(connectCalled).toBe(false); // Handler registered after construction
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
describe("sendIntent", () => {
|
|
152
|
-
test("should encode and send intent to server", () => {
|
|
153
|
-
const intent: MoveIntent = {
|
|
154
|
-
kind: 1,
|
|
155
|
-
tick: 100,
|
|
156
|
-
dx: 5.5,
|
|
157
|
-
dy: -3.2,
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
client.sendIntent(intent);
|
|
161
|
-
|
|
162
|
-
expect(transport.sentMessages).toHaveLength(1);
|
|
163
|
-
const message = transport.sentMessages[0];
|
|
164
|
-
|
|
165
|
-
// Check message type header
|
|
166
|
-
expect(message[0]).toBe(0x01); // MessageType.INTENT
|
|
167
|
-
|
|
168
|
-
// Verify intent data (skip message type byte)
|
|
169
|
-
const intentData = message.subarray(1);
|
|
170
|
-
const decoded = intentRegistry.decode(intentData) as MoveIntent;
|
|
171
|
-
expect(decoded.kind).toBe(1);
|
|
172
|
-
expect(decoded.tick).toBe(100);
|
|
173
|
-
expect(decoded.dx).toBeCloseTo(5.5, 2);
|
|
174
|
-
expect(decoded.dy).toBeCloseTo(-3.2, 2);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test("should not send intent when disconnected", () => {
|
|
178
|
-
transport.simulateDisconnect();
|
|
179
|
-
|
|
180
|
-
const intent: MoveIntent = {
|
|
181
|
-
kind: 1,
|
|
182
|
-
tick: 100,
|
|
183
|
-
dx: 1,
|
|
184
|
-
dy: 1,
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
client.sendIntent(intent);
|
|
188
|
-
expect(transport.sentMessages).toHaveLength(0);
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
test("should handle encoding errors gracefully", () => {
|
|
192
|
-
const badIntentRegistry = new IntentRegistry();
|
|
193
|
-
const badClient = new ClientNetwork<GameSnapshots>({
|
|
194
|
-
transport: new MockTransportAdapter(),
|
|
195
|
-
intentRegistry: badIntentRegistry,
|
|
196
|
-
snapshotRegistry,
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
const intent: MoveIntent = {
|
|
200
|
-
kind: 99 as 1, // Not registered
|
|
201
|
-
tick: 100,
|
|
202
|
-
dx: 1,
|
|
203
|
-
dy: 1,
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
// Should not throw
|
|
207
|
-
expect(() => badClient.sendIntent(intent)).not.toThrow();
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
describe("onSnapshot", () => {
|
|
212
|
-
test("should receive and decode player snapshot", () => {
|
|
213
|
-
let receivedSnapshot: Snapshot<PlayerUpdate> | null = null;
|
|
214
|
-
|
|
215
|
-
client.onSnapshot<PlayerUpdate>("player", (snapshot) => {
|
|
216
|
-
receivedSnapshot = snapshot;
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Create snapshot
|
|
220
|
-
const snapshot: Snapshot<PlayerUpdate> = {
|
|
221
|
-
tick: 42,
|
|
222
|
-
updates: {
|
|
223
|
-
x: 10.5,
|
|
224
|
-
y: 20.3,
|
|
225
|
-
health: 100,
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
// Encode and send
|
|
230
|
-
const snapshotData = snapshotRegistry.encode("player", snapshot);
|
|
231
|
-
const message = new Uint8Array(1 + snapshotData.byteLength);
|
|
232
|
-
message[0] = 0x02; // MessageType.SNAPSHOT
|
|
233
|
-
message.set(snapshotData, 1);
|
|
234
|
-
|
|
235
|
-
transport.simulateMessage(message);
|
|
236
|
-
|
|
237
|
-
expect(receivedSnapshot).not.toBeNull();
|
|
238
|
-
expect(receivedSnapshot!.tick).toBe(42);
|
|
239
|
-
expect(receivedSnapshot!.updates.x).toBeCloseTo(10.5, 2);
|
|
240
|
-
expect(receivedSnapshot!.updates.y).toBeCloseTo(20.3, 2);
|
|
241
|
-
expect(receivedSnapshot!.updates.health).toBe(100);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
test("should receive and decode score snapshot", () => {
|
|
245
|
-
let receivedSnapshot: Snapshot<ScoreUpdate> | null = null;
|
|
246
|
-
|
|
247
|
-
client.onSnapshot<ScoreUpdate>("score", (snapshot) => {
|
|
248
|
-
receivedSnapshot = snapshot;
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
const snapshot: Snapshot<ScoreUpdate> = {
|
|
252
|
-
tick: 100,
|
|
253
|
-
updates: {
|
|
254
|
-
score: 9999,
|
|
255
|
-
},
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
const snapshotData = snapshotRegistry.encode("score", snapshot);
|
|
259
|
-
const message = new Uint8Array(1 + snapshotData.byteLength);
|
|
260
|
-
message[0] = 0x02; // MessageType.SNAPSHOT
|
|
261
|
-
message.set(snapshotData, 1);
|
|
262
|
-
|
|
263
|
-
transport.simulateMessage(message);
|
|
264
|
-
|
|
265
|
-
expect(receivedSnapshot).not.toBeNull();
|
|
266
|
-
expect(receivedSnapshot!.tick).toBe(100);
|
|
267
|
-
expect(receivedSnapshot!.updates.score).toBe(9999);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
test("should handle multiple snapshot types independently", () => {
|
|
271
|
-
const playerSnapshots: Snapshot<PlayerUpdate>[] = [];
|
|
272
|
-
const scoreSnapshots: Snapshot<ScoreUpdate>[] = [];
|
|
273
|
-
|
|
274
|
-
client.onSnapshot<PlayerUpdate>("player", (s) => playerSnapshots.push(s));
|
|
275
|
-
client.onSnapshot<ScoreUpdate>("score", (s) => scoreSnapshots.push(s));
|
|
276
|
-
|
|
277
|
-
// Send player snapshot
|
|
278
|
-
const playerSnapshot: Snapshot<PlayerUpdate> = {
|
|
279
|
-
tick: 1,
|
|
280
|
-
updates: { x: 1, y: 2, health: 50 },
|
|
281
|
-
};
|
|
282
|
-
const playerData = snapshotRegistry.encode("player", playerSnapshot);
|
|
283
|
-
const playerMsg = new Uint8Array(1 + playerData.byteLength);
|
|
284
|
-
playerMsg[0] = 0x02;
|
|
285
|
-
playerMsg.set(playerData, 1);
|
|
286
|
-
transport.simulateMessage(playerMsg);
|
|
287
|
-
|
|
288
|
-
// Send score snapshot
|
|
289
|
-
const scoreSnapshot: Snapshot<ScoreUpdate> = {
|
|
290
|
-
tick: 2,
|
|
291
|
-
updates: { score: 123 },
|
|
292
|
-
};
|
|
293
|
-
const scoreData = snapshotRegistry.encode("score", scoreSnapshot);
|
|
294
|
-
const scoreMsg = new Uint8Array(1 + scoreData.byteLength);
|
|
295
|
-
scoreMsg[0] = 0x02;
|
|
296
|
-
scoreMsg.set(scoreData, 1);
|
|
297
|
-
transport.simulateMessage(scoreMsg);
|
|
298
|
-
|
|
299
|
-
expect(playerSnapshots).toHaveLength(1);
|
|
300
|
-
expect(scoreSnapshots).toHaveLength(1);
|
|
301
|
-
expect(playerSnapshots[0].tick).toBe(1);
|
|
302
|
-
expect(scoreSnapshots[0].tick).toBe(2);
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
test("should return unsubscribe function", () => {
|
|
306
|
-
let callCount = 0;
|
|
307
|
-
const unsubscribe = client.onSnapshot<PlayerUpdate>("player", () => {
|
|
308
|
-
callCount++;
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
const snapshot: Snapshot<PlayerUpdate> = {
|
|
312
|
-
tick: 1,
|
|
313
|
-
updates: { x: 1, y: 1, health: 100 },
|
|
314
|
-
};
|
|
315
|
-
const snapshotData = snapshotRegistry.encode("player", snapshot);
|
|
316
|
-
const message = new Uint8Array(1 + snapshotData.byteLength);
|
|
317
|
-
message[0] = 0x02;
|
|
318
|
-
message.set(snapshotData, 1);
|
|
319
|
-
|
|
320
|
-
transport.simulateMessage(message);
|
|
321
|
-
expect(callCount).toBe(1);
|
|
322
|
-
|
|
323
|
-
// Unsubscribe
|
|
324
|
-
unsubscribe();
|
|
325
|
-
|
|
326
|
-
// Send another snapshot
|
|
327
|
-
transport.simulateMessage(message);
|
|
328
|
-
expect(callCount).toBe(1); // Should not increase
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test("should handle unknown snapshot types gracefully", () => {
|
|
332
|
-
// Register handler for "player" only
|
|
333
|
-
let called = false;
|
|
334
|
-
client.onSnapshot<PlayerUpdate>("player", () => {
|
|
335
|
-
called = true;
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
// Send snapshot for unhandled type "score" (registered but no handler)
|
|
339
|
-
const snapshot: Snapshot<ScoreUpdate> = {
|
|
340
|
-
tick: 1,
|
|
341
|
-
updates: { score: 42 },
|
|
342
|
-
};
|
|
343
|
-
const snapshotData = snapshotRegistry.encode("score", snapshot);
|
|
344
|
-
const message = new Uint8Array(1 + snapshotData.byteLength);
|
|
345
|
-
message[0] = 0x02;
|
|
346
|
-
message.set(snapshotData, 1);
|
|
347
|
-
|
|
348
|
-
// Should not throw or call the player handler
|
|
349
|
-
expect(() => transport.simulateMessage(message)).not.toThrow();
|
|
350
|
-
expect(called).toBe(false);
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
describe("Connection lifecycle", () => {
|
|
355
|
-
test("should trigger onConnect handler when connection opens", () => {
|
|
356
|
-
let connectCalled = false;
|
|
357
|
-
const newTransport = new MockTransportAdapter();
|
|
358
|
-
|
|
359
|
-
const newClient = new ClientNetwork<GameSnapshots>({
|
|
360
|
-
transport: newTransport,
|
|
361
|
-
intentRegistry,
|
|
362
|
-
snapshotRegistry,
|
|
363
|
-
});
|
|
364
|
-
|
|
365
|
-
newClient.onConnect(() => {
|
|
366
|
-
connectCalled = true;
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
// Not connected yet - waiting for transport to open
|
|
370
|
-
expect(connectCalled).toBe(false);
|
|
371
|
-
expect(newClient.isConnected()).toBe(false);
|
|
372
|
-
|
|
373
|
-
// Trigger open event
|
|
374
|
-
if (newTransport.openHandler) {
|
|
375
|
-
newTransport.openHandler();
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Now should be connected and handler should have been called
|
|
379
|
-
expect(connectCalled).toBe(true);
|
|
380
|
-
expect(newClient.isConnected()).toBe(true);
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
test("should trigger onDisconnect handler", () => {
|
|
384
|
-
let disconnectCalled = false;
|
|
385
|
-
client.onDisconnect(() => {
|
|
386
|
-
disconnectCalled = true;
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
transport.simulateDisconnect();
|
|
390
|
-
|
|
391
|
-
expect(disconnectCalled).toBe(true);
|
|
392
|
-
expect(client.isConnected()).toBe(false);
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
test("should handle multiple disconnect handlers", () => {
|
|
396
|
-
let count = 0;
|
|
397
|
-
client.onDisconnect(() => count++);
|
|
398
|
-
client.onDisconnect(() => count++);
|
|
399
|
-
client.onDisconnect(() => count++);
|
|
400
|
-
|
|
401
|
-
transport.simulateDisconnect();
|
|
402
|
-
expect(count).toBe(3);
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
test("should return unsubscribe function for onConnect", () => {
|
|
406
|
-
let callCount = 0;
|
|
407
|
-
const unsub = client.onConnect(() => callCount++);
|
|
408
|
-
unsub();
|
|
409
|
-
|
|
410
|
-
// Create new client to trigger connect
|
|
411
|
-
const newTransport = new MockTransportAdapter();
|
|
412
|
-
const newClient = new ClientNetwork<GameSnapshots>({
|
|
413
|
-
transport: newTransport,
|
|
414
|
-
intentRegistry,
|
|
415
|
-
snapshotRegistry,
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
expect(callCount).toBe(0);
|
|
419
|
-
});
|
|
420
|
-
|
|
421
|
-
test("should return unsubscribe function for onDisconnect", () => {
|
|
422
|
-
let callCount = 0;
|
|
423
|
-
const unsub = client.onDisconnect(() => callCount++);
|
|
424
|
-
|
|
425
|
-
transport.simulateDisconnect();
|
|
426
|
-
expect(callCount).toBe(1);
|
|
427
|
-
|
|
428
|
-
unsub();
|
|
429
|
-
// Can't trigger disconnect again on same transport, but verifies unsubscribe works
|
|
430
|
-
});
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
describe("disconnect", () => {
|
|
434
|
-
test("should close transport connection", () => {
|
|
435
|
-
client.disconnect();
|
|
436
|
-
expect(transport.closed).toBe(true);
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
test("should trigger disconnect handlers when calling disconnect", () => {
|
|
440
|
-
let disconnectCalled = false;
|
|
441
|
-
client.onDisconnect(() => {
|
|
442
|
-
disconnectCalled = true;
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
client.disconnect();
|
|
446
|
-
expect(disconnectCalled).toBe(true);
|
|
447
|
-
expect(client.isConnected()).toBe(false);
|
|
448
|
-
});
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
describe("Message handling", () => {
|
|
452
|
-
test("should ignore empty messages", () => {
|
|
453
|
-
let snapshotReceived = false;
|
|
454
|
-
client.onSnapshot<PlayerUpdate>("player", () => {
|
|
455
|
-
snapshotReceived = true;
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
transport.simulateMessage(new Uint8Array(0));
|
|
459
|
-
expect(snapshotReceived).toBe(false);
|
|
460
|
-
});
|
|
461
|
-
|
|
462
|
-
test("should handle custom message type gracefully", () => {
|
|
463
|
-
const message = new Uint8Array([0xff]); // MessageType.CUSTOM
|
|
464
|
-
expect(() => transport.simulateMessage(message)).not.toThrow();
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
test("should handle unknown message types gracefully", () => {
|
|
468
|
-
const message = new Uint8Array([0x99, 1, 2, 3]); // Unknown type
|
|
469
|
-
expect(() => transport.simulateMessage(message)).not.toThrow();
|
|
470
|
-
});
|
|
471
|
-
|
|
472
|
-
test("should reject messages exceeding max size", () => {
|
|
473
|
-
const smallClient = new ClientNetwork<GameSnapshots>({
|
|
474
|
-
transport,
|
|
475
|
-
intentRegistry,
|
|
476
|
-
snapshotRegistry,
|
|
477
|
-
config: { maxMessageSize: 10 },
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
let snapshotReceived = false;
|
|
481
|
-
smallClient.onSnapshot<PlayerUpdate>("player", () => {
|
|
482
|
-
snapshotReceived = true;
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
// Create large message
|
|
486
|
-
const largeMessage = new Uint8Array(100);
|
|
487
|
-
largeMessage[0] = 0x02; // MessageType.SNAPSHOT
|
|
488
|
-
|
|
489
|
-
transport.simulateMessage(largeMessage);
|
|
490
|
-
expect(snapshotReceived).toBe(false);
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
test("should handle malformed snapshot data gracefully", () => {
|
|
494
|
-
let snapshotReceived = false;
|
|
495
|
-
client.onSnapshot<PlayerUpdate>("player", () => {
|
|
496
|
-
snapshotReceived = true;
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
// Send malformed data
|
|
500
|
-
const badMessage = new Uint8Array([0x02, 99, 88, 77]); // Invalid snapshot
|
|
501
|
-
expect(() => transport.simulateMessage(badMessage)).not.toThrow();
|
|
502
|
-
expect(snapshotReceived).toBe(false);
|
|
503
|
-
});
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
describe("Debug logging", () => {
|
|
507
|
-
test("should not log when debug is false", () => {
|
|
508
|
-
const logs: string[] = [];
|
|
509
|
-
const originalLog = console.log;
|
|
510
|
-
console.log = (...args: any[]) => logs.push(args.join(" "));
|
|
511
|
-
|
|
512
|
-
const intent: MoveIntent = {
|
|
513
|
-
kind: 1,
|
|
514
|
-
tick: 1,
|
|
515
|
-
dx: 1,
|
|
516
|
-
dy: 1,
|
|
517
|
-
};
|
|
518
|
-
client.sendIntent(intent);
|
|
519
|
-
|
|
520
|
-
console.log = originalLog;
|
|
521
|
-
expect(logs.filter((l) => l.includes("[ClientNetwork]"))).toHaveLength(0);
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
test("should log when debug is true", () => {
|
|
525
|
-
const logs: string[] = [];
|
|
526
|
-
const originalLog = console.log;
|
|
527
|
-
console.log = (...args: any[]) => logs.push(args.join(" "));
|
|
528
|
-
|
|
529
|
-
const debugClient = new ClientNetwork<GameSnapshots>({
|
|
530
|
-
transport: new MockTransportAdapter(),
|
|
531
|
-
intentRegistry,
|
|
532
|
-
snapshotRegistry,
|
|
533
|
-
config: { debug: true },
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
const intent: MoveIntent = {
|
|
537
|
-
kind: 1,
|
|
538
|
-
tick: 1,
|
|
539
|
-
dx: 1,
|
|
540
|
-
dy: 1,
|
|
541
|
-
};
|
|
542
|
-
debugClient.sendIntent(intent);
|
|
543
|
-
|
|
544
|
-
console.log = originalLog;
|
|
545
|
-
expect(logs.filter((l) => l.includes("[ClientNetwork]")).length).toBeGreaterThan(0);
|
|
546
|
-
});
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
describe("Memory pooling", () => {
|
|
550
|
-
test("should reuse pooled objects across snapshots (zero-copy)", () => {
|
|
551
|
-
const transport = new MockTransportAdapter();
|
|
552
|
-
const client = new ClientNetwork<GameSnapshots>({
|
|
553
|
-
transport,
|
|
554
|
-
intentRegistry,
|
|
555
|
-
snapshotRegistry,
|
|
556
|
-
config: { debug: false },
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
// Extract data immediately (correct pattern for zero-copy)
|
|
560
|
-
const extractedData: Array<{ tick: number; x: number; y: number; health: number }> = [];
|
|
561
|
-
|
|
562
|
-
client.onSnapshot<PlayerUpdate>("player", (snapshot) => {
|
|
563
|
-
// CORRECT: Extract data immediately, don't store references
|
|
564
|
-
extractedData.push({
|
|
565
|
-
tick: snapshot.tick,
|
|
566
|
-
x: snapshot.updates.x!,
|
|
567
|
-
y: snapshot.updates.y!,
|
|
568
|
-
health: snapshot.updates.health!,
|
|
569
|
-
});
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
// Simulate opening connection
|
|
573
|
-
if (transport.openHandler) transport.openHandler();
|
|
574
|
-
|
|
575
|
-
// Send first snapshot (wrap with MessageType.SNAPSHOT header)
|
|
576
|
-
const snapshot1Data = snapshotRegistry.encode("player", {
|
|
577
|
-
tick: 1,
|
|
578
|
-
updates: { x: 10, y: 20, health: 100 },
|
|
579
|
-
});
|
|
580
|
-
const snapshot1 = new Uint8Array(1 + snapshot1Data.byteLength);
|
|
581
|
-
snapshot1[0] = MessageType.SNAPSHOT;
|
|
582
|
-
snapshot1.set(snapshot1Data, 1);
|
|
583
|
-
if (transport.messageHandler) transport.messageHandler(snapshot1);
|
|
584
|
-
|
|
585
|
-
// Send second snapshot with different data
|
|
586
|
-
const snapshot2Data = snapshotRegistry.encode("player", {
|
|
587
|
-
tick: 2,
|
|
588
|
-
updates: { x: 15, y: 25, health: 90 },
|
|
589
|
-
});
|
|
590
|
-
const snapshot2 = new Uint8Array(1 + snapshot2Data.byteLength);
|
|
591
|
-
snapshot2[0] = MessageType.SNAPSHOT;
|
|
592
|
-
snapshot2.set(snapshot2Data, 1);
|
|
593
|
-
if (transport.messageHandler) transport.messageHandler(snapshot2);
|
|
594
|
-
|
|
595
|
-
// Verify both snapshots were received correctly
|
|
596
|
-
expect(extractedData.length).toBe(2);
|
|
597
|
-
|
|
598
|
-
// First snapshot data should be correct
|
|
599
|
-
expect(extractedData[0].tick).toBe(1);
|
|
600
|
-
expect(extractedData[0].x).toBe(10);
|
|
601
|
-
expect(extractedData[0].y).toBe(20);
|
|
602
|
-
expect(extractedData[0].health).toBe(100);
|
|
603
|
-
|
|
604
|
-
// Second snapshot data should be correct
|
|
605
|
-
expect(extractedData[1].tick).toBe(2);
|
|
606
|
-
expect(extractedData[1].x).toBe(15);
|
|
607
|
-
expect(extractedData[1].y).toBe(25);
|
|
608
|
-
expect(extractedData[1].health).toBe(90);
|
|
609
|
-
});
|
|
610
|
-
|
|
611
|
-
test("handlers must extract data immediately (zero-copy pattern)", () => {
|
|
612
|
-
const transport = new MockTransportAdapter();
|
|
613
|
-
const client = new ClientNetwork<GameSnapshots>({
|
|
614
|
-
transport,
|
|
615
|
-
intentRegistry,
|
|
616
|
-
snapshotRegistry,
|
|
617
|
-
config: { debug: false },
|
|
618
|
-
});
|
|
619
|
-
|
|
620
|
-
// Simulate a reconciliator-like usage pattern (correct way)
|
|
621
|
-
interface ExtractedState {
|
|
622
|
-
x: number;
|
|
623
|
-
y: number;
|
|
624
|
-
health: number;
|
|
625
|
-
}
|
|
626
|
-
let extractedState: ExtractedState | null = null;
|
|
627
|
-
|
|
628
|
-
client.onSnapshot<PlayerUpdate>("player", (snapshot) => {
|
|
629
|
-
// CORRECT: Extract data immediately instead of storing references
|
|
630
|
-
extractedState = {
|
|
631
|
-
x: snapshot.updates.x!,
|
|
632
|
-
y: snapshot.updates.y!,
|
|
633
|
-
health: snapshot.updates.health!,
|
|
634
|
-
};
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
// Simulate opening connection
|
|
638
|
-
if (transport.openHandler) transport.openHandler();
|
|
639
|
-
|
|
640
|
-
// Send first snapshot (wrap with MessageType.SNAPSHOT header)
|
|
641
|
-
const snapshot1Data = snapshotRegistry.encode("player", {
|
|
642
|
-
tick: 1,
|
|
643
|
-
updates: { x: 10, y: 20, health: 100 },
|
|
644
|
-
});
|
|
645
|
-
const snapshot1 = new Uint8Array(1 + snapshot1Data.byteLength);
|
|
646
|
-
snapshot1[0] = MessageType.SNAPSHOT;
|
|
647
|
-
snapshot1.set(snapshot1Data, 1);
|
|
648
|
-
if (transport.messageHandler) transport.messageHandler(snapshot1);
|
|
649
|
-
|
|
650
|
-
const firstState = extractedState!;
|
|
651
|
-
expect(firstState.x).toBe(10);
|
|
652
|
-
expect(firstState.y).toBe(20);
|
|
653
|
-
expect(firstState.health).toBe(100);
|
|
654
|
-
|
|
655
|
-
// Send second snapshot (wrap with MessageType.SNAPSHOT header)
|
|
656
|
-
const snapshot2Data = snapshotRegistry.encode("player", {
|
|
657
|
-
tick: 2,
|
|
658
|
-
updates: { x: 99, y: 99, health: 50 },
|
|
659
|
-
});
|
|
660
|
-
const snapshot2 = new Uint8Array(1 + snapshot2Data.byteLength);
|
|
661
|
-
snapshot2[0] = MessageType.SNAPSHOT;
|
|
662
|
-
snapshot2.set(snapshot2Data, 1);
|
|
663
|
-
if (transport.messageHandler) transport.messageHandler(snapshot2);
|
|
664
|
-
|
|
665
|
-
// First extracted state should NOT be mutated (we extracted primitives)
|
|
666
|
-
expect(firstState.x).toBe(10);
|
|
667
|
-
expect(firstState.y).toBe(20);
|
|
668
|
-
expect(firstState.health).toBe(100);
|
|
669
|
-
|
|
670
|
-
// New extracted state should have the new values
|
|
671
|
-
expect(extractedState!.x).toBe(99);
|
|
672
|
-
expect(extractedState!.y).toBe(99);
|
|
673
|
-
expect(extractedState!.health).toBe(50);
|
|
674
|
-
});
|
|
675
|
-
});
|
|
676
|
-
|
|
677
|
-
describe("RPC Memory pooling", () => {
|
|
678
|
-
// Define RPC for testing
|
|
679
|
-
const TestRPC = defineRPC({
|
|
680
|
-
method: "testRpc",
|
|
681
|
-
schema: {
|
|
682
|
-
value: BinaryPrimitives.u32,
|
|
683
|
-
message: BinaryPrimitives.string(32),
|
|
684
|
-
},
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
interface TestRPCData {
|
|
688
|
-
value: number;
|
|
689
|
-
message: string;
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
test("should reuse pooled objects across RPC calls", () => {
|
|
693
|
-
const rpcRegistry = new RpcRegistry();
|
|
694
|
-
rpcRegistry.register(TestRPC);
|
|
695
|
-
|
|
696
|
-
const transport = new MockTransportAdapter();
|
|
697
|
-
const client = new ClientNetwork<GameSnapshots>({
|
|
698
|
-
transport,
|
|
699
|
-
intentRegistry,
|
|
700
|
-
snapshotRegistry,
|
|
701
|
-
rpcRegistry,
|
|
702
|
-
config: { debug: false },
|
|
703
|
-
});
|
|
704
|
-
|
|
705
|
-
const receivedRpcs: Array<TestRPCData> = [];
|
|
706
|
-
|
|
707
|
-
client.onRPC(TestRPC, (data) => {
|
|
708
|
-
// Store the RPC data - this should be safe due to shallow copy
|
|
709
|
-
receivedRpcs.push(data);
|
|
710
|
-
});
|
|
711
|
-
|
|
712
|
-
// Simulate opening connection
|
|
713
|
-
if (transport.openHandler) transport.openHandler();
|
|
714
|
-
|
|
715
|
-
// Send first RPC
|
|
716
|
-
const rpc1Data = rpcRegistry.encode(TestRPC, {
|
|
717
|
-
value: 100,
|
|
718
|
-
message: "first",
|
|
719
|
-
});
|
|
720
|
-
const rpc1 = new Uint8Array(1 + rpc1Data.byteLength);
|
|
721
|
-
rpc1[0] = MessageType.CUSTOM;
|
|
722
|
-
rpc1.set(rpc1Data, 1);
|
|
723
|
-
if (transport.messageHandler) transport.messageHandler(rpc1);
|
|
724
|
-
|
|
725
|
-
// Send second RPC with different data
|
|
726
|
-
const rpc2Data = rpcRegistry.encode(TestRPC, {
|
|
727
|
-
value: 200,
|
|
728
|
-
message: "second",
|
|
729
|
-
});
|
|
730
|
-
const rpc2 = new Uint8Array(1 + rpc2Data.byteLength);
|
|
731
|
-
rpc2[0] = MessageType.CUSTOM;
|
|
732
|
-
rpc2.set(rpc2Data, 1);
|
|
733
|
-
if (transport.messageHandler) transport.messageHandler(rpc2);
|
|
734
|
-
|
|
735
|
-
// Verify both RPCs were received correctly
|
|
736
|
-
expect(receivedRpcs.length).toBe(2);
|
|
737
|
-
|
|
738
|
-
// First RPC should maintain its original values
|
|
739
|
-
expect(receivedRpcs[0].value).toBe(100);
|
|
740
|
-
expect(receivedRpcs[0].message).toBe("first");
|
|
741
|
-
|
|
742
|
-
// Second RPC should have its own values
|
|
743
|
-
expect(receivedRpcs[1].value).toBe(200);
|
|
744
|
-
expect(receivedRpcs[1].message).toBe("second");
|
|
745
|
-
|
|
746
|
-
// The data objects should be different references (shallow copy worked)
|
|
747
|
-
expect(receivedRpcs[0]).not.toBe(receivedRpcs[1]);
|
|
748
|
-
});
|
|
749
|
-
|
|
750
|
-
test("handlers can safely store references to RPC data", () => {
|
|
751
|
-
const rpcRegistry = new RpcRegistry();
|
|
752
|
-
rpcRegistry.register(TestRPC);
|
|
753
|
-
|
|
754
|
-
const transport = new MockTransportAdapter();
|
|
755
|
-
const client = new ClientNetwork<GameSnapshots>({
|
|
756
|
-
transport,
|
|
757
|
-
intentRegistry,
|
|
758
|
-
snapshotRegistry,
|
|
759
|
-
rpcRegistry,
|
|
760
|
-
config: { debug: false },
|
|
761
|
-
});
|
|
762
|
-
|
|
763
|
-
// Simulate storing RPC data (common pattern)
|
|
764
|
-
let storedData: TestRPCData | null = null;
|
|
765
|
-
|
|
766
|
-
client.onRPC(TestRPC, (data) => {
|
|
767
|
-
// Store the data directly
|
|
768
|
-
storedData = data;
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
// Simulate opening connection
|
|
772
|
-
if (transport.openHandler) transport.openHandler();
|
|
773
|
-
|
|
774
|
-
// Send first RPC
|
|
775
|
-
const rpc1Data = rpcRegistry.encode(TestRPC, {
|
|
776
|
-
value: 42,
|
|
777
|
-
message: "hello",
|
|
778
|
-
});
|
|
779
|
-
const rpc1 = new Uint8Array(1 + rpc1Data.byteLength);
|
|
780
|
-
rpc1[0] = MessageType.CUSTOM;
|
|
781
|
-
rpc1.set(rpc1Data, 1);
|
|
782
|
-
if (transport.messageHandler) transport.messageHandler(rpc1);
|
|
783
|
-
|
|
784
|
-
const firstData = storedData;
|
|
785
|
-
expect((firstData as unknown as TestRPCData)?.value).toBe(42);
|
|
786
|
-
expect((firstData as unknown as TestRPCData)?.message).toBe("hello");
|
|
787
|
-
|
|
788
|
-
// Send second RPC
|
|
789
|
-
const rpc2Data = rpcRegistry.encode(TestRPC, {
|
|
790
|
-
value: 999,
|
|
791
|
-
message: "world",
|
|
792
|
-
});
|
|
793
|
-
const rpc2 = new Uint8Array(1 + rpc2Data.byteLength);
|
|
794
|
-
rpc2[0] = MessageType.CUSTOM;
|
|
795
|
-
rpc2.set(rpc2Data, 1);
|
|
796
|
-
if (transport.messageHandler) transport.messageHandler(rpc2);
|
|
797
|
-
|
|
798
|
-
// First stored data should NOT be mutated by the second RPC
|
|
799
|
-
expect((firstData as unknown as TestRPCData)?.value).toBe(42);
|
|
800
|
-
expect((firstData as unknown as TestRPCData)?.message).toBe("hello");
|
|
801
|
-
|
|
802
|
-
// New stored data should have the new values
|
|
803
|
-
expect((storedData as unknown as TestRPCData)?.value).toBe(999);
|
|
804
|
-
expect((storedData as unknown as TestRPCData)?.message).toBe("world");
|
|
805
|
-
});
|
|
806
|
-
});
|
|
807
|
-
});
|