@woosh/meep-engine 2.168.0 → 2.169.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/bundle-worker-image-decoder.js +1 -1
- package/package.json +1 -1
- package/src/REVIEW_2026_08_06/adv-core.md +511 -0
- package/src/REVIEW_2026_08_06/adv-gfx.md +480 -0
- package/src/REVIEW_2026_08_06/adv-view.md +446 -0
- package/src/REVIEW_2026_08_06/api-core.md +296 -0
- package/src/REVIEW_2026_08_06/api-engine.md +195 -0
- package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +507 -0
- package/src/REVIEW_2026_08_06/core-binary.md +348 -0
- package/src/REVIEW_2026_08_06/core-collection.md +376 -0
- package/src/REVIEW_2026_08_06/core-math.md +221 -0
- package/src/REVIEW_2026_08_06/core-process.md +266 -0
- package/src/REVIEW_2026_08_06/core-science.md +223 -0
- package/src/REVIEW_2026_08_06/engine-ai-nav.md +279 -0
- package/src/REVIEW_2026_08_06/engine-animation.md +167 -0
- package/src/REVIEW_2026_08_06/engine-asset.md +166 -0
- package/src/REVIEW_2026_08_06/engine-ecs.md +920 -0
- package/src/REVIEW_2026_08_06/engine-input.md +197 -0
- package/src/REVIEW_2026_08_06/engine-misc.md +248 -0
- package/src/REVIEW_2026_08_06/engine-terrain.md +52 -0
- package/src/REVIEW_2026_08_06/generation.md +176 -0
- package/src/REVIEW_2026_08_06/geom-2d.md +434 -0
- package/src/REVIEW_2026_08_06/geom-3d.md +117 -0
- package/src/REVIEW_2026_08_06/gfx-gi.md +143 -0
- package/src/REVIEW_2026_08_06/gfx-render.md +740 -0
- package/src/REVIEW_2026_08_06/gfx-texture.md +690 -0
- package/src/REVIEW_2026_08_06/prior-criticals.md +49 -0
- package/src/REVIEW_2026_08_06/prior-highs.md +138 -0
- package/src/REVIEW_2026_08_06/test-gaps.md +364 -0
- package/src/REVIEW_2026_08_06/view.md +667 -0
- package/src/REVIEW_2026_08_06.md +571 -0
- package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
- package/src/core/binary/BinaryBuffer.js +12 -1
- package/src/core/binary/BitImage2.d.ts +5 -1
- package/src/core/binary/BitImage2.d.ts.map +1 -1
- package/src/core/binary/BitImage2.js +5 -1
- package/src/core/bvh8/build/NodeProxy.d.ts.map +1 -1
- package/src/core/bvh8/build/NodeProxy.js +314 -308
- package/src/core/collection/list/List.d.ts +15 -0
- package/src/core/collection/list/List.d.ts.map +1 -1
- package/src/core/collection/list/List.js +34 -0
- package/src/core/collection/list/SortedListProjection.d.ts.map +1 -1
- package/src/core/collection/list/SortedListProjection.js +4 -1
- package/src/core/collection/map/BiMap.d.ts +25 -2
- package/src/core/collection/map/BiMap.d.ts.map +1 -1
- package/src/core/collection/map/BiMap.js +57 -3
- package/src/core/debug/matchers/IsAnything.d.ts.map +1 -1
- package/src/core/debug/matchers/IsAnything.js +5 -1
- package/src/core/events/signal/Signal.d.ts.map +1 -1
- package/src/core/events/signal/Signal.js +3 -1
- package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +433 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.d.ts +31 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_is_counter_clockwise.js +84 -0
- package/src/core/geom/2d/polygon/polygon2_signed_area.d.ts +24 -2
- package/src/core/geom/2d/polygon/polygon2_signed_area.d.ts.map +1 -1
- package/src/core/geom/2d/polygon/polygon2_signed_area.js +52 -34
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts +28 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +86 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.d.ts +40 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulate.js +1280 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.d.ts +28 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.d.ts.map +1 -0
- package/src/core/geom/2d/polygon/polygon2_triangulation_deviation.js +77 -0
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts +6 -3
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.d.ts.map +1 -1
- package/src/core/geom/2d/triangle/tri2_rasterize_conservative.js +26 -13
- package/src/core/geom/2d/triangle/tri2_signed_area.d.ts +17 -1
- package/src/core/geom/2d/triangle/tri2_signed_area.d.ts.map +1 -1
- package/src/core/geom/2d/triangle/tri2_signed_area.js +35 -23
- package/src/core/geom/2d/v2_morton_encode.d.ts +20 -0
- package/src/core/geom/2d/v2_morton_encode.d.ts.map +1 -0
- package/src/core/geom/2d/v2_morton_encode.js +23 -0
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts.map +1 -1
- package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +4 -1
- package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts +27 -0
- package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts.map +1 -0
- package/src/core/geom/3d/polygon/polygon3_compute_normal.js +60 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.d.ts +34 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.d.ts.map +1 -0
- package/src/core/geom/3d/polygon/polygon3_triangulate.js +94 -0
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts +8 -2
- package/src/core/geom/3d/quaternion/quat3_multiply.d.ts.map +1 -1
- package/src/core/geom/3d/quaternion/quat3_multiply.js +8 -2
- package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
- package/src/core/geom/3d/shape/ConvexHullShape3D.js +32 -2
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.d.ts +23 -6
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.d.ts.map +1 -1
- package/src/core/geom/3d/topology/struct/binary/io/face/bt_face_triangulate.js +268 -124
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.d.ts +17 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_assert_valid.js +44 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.d.ts +20 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_count_edge_faces.js +40 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.d.ts +19 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_closed.js +34 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.d.ts +22 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_is_manifold.js +37 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.d.ts +87 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.d.ts.map +1 -0
- package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_validate.js +599 -0
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.d.ts +32 -0
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_compute_signed_volume.js +66 -7
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.d.ts +22 -1
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_mesh_compute_signed_volume.js +83 -38
- package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +554 -0
- package/src/core/geom/vec3/v3_cotangent.d.ts +6 -2
- package/src/core/geom/vec3/v3_cotangent.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_cotangent.js +6 -2
- package/src/core/geom/vec3/v3_cross.d.ts +6 -2
- package/src/core/geom/vec3/v3_cross.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_cross.js +6 -2
- package/src/core/geom/vec3/v3_multiply.d.ts +6 -2
- package/src/core/geom/vec3/v3_multiply.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_multiply.js +6 -2
- package/src/core/geom/vec3/v3_subtract.d.ts +6 -2
- package/src/core/geom/vec3/v3_subtract.d.ts.map +1 -1
- package/src/core/geom/vec3/v3_subtract.js +6 -2
- package/src/core/localization/Localization.js +1 -1
- package/src/core/math/random/randomSeed.d.ts +13 -0
- package/src/core/math/random/randomSeed.d.ts.map +1 -0
- package/src/core/math/random/randomSeed.js +16 -0
- package/src/core/math/spline/spline3_hermite_apply_transform.d.ts +50 -0
- package/src/core/math/spline/spline3_hermite_apply_transform.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_apply_transform.js +107 -0
- package/src/core/math/spline/spline3_hermite_reverse.d.ts +34 -0
- package/src/core/math/spline/spline3_hermite_reverse.d.ts.map +1 -0
- package/src/core/math/spline/spline3_hermite_reverse.js +49 -0
- package/src/core/math/statistics/hammersley_sequence.d.ts +17 -4
- package/src/core/math/statistics/hammersley_sequence.d.ts.map +1 -1
- package/src/core/math/statistics/hammersley_sequence.js +25 -16
- package/src/core/process/Future.d.ts.map +1 -1
- package/src/core/process/Future.js +12 -0
- package/src/core/process/PromiseWatcher.d.ts.map +1 -1
- package/src/core/process/PromiseWatcher.js +9 -2
- package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
- package/src/core/process/executor/ConcurrentExecutor.js +12 -2
- package/src/core/process/worker/WorkerProxy.d.ts.map +1 -1
- package/src/core/process/worker/WorkerProxy.js +5 -2
- package/src/core/process/worker/extractTransferables.d.ts.map +1 -1
- package/src/core/process/worker/extractTransferables.js +4 -1
- package/src/engine/Engine.d.ts +12 -12
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +149 -10
- package/src/engine/EngineHarness.d.ts.map +1 -1
- package/src/engine/EngineHarness.js +4 -1
- package/src/engine/asset/loaders/ArrayBufferLoader.d.ts.map +1 -1
- package/src/engine/asset/loaders/ArrayBufferLoader.js +7 -0
- package/src/engine/ecs/speaker/lines/sets/LineSetDescription.d.ts.map +1 -1
- package/src/engine/ecs/speaker/lines/sets/LineSetDescription.js +3 -1
- package/src/engine/ecs/terrain/ecs/TerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/TerrainSystem.js +4 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts +29 -0
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +84 -23
- package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +446 -0
- package/src/engine/graphics/GraphicsEngine.d.ts +56 -7
- package/src/engine/graphics/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics/GraphicsEngine.js +196 -24
- package/src/engine/graphics/context/WebGLContextFailureReason.d.ts +9 -0
- package/src/engine/graphics/context/WebGLContextFailureReason.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextFailureReason.js +17 -0
- package/src/engine/graphics/context/WebGLContextMonitor.d.ts +106 -0
- package/src/engine/graphics/context/WebGLContextMonitor.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextMonitor.js +341 -0
- package/src/engine/graphics/context/WebGLContextState.d.ts +14 -0
- package/src/engine/graphics/context/WebGLContextState.d.ts.map +1 -0
- package/src/engine/graphics/context/WebGLContextState.js +24 -0
- package/src/engine/graphics/context/testWebGLContextLoss.d.ts +2 -0
- package/src/engine/graphics/context/testWebGLContextLoss.d.ts.map +1 -0
- package/src/engine/graphics/context/testWebGLContextLoss.js +696 -0
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationAdapter.js +68 -32
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.d.ts +14 -0
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/graphics/ecs/camera/serialization/CameraSerializationUpgrader_0_1.js +41 -0
- package/src/engine/graphics/ecs/decal/v2/FPDecalSystem.js +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +5 -0
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts +5 -1
- package/src/engine/graphics/ecs/trail2d/Trail2D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2D.js +15 -0
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail2d/Trail2DSystem.js +2 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +4 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3D.js +488 -476
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/trail3d/Trail3DSystem.js +280 -253
- package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +3 -2
- package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.d.ts.map +1 -1
- package/src/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +3 -1
- package/src/engine/graphics/geometry/computeMeshSurfaceArea.js +1 -1
- package/src/engine/graphics/load_and_set_cubemap_v0.d.ts +5 -0
- package/src/engine/graphics/load_and_set_cubemap_v0.d.ts.map +1 -1
- package/src/engine/graphics/load_and_set_cubemap_v0.js +26 -21
- package/src/engine/graphics/particles/particular/group/ParticleGroup.d.ts.map +1 -1
- package/src/engine/graphics/particles/particular/group/ParticleGroup.js +19 -8
- package/src/engine/graphics/particles/particular/group/optimizeCommandQueue.js +1 -1
- package/src/engine/graphics/texture/atlas/AtlasLookupTexture.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/AtlasLookupTexture.js +10 -3
- package/src/engine/graphics/texture/atlas/TextureAtlas.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/TextureAtlas.js +4 -1
- package/src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.d.ts.map +1 -1
- package/src/engine/graphics/texture/atlas/gpu/WebGLTextureAtlas.js +11 -0
- package/src/engine/graphics/texture/cubemap/load_environment_map.d.ts.map +1 -1
- package/src/engine/graphics/texture/cubemap/load_environment_map.js +5 -2
- package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_Chamfer.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/distance/computeSignedDistanceField_Chamfer.js +26 -18
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.d.ts +15 -2
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/filter/sampler2d_blur_gaussian.js +29 -3
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.d.ts +11 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.d.ts.map +1 -1
- package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_lanczos.js +158 -171
- package/src/engine/graphics/texture/sampler/sampler2d_paint.js +1 -1
- package/src/engine/graphics/texture/sampler/util/bitSet2Sampler2D.js +2 -2
- package/src/engine/input/ecs/util/TopDownCameraControllerHelper.d.ts.map +1 -1
- package/src/engine/input/ecs/util/TopDownCameraControllerHelper.js +10 -3
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.d.ts +14 -0
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.d.ts.map +1 -0
- package/src/engine/navigation/ecs/components/PathSerializationUpgrader_2_3.js +38 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +433 -425
- package/src/engine/physics/constraint/solve_constraints.d.ts +4 -1
- package/src/engine/physics/constraint/solve_constraints.d.ts.map +1 -1
- package/src/engine/physics/constraint/solve_constraints.js +38 -13
- package/src/engine/physics/contact/ManifoldStore.d.ts +15 -3
- package/src/engine/physics/contact/ManifoldStore.d.ts.map +1 -1
- package/src/engine/physics/contact/ManifoldStore.js +15 -3
- package/src/engine/physics/ecs/Joint.d.ts +7 -4
- package/src/engine/physics/ecs/Joint.d.ts.map +1 -1
- package/src/engine/physics/ecs/Joint.js +7 -4
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +14 -0
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +16 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/physics/fluid/ecs/FluidSystem.d.ts +3 -3
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts +15 -7
- package/src/engine/physics/narrowphase/convex_convex_manifold.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/convex_convex_manifold.js +33 -10
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +6 -2
- package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_concave.js +6 -2
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +6 -2
- package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
- package/src/engine/physics/narrowphase/refine_ray_hit.js +6 -2
- package/src/engine/physics/queries/raycast.d.ts.map +1 -1
- package/src/engine/physics/queries/raycast.js +11 -4
- package/src/engine/save/storage/IndexedDBStorage.d.ts.map +1 -1
- package/src/engine/save/storage/IndexedDBStorage.js +21 -3
- package/src/engine/simulation/Ticker.d.ts +12 -0
- package/src/engine/simulation/Ticker.d.ts.map +1 -1
- package/src/engine/simulation/Ticker.js +18 -0
- package/src/engine/sound/simulation/AcousticSimulator.d.ts.map +1 -1
- package/src/engine/sound/simulation/AcousticSimulator.js +9 -1
- package/src/engine/sound/simulation/core/VolumeField.d.ts +6 -2
- package/src/engine/sound/simulation/core/VolumeField.d.ts.map +1 -1
- package/src/engine/sound/simulation/core/VolumeField.js +6 -2
- package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts +6 -2
- package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/AcousticProbeField.js +6 -2
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.d.ts +6 -2
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/acoustic_probe_transfer.js +6 -2
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.d.ts +3 -1
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.d.ts.map +1 -1
- package/src/engine/sound/simulation/probe/bakeProbeReflectors.js +3 -1
- package/src/engine/ui/GUIEngine.d.ts.map +1 -1
- package/src/engine/ui/GUIEngine.js +8 -1
- package/src/generation/theme/TerrainTheme.d.ts.map +1 -1
- package/src/generation/theme/TerrainTheme.js +2 -1
- package/src/generation/theme/ThemeEngine.d.ts.map +1 -1
- package/src/generation/theme/ThemeEngine.js +6 -5
- package/src/view/ViewGroup.d.ts.map +1 -1
- package/src/view/ViewGroup.js +9 -7
- package/src/view/graphics/WebGLContextFailureView.d.ts +19 -0
- package/src/view/graphics/WebGLContextFailureView.d.ts.map +1 -0
- package/src/view/graphics/WebGLContextFailureView.js +76 -0
- package/src/view/minimap/Minimap.d.ts +8 -0
- package/src/view/minimap/Minimap.d.ts.map +1 -1
- package/src/view/minimap/Minimap.js +16 -3
- package/src/view/minimap/dom/MinimapMarkerView.d.ts.map +1 -1
- package/src/view/minimap/dom/MinimapMarkerView.js +3 -1
|
@@ -1,425 +1,433 @@
|
|
|
1
|
-
import { assert } from "../../../core/assert.js";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* @
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
* @
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
this.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
this.
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* @
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
*
|
|
197
|
-
*
|
|
198
|
-
*
|
|
199
|
-
*
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* @type {Signal}
|
|
221
|
-
*/
|
|
222
|
-
this.
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Fires
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
*
|
|
269
|
-
* @param {
|
|
270
|
-
*/
|
|
271
|
-
|
|
272
|
-
this.peer.
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
*
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
*
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
*
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
this
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
1
|
+
import { assert } from "../../../core/assert.js";
|
|
2
|
+
import { Uint32Map } from "../../../core/collection/Uint32Map.js";
|
|
3
|
+
import Signal from "../../../core/events/signal/Signal.js";
|
|
4
|
+
import { AlwaysRelevantScope } from "../replication/ScopeFilter.js";
|
|
5
|
+
import { RewindEngine } from "../sim/RewindEngine.js";
|
|
6
|
+
import { InputRing } from "../state/InputRing.js";
|
|
7
|
+
import { TimeDilation } from "../time/TimeDilation.js";
|
|
8
|
+
import { NetworkPeer } from "./NetworkPeer.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Client-side orchestrator for the predict-reconcile model.
|
|
12
|
+
*
|
|
13
|
+
* Encapsulates the algorithm previously inlined in
|
|
14
|
+
* `app/src/prototype/network_prototype_prediction.js`:
|
|
15
|
+
*
|
|
16
|
+
* - Per tick: sample input, store it in the {@link InputRing}, fire
|
|
17
|
+
* {@link onPredict} so the user's game code can execute SimAction(s)
|
|
18
|
+
* representing the input. The action gets recorded into the local
|
|
19
|
+
* action_log via the executor (capturing prior-state for later rewind),
|
|
20
|
+
* and goes out over the action stream when `peer.end_tick` flushes it.
|
|
21
|
+
*
|
|
22
|
+
* - On AUTH_STATE arrival: rewind the world to end-of-(server_frame - 1)
|
|
23
|
+
* via the {@link RewindEngine}, deserialize the server's authoritative
|
|
24
|
+
* state bytes via {@link onApplyAuthState}, then replay forward by
|
|
25
|
+
* firing {@link onReplay} for every frame in the InputRing between
|
|
26
|
+
* `server_frame + 1` and the client's current frame. The user's
|
|
27
|
+
* {@link onReplay} handler re-executes the same SimActions the original
|
|
28
|
+
* predict path produced.
|
|
29
|
+
*
|
|
30
|
+
* - A no-op short-circuit is built in: before doing the rewind+replay, the
|
|
31
|
+
* orchestrator gives the user a chance to compute what the replay WOULD
|
|
32
|
+
* produce via {@link onComputeExpected}; if that matches the current
|
|
33
|
+
* local state within a tolerance, the rewind is skipped entirely. This
|
|
34
|
+
* keeps the steady-state reconciliation loop from oscillating.
|
|
35
|
+
*
|
|
36
|
+
* The orchestrator does NOT bake in input encoding — the user provides
|
|
37
|
+
* serialize/deserialize/apply hooks. This keeps `MovePlayerAction` (and
|
|
38
|
+
* any other input action shape) decoupled from the orchestrator.
|
|
39
|
+
*
|
|
40
|
+
* @author Alex Goldring
|
|
41
|
+
* @copyright Company Named Limited (c) 2026
|
|
42
|
+
*/
|
|
43
|
+
export class ServerAuthoritativeClient {
|
|
44
|
+
/**
|
|
45
|
+
* Newest server frame already reconciled, per network id. A single
|
|
46
|
+
* watermark for the whole client would let the first entity in a frame
|
|
47
|
+
* consume it and silently drop every other entity in that same frame.
|
|
48
|
+
* Uint32Map reports an absent key as -1, which is the correct starting
|
|
49
|
+
* watermark.
|
|
50
|
+
* @type {Uint32Map}
|
|
51
|
+
*/
|
|
52
|
+
#last_reconciled_frame = new Uint32Map();
|
|
53
|
+
#current_frame;
|
|
54
|
+
#expected_scratch;
|
|
55
|
+
#measured_scratch;
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {{
|
|
60
|
+
* world: EntityComponentDataset,
|
|
61
|
+
* binary_registry: BinarySerializationRegistry,
|
|
62
|
+
* replicated_components: Function[],
|
|
63
|
+
* action_classes: Function[],
|
|
64
|
+
* scope_filter?: object,
|
|
65
|
+
* frame_capacity?: number,
|
|
66
|
+
* initial_buffer_size?: number,
|
|
67
|
+
* input_ring_capacity?: number,
|
|
68
|
+
* input_byte_size?: number,
|
|
69
|
+
* reconcile_epsilon?: number,
|
|
70
|
+
* time_dilation?: TimeDilation,
|
|
71
|
+
* }} options
|
|
72
|
+
*
|
|
73
|
+
* `time_dilation`: optional pre-configured {@link TimeDilation}.
|
|
74
|
+
* On TIME_DILATION feedback the client recomputes
|
|
75
|
+
* {@link dilation_factor} for the caller to scale its tick cadence.
|
|
76
|
+
* Defaults to a stock instance; `dilation_factor` is 1.0 until the
|
|
77
|
+
* first feedback arrives.
|
|
78
|
+
*/
|
|
79
|
+
constructor({
|
|
80
|
+
world,
|
|
81
|
+
binary_registry,
|
|
82
|
+
replicated_components,
|
|
83
|
+
action_classes,
|
|
84
|
+
scope_filter = new AlwaysRelevantScope(),
|
|
85
|
+
frame_capacity = 32,
|
|
86
|
+
initial_buffer_size = 1024,
|
|
87
|
+
input_ring_capacity = 256,
|
|
88
|
+
input_byte_size = 4,
|
|
89
|
+
reconcile_epsilon = 1e-4,
|
|
90
|
+
time_dilation = new TimeDilation(),
|
|
91
|
+
}) {
|
|
92
|
+
/** @type {NetworkPeer} */
|
|
93
|
+
this.peer = new NetworkPeer({
|
|
94
|
+
world,
|
|
95
|
+
binary_registry,
|
|
96
|
+
replicated_components,
|
|
97
|
+
action_classes,
|
|
98
|
+
scope_filter,
|
|
99
|
+
frame_capacity,
|
|
100
|
+
initial_buffer_size,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
/** @type {EntityComponentDataset} */
|
|
104
|
+
this.world = world;
|
|
105
|
+
/** @type {SimActionExecutor} */
|
|
106
|
+
this.executor = this.peer.executor;
|
|
107
|
+
/** @type {ReplicationSlotTable} */
|
|
108
|
+
this.slot_table = this.peer.slot_table;
|
|
109
|
+
/** @type {ActionLog} */
|
|
110
|
+
this.action_log = this.peer.action_log;
|
|
111
|
+
/** @type {SimActionRegistry} */
|
|
112
|
+
this.action_registry = this.peer.action_registry;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Per-frame input ring. Sized to hold roughly 2× RTT_in_frames of
|
|
116
|
+
* history so reconciliation can always find the inputs to replay.
|
|
117
|
+
* @type {InputRing}
|
|
118
|
+
*/
|
|
119
|
+
this.input_ring = new InputRing({
|
|
120
|
+
frame_capacity: input_ring_capacity,
|
|
121
|
+
initial_buffer_size: input_byte_size,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
/** @type {RewindEngine} */
|
|
125
|
+
this.rewind_engine = new RewindEngine({
|
|
126
|
+
action_log: this.action_log,
|
|
127
|
+
world: this.world,
|
|
128
|
+
component_registry: this.peer.component_registry,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Tolerance for the no-op reconciliation short-circuit. If the user's
|
|
133
|
+
* {@link onComputeExpected} returns a state that matches the local
|
|
134
|
+
* state to within this epsilon, the rewind+replay is skipped.
|
|
135
|
+
* @type {number}
|
|
136
|
+
*/
|
|
137
|
+
this.reconcile_epsilon = reconcile_epsilon;
|
|
138
|
+
|
|
139
|
+
// State
|
|
140
|
+
this.#current_frame = -1;
|
|
141
|
+
|
|
142
|
+
// Counters
|
|
143
|
+
/** @type {number} */
|
|
144
|
+
this.reconcile_count = 0;
|
|
145
|
+
/** @type {number} */
|
|
146
|
+
this.replay_frame_count = 0;
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Adaptive tick-rate controller. Server sends observed input-buffer
|
|
150
|
+
* depth via the TIME_DILATION packet; this maps it to a tick-period
|
|
151
|
+
* multiplier {@link dilation_factor}.
|
|
152
|
+
* @type {TimeDilation}
|
|
153
|
+
*/
|
|
154
|
+
this.time_dilation = time_dilation;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Most recent buffer depth reported by the server. -1 before any
|
|
158
|
+
* feedback has arrived.
|
|
159
|
+
* @type {number}
|
|
160
|
+
*/
|
|
161
|
+
this.last_buffer_depth = -1;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Tick-period multiplier from the latest TIME_DILATION feedback.
|
|
165
|
+
* `< 1.0` ⇒ caller should run faster, `> 1.0` ⇒ slower. 1.0 until
|
|
166
|
+
* the first feedback arrives.
|
|
167
|
+
*
|
|
168
|
+
* scheduled_tick_period_ms = TICK_PERIOD_MS * client.dilation_factor;
|
|
169
|
+
*
|
|
170
|
+
* @type {number}
|
|
171
|
+
*/
|
|
172
|
+
this.dilation_factor = 1.0;
|
|
173
|
+
|
|
174
|
+
this.peer.onTimeDilationFeedback.add((_peer_id, buffer_depth) => {
|
|
175
|
+
this.last_buffer_depth = buffer_depth;
|
|
176
|
+
this.dilation_factor = this.time_dilation.compute(buffer_depth);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Fires once per tick during prediction. Args: `(frame_number,
|
|
181
|
+
* input_writer)` where `input_writer(write_fn)` records the input
|
|
182
|
+
* bytes into the InputRing. The user's handler should:
|
|
183
|
+
* 1. Sample input.
|
|
184
|
+
* 2. Write input bytes via `input_writer(buf => buf.writeXxx(...))`.
|
|
185
|
+
* 3. Execute SimAction(s) representing the input via
|
|
186
|
+
* `client.executor.execute(action)`.
|
|
187
|
+
* @type {Signal}
|
|
188
|
+
*/
|
|
189
|
+
this.onPredict = new Signal();
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Fires once per frame during reconciliation replay. Args:
|
|
193
|
+
* `(frame_number, input_reader)` where `input_reader` is a
|
|
194
|
+
* BinaryBuffer positioned at the start of that frame's stored
|
|
195
|
+
* input bytes (or `null` if the frame's input wasn't recorded —
|
|
196
|
+
* the handler must short-circuit in that case). The user's handler
|
|
197
|
+
* should re-execute the SAME SimAction(s) that the predict handler
|
|
198
|
+
* produced for the input.
|
|
199
|
+
* @type {Signal}
|
|
200
|
+
*/
|
|
201
|
+
this.onReplay = new Signal();
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Fires when AUTH_STATE arrives, BEFORE the rewind+replay is
|
|
205
|
+
* decided. Args: `(server_frame, network_id, buffer)`.
|
|
206
|
+
*
|
|
207
|
+
* The handler should peek at the server's bytes (the buffer is
|
|
208
|
+
* positioned at the start of the auth payload; the handler may
|
|
209
|
+
* advance and restore `buffer.position`) and compute what the
|
|
210
|
+
* client's predicted state at the *current* frame WOULD be if a
|
|
211
|
+
* full rewind+replay were performed, returning a 1D scalar or
|
|
212
|
+
* `null` to skip the short-circuit. The orchestrator compares
|
|
213
|
+
* the returned value to {@link onMeasureCurrent}'s return value
|
|
214
|
+
* and short-circuits the rewind if they match within
|
|
215
|
+
* {@link reconcile_epsilon}.
|
|
216
|
+
*
|
|
217
|
+
* Game code that doesn't want the optimization can simply not
|
|
218
|
+
* subscribe to this; the rewind+replay then runs unconditionally.
|
|
219
|
+
*
|
|
220
|
+
* @type {Signal}
|
|
221
|
+
*/
|
|
222
|
+
this.onComputeExpected = new Signal();
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Fires alongside {@link onComputeExpected} to measure the current
|
|
226
|
+
* local state for comparison. Args: `(network_id)`. Returns a
|
|
227
|
+
* scalar.
|
|
228
|
+
* @type {Signal}
|
|
229
|
+
*/
|
|
230
|
+
this.onMeasureCurrent = new Signal();
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Fires inside __handle_auth_state AFTER the no-op short-circuit
|
|
234
|
+
* decided a rewind is needed but BEFORE the rewind starts.
|
|
235
|
+
* Args: `(server_frame, network_id)`. Used by higher-level wiring
|
|
236
|
+
* (e.g. {@link NetworkSession}) to bring the live world into
|
|
237
|
+
* canonical form before `RewindEngine.rewind_to` reads it.
|
|
238
|
+
* @type {Signal}
|
|
239
|
+
*/
|
|
240
|
+
this.onBeforeReconcile = new Signal();
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Fires on AUTH_STATE arrival, after the no-op check decided to
|
|
244
|
+
* actually do the rewind+replay. Args: `(server_frame,
|
|
245
|
+
* network_id, buffer)` where buffer is positioned at the auth
|
|
246
|
+
* payload bytes. The handler should deserialize the bytes into
|
|
247
|
+
* the world (the rewind has already happened; the buffer's bytes
|
|
248
|
+
* ARE the new authoritative state at server_frame).
|
|
249
|
+
* @type {Signal}
|
|
250
|
+
*/
|
|
251
|
+
this.onApplyAuthState = new Signal();
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Fires after each completed reconciliation (rewind + apply + replay).
|
|
255
|
+
* Diagnostic. Args: `(server_frame, replay_count)`.
|
|
256
|
+
* @type {Signal}
|
|
257
|
+
*/
|
|
258
|
+
this.onReconcileComplete = new Signal();
|
|
259
|
+
|
|
260
|
+
// Wire the AUTH_STATE handler to the algorithm above.
|
|
261
|
+
this.peer.onAuthState.add((_peer_id, server_frame, network_id, buffer) => {
|
|
262
|
+
this.#handle_auth_state(server_frame, network_id, buffer);
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Connect to a server peer over a transport.
|
|
268
|
+
* @param {number} peer_id
|
|
269
|
+
* @param {object} transport
|
|
270
|
+
*/
|
|
271
|
+
connect_to_server(peer_id, transport) {
|
|
272
|
+
this.peer.connect_peer(peer_id, transport);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Disconnect from a server peer.
|
|
277
|
+
* @param {number} peer_id
|
|
278
|
+
*/
|
|
279
|
+
disconnect_from_server(peer_id) {
|
|
280
|
+
this.peer.disconnect_peer(peer_id);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Channel accessor for manual packet plumbing.
|
|
284
|
+
* @param {number} peer_id */
|
|
285
|
+
channel_for(peer_id) {
|
|
286
|
+
return this.peer.channel_for(peer_id);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Run one client-side prediction tick.
|
|
291
|
+
*
|
|
292
|
+
* 1. Open action_log frame.
|
|
293
|
+
* 2. Fire {@link onPredict} so the user samples input, records it in the
|
|
294
|
+
* InputRing, and executes SimAction(s).
|
|
295
|
+
* 3. Close action_log frame (this flushes the action stream to all
|
|
296
|
+
* connected peers via `peer.end_tick`).
|
|
297
|
+
*
|
|
298
|
+
* @param {number} current_frame
|
|
299
|
+
*/
|
|
300
|
+
tick(current_frame) {
|
|
301
|
+
assert.isNonNegativeInteger(current_frame, 'current_frame');
|
|
302
|
+
this.#current_frame = current_frame;
|
|
303
|
+
|
|
304
|
+
this.peer.begin_tick(current_frame);
|
|
305
|
+
try {
|
|
306
|
+
const input_writer = (cb) => this.input_ring.write(current_frame, cb);
|
|
307
|
+
this.onPredict.send2(current_frame, input_writer);
|
|
308
|
+
} finally {
|
|
309
|
+
this.peer.end_tick();
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Whether the InputRing has stored input bytes for the given frame.
|
|
315
|
+
* Useful in {@link onReplay} handlers that need to short-circuit
|
|
316
|
+
* frames with no recorded input.
|
|
317
|
+
*
|
|
318
|
+
* @param {number} frame
|
|
319
|
+
* @returns {boolean}
|
|
320
|
+
*/
|
|
321
|
+
has_input_for(frame) {
|
|
322
|
+
return this.input_ring.has(frame);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Read-only access to the InputRing buffer for a given frame. Position
|
|
327
|
+
* is reset to 0 by the call.
|
|
328
|
+
*
|
|
329
|
+
* @param {number} frame
|
|
330
|
+
* @returns {BinaryBuffer}
|
|
331
|
+
*/
|
|
332
|
+
input_for(frame) {
|
|
333
|
+
return this.input_ring.buffer_for(frame);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** @private */
|
|
337
|
+
#handle_auth_state(server_frame, network_id, buffer) {
|
|
338
|
+
// Out-of-order safety: never reconcile to an older frame than the
|
|
339
|
+
// last one we already reconciled to.
|
|
340
|
+
if (server_frame <= this.#last_reconciled_frame.get(network_id)) return;
|
|
341
|
+
this.#last_reconciled_frame.set(network_id, server_frame);
|
|
342
|
+
|
|
343
|
+
const local = this.slot_table.entity_for(network_id);
|
|
344
|
+
if (local < 0) return;
|
|
345
|
+
|
|
346
|
+
// No-op short-circuit. If the user provided both
|
|
347
|
+
// onComputeExpected and onMeasureCurrent, compare. If the values
|
|
348
|
+
// match within reconcile_epsilon, skip the rewind entirely.
|
|
349
|
+
if (this.onComputeExpected.hasHandlers() && this.onMeasureCurrent.hasHandlers()) {
|
|
350
|
+
// Capture the buffer position so onComputeExpected can peek
|
|
351
|
+
// without disturbing the actual apply step below.
|
|
352
|
+
const peek_pos = buffer.position;
|
|
353
|
+
let expected = null;
|
|
354
|
+
this.onComputeExpected.send3(server_frame, network_id, buffer);
|
|
355
|
+
buffer.position = peek_pos;
|
|
356
|
+
// The handler can stash the computed value on the buffer or via
|
|
357
|
+
// a side channel; here we use a sentinel attached to the signal
|
|
358
|
+
// result. Simpler protocol: handler stashes value in
|
|
359
|
+
// this.#expected_scratch. Let's do that.
|
|
360
|
+
expected = this.#expected_scratch;
|
|
361
|
+
this.#expected_scratch = null;
|
|
362
|
+
|
|
363
|
+
let measured = null;
|
|
364
|
+
this.onMeasureCurrent.send1(network_id);
|
|
365
|
+
measured = this.#measured_scratch;
|
|
366
|
+
this.#measured_scratch = null;
|
|
367
|
+
|
|
368
|
+
if (expected !== null && measured !== null
|
|
369
|
+
&& Math.abs(expected - measured) < this.reconcile_epsilon) {
|
|
370
|
+
return; // Matches — no rewind needed.
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
this.onBeforeReconcile.send2(server_frame, network_id);
|
|
375
|
+
|
|
376
|
+
// Real divergence: rewind, apply server's bytes, replay forward.
|
|
377
|
+
const rewind_target = server_frame - 1;
|
|
378
|
+
if (this.#current_frame > rewind_target) {
|
|
379
|
+
try {
|
|
380
|
+
this.rewind_engine.rewind_to(this.#current_frame, rewind_target);
|
|
381
|
+
} catch (e) {
|
|
382
|
+
// The action_log probably rolled past server_frame. The
|
|
383
|
+
// server would normally back-fill us via STATE_BURST; for now
|
|
384
|
+
// skip this reconciliation.
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Apply the server's authoritative state bytes. The handler does the
|
|
390
|
+
// adapter-specific deserialize.
|
|
391
|
+
this.onApplyAuthState.send3(server_frame, network_id, buffer);
|
|
392
|
+
|
|
393
|
+
// Replay forward from (server_frame + 1) through current_frame.
|
|
394
|
+
let replay_count = 0;
|
|
395
|
+
for (let f = server_frame + 1; f <= this.#current_frame; f++) {
|
|
396
|
+
this.action_log.begin_frame(f);
|
|
397
|
+
try {
|
|
398
|
+
let input_reader = null;
|
|
399
|
+
if (this.input_ring.has(f)) {
|
|
400
|
+
input_reader = this.input_ring.buffer_for(f);
|
|
401
|
+
input_reader.position = 0;
|
|
402
|
+
}
|
|
403
|
+
this.onReplay.send2(f, input_reader);
|
|
404
|
+
} finally {
|
|
405
|
+
this.action_log.end_frame();
|
|
406
|
+
}
|
|
407
|
+
replay_count++;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
this.reconcile_count++;
|
|
411
|
+
this.replay_frame_count += replay_count;
|
|
412
|
+
this.onReconcileComplete.send2(server_frame, replay_count);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Helper for the {@link onComputeExpected} handler: stash the
|
|
417
|
+
* computed scalar so the orchestrator can read it. Called inside the
|
|
418
|
+
* handler.
|
|
419
|
+
* @param {number} value
|
|
420
|
+
*/
|
|
421
|
+
set_expected(value) {
|
|
422
|
+
this.#expected_scratch = value;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Helper for the {@link onMeasureCurrent} handler: stash the measured
|
|
427
|
+
* scalar so the orchestrator can read it. Called inside the handler.
|
|
428
|
+
* @param {number} value
|
|
429
|
+
*/
|
|
430
|
+
set_measured(value) {
|
|
431
|
+
this.#measured_scratch = value;
|
|
432
|
+
}
|
|
433
|
+
}
|