@woosh/meep-engine 3.45.0 → 3.47.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/build/bundle-worker-terrain.js +1 -1
- package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
- package/editor/actions/concrete/TransformModifyAction.js +10 -2
- package/editor/ecs/component/FieldDescriptor.js +15 -0
- package/editor/ecs/component/createObjectEditor.js +41 -12
- package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts.map +1 -1
- package/editor/ecs/component/editors/ecs/Transform64Editor.js +77 -70
- package/editor/tools/TransformToolV2.d.ts.map +1 -1
- package/editor/tools/TransformToolV2.js +4 -0
- package/editor/tools/v2/TransformControls.d.ts.map +1 -1
- package/editor/tools/v2/TransformControls.js +7 -0
- package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
- package/editor/view/ecs/EntityEditor.js +4 -2
- package/package.json +1 -1
- package/src/core/assert.d.ts.map +1 -1
- package/src/core/assert.js +5 -1
- package/src/core/binary/hash/XXH128Hash.d.ts +1 -37
- package/src/core/binary/hash/XXH128Hash.d.ts.map +1 -1
- package/src/core/binary/hash/XXH128Hash.js +4 -55
- package/src/core/binary/hash/XXH3_SECRET.d.ts +1 -12
- package/src/core/binary/hash/XXH3_SECRET.d.ts.map +1 -1
- package/src/core/binary/hash/XXH3_SECRET.js +4 -25
- package/src/core/binary/hash/crc32.d.ts +1 -15
- package/src/core/binary/hash/crc32.d.ts.map +1 -1
- package/src/core/binary/hash/crc32.js +4 -96
- package/src/core/binary/hash/xxh3_128.d.ts +1 -9
- package/src/core/binary/hash/xxh3_128.d.ts.map +1 -1
- package/src/core/binary/hash/xxh3_128.js +4 -341
- package/src/core/binary/hash/xxh3_64.d.ts +1 -8
- package/src/core/binary/hash/xxh3_64.d.ts.map +1 -1
- package/src/core/binary/hash/xxh3_64.js +4 -241
- package/src/core/binary/hash/xxh3_common.d.ts +1 -223
- package/src/core/binary/hash/xxh3_common.d.ts.map +1 -1
- package/src/core/binary/hash/xxh3_common.js +90 -476
- package/src/core/collection/array/array_insert_at.d.ts +16 -0
- package/src/core/collection/array/array_insert_at.d.ts.map +1 -0
- package/src/core/collection/array/array_insert_at.js +32 -0
- package/src/core/collection/array/array_remove_at.d.ts +15 -0
- package/src/core/collection/array/array_remove_at.d.ts.map +1 -0
- package/src/core/collection/array/array_remove_at.js +30 -0
- package/src/core/collection/array/combine_hash.d.ts +6 -0
- package/src/core/collection/array/combine_hash.d.ts.map +1 -1
- package/src/core/collection/array/combine_hash.js +6 -0
- package/src/core/function/FunctionCompiler.js +2 -2
- package/src/core/geom/3d/mat4/m4_multiply.d.ts +3 -0
- package/src/core/geom/3d/mat4/m4_multiply.d.ts.map +1 -1
- package/src/core/geom/3d/mat4/m4_multiply.js +3 -0
- package/src/core/geom/3d/mat4/m4_multiply_f64.d.ts +30 -0
- package/src/core/geom/3d/mat4/m4_multiply_f64.d.ts.map +1 -0
- package/src/core/geom/3d/mat4/m4_multiply_f64.js +106 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts +37 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts.map +1 -0
- package/src/core/geom/3d/quaternion/quat3_look_rotation.js +172 -0
- package/src/core/geom/ConicRay.js +2 -2
- package/src/core/geom/Quaternion.d.ts +1 -0
- package/src/core/geom/Quaternion.d.ts.map +1 -1
- package/src/core/geom/Quaternion.js +12 -48
- package/src/core/geom/Vector4.d.ts.map +1 -1
- package/src/core/geom/Vector4.js +8 -5
- package/src/core/math/hash/XXH128Hash.d.ts +38 -0
- package/src/core/math/hash/XXH128Hash.d.ts.map +1 -0
- package/src/core/math/hash/XXH128Hash.js +55 -0
- package/src/core/math/hash/XXH3_SECRET.d.ts +13 -0
- package/src/core/math/hash/XXH3_SECRET.d.ts.map +1 -0
- package/src/core/math/hash/XXH3_SECRET.js +25 -0
- package/src/core/math/hash/crc32.d.ts +16 -0
- package/src/core/math/hash/crc32.d.ts.map +1 -0
- package/src/core/math/hash/crc32.js +96 -0
- package/src/core/math/hash/xxh3_128.d.ts +10 -0
- package/src/core/math/hash/xxh3_128.d.ts.map +1 -0
- package/src/core/math/hash/xxh3_128.js +341 -0
- package/src/core/math/hash/xxh3_64.d.ts +9 -0
- package/src/core/math/hash/xxh3_64.d.ts.map +1 -0
- package/src/core/math/hash/xxh3_64.js +241 -0
- package/src/core/math/hash/xxh3_common.d.ts +224 -0
- package/src/core/math/hash/xxh3_common.d.ts.map +1 -0
- package/src/core/math/hash/xxh3_common.js +476 -0
- package/src/core/math/lookup/ParameterLookupTable.js +2 -2
- package/src/core/model/node-graph/node/NodeInstancePortReference.js +2 -2
- package/src/core/model/reactive/model/ReactiveBinaryExpression.js +2 -2
- package/src/core/model/reactive/model/terminal/ReactiveReference.js +2 -2
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +9 -4
- package/src/engine/asset/AssetDescription.js +2 -2
- package/src/engine/ecs/animation/Animation.js +2 -2
- package/src/engine/ecs/animation/AnimationClip.d.ts.map +1 -1
- package/src/engine/ecs/animation/AnimationClip.js +7 -5
- package/src/engine/ecs/gui/GUIElement.d.ts.map +1 -1
- package/src/engine/ecs/gui/GUIElement.js +7 -5
- package/src/engine/ecs/gui/hud/HeadsUpDisplay.js +2 -2
- package/src/engine/ecs/gui/position/ViewportPosition.d.ts.map +1 -1
- package/src/engine/ecs/gui/position/ViewportPosition.js +11 -7
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +10 -0
- package/src/engine/ecs/transform/Transform64.d.ts +1 -1
- package/src/engine/ecs/transform/Transform64.d.ts.map +1 -1
- package/src/engine/ecs/transform/Transform64.js +5 -4
- package/src/engine/ecs/transform/t64_look_rotation.d.ts +4 -4
- package/src/engine/ecs/transform/t64_look_rotation.d.ts.map +1 -1
- package/src/engine/ecs/transform/t64_look_rotation.js +7 -18
- package/src/engine/graphics/ecs/animation/animator/AnimationClip.d.ts.map +1 -1
- package/src/engine/graphics/ecs/animation/animator/AnimationClip.js +8 -5
- package/src/engine/graphics/ecs/animation/animator/AnimationClipDefinition.js +2 -2
- package/src/engine/graphics/ecs/animation/animator/AnimationNotification.js +2 -2
- package/src/engine/graphics/ecs/animation/animator/AnimationNotificationDefinition.js +2 -2
- package/src/engine/graphics/ecs/animation/animator/graph/definition/AnimationGraphDefinition.js +2 -2
- package/src/engine/graphics/ecs/animation/animator/graph/definition/AnimationStateDefinition.js +2 -2
- package/src/engine/graphics/ecs/animation/animator/graph/definition/AnimationTransitionDefinition.d.ts.map +1 -1
- package/src/engine/graphics/ecs/animation/animator/graph/definition/AnimationTransitionDefinition.js +8 -5
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
- package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +30 -1
- package/src/engine/graphics/ecs/decal/v2/Decal.d.ts.map +1 -1
- package/src/engine/graphics/ecs/decal/v2/Decal.js +9 -8
- package/src/engine/graphics/render/frame_graph/resource/TextureResourceDescriptor.d.ts.map +1 -1
- package/src/engine/graphics/render/frame_graph/resource/TextureResourceDescriptor.js +9 -6
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +6 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +72 -29
- package/src/engine/graphics3/ParticipatingMedia.js +204 -204
- package/src/engine/graphics3/animation/AnimationClips.d.ts +5 -0
- package/src/engine/graphics3/animation/AnimationClips.d.ts.map +1 -1
- package/src/engine/graphics3/animation/AnimationClips.js +5 -0
- package/src/engine/grid/obstacle/GridObstacle.js +2 -2
- package/src/engine/input/devices/KeyboardDevice.d.ts +1 -0
- package/src/engine/input/devices/KeyboardDevice.d.ts.map +1 -1
- package/src/engine/input/devices/KeyboardDevice.js +7 -6
- package/src/engine/intelligence/behavior/selector/WeightedElement.js +2 -2
- package/src/engine/interpolation/Interpolated.js +2 -2
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
- package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +15 -2
- package/src/engine/network/CONGESTION_CONTROL.md +9 -9
- package/src/engine/network/MESSAGE_TRANSPORT.md +364 -0
- package/src/engine/network/NetworkSession.d.ts +31 -11
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +453 -286
- package/src/engine/network/README.md +77 -27
- package/src/engine/network/SMOOTHNESS_DESIGN.md +13 -5
- package/src/engine/network/diagnostics/BandwidthMeter.d.ts +1 -1
- package/src/engine/network/diagnostics/BandwidthMeter.js +1 -1
- package/src/engine/network/orchestrator/HostJoin.d.ts +111 -0
- package/src/engine/network/orchestrator/HostJoin.d.ts.map +1 -0
- package/src/engine/network/orchestrator/HostJoin.js +271 -0
- package/src/engine/network/orchestrator/NetworkPeer.d.ts +151 -112
- package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/NetworkPeer.js +450 -499
- package/src/engine/network/orchestrator/PendingSlices.d.ts +30 -0
- package/src/engine/network/orchestrator/PendingSlices.d.ts.map +1 -0
- package/src/engine/network/orchestrator/PendingSlices.js +83 -0
- package/src/engine/network/orchestrator/ReliableFrames.d.ts +34 -0
- package/src/engine/network/orchestrator/ReliableFrames.d.ts.map +1 -0
- package/src/engine/network/orchestrator/ReliableFrames.js +96 -0
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +77 -10
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +236 -61
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +20 -6
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
- package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +33 -8
- package/src/engine/network/replication/Replicator.d.ts.map +1 -1
- package/src/engine/network/replication/Replicator.js +7 -1
- package/src/engine/network/sim/ActionLog.d.ts +32 -5
- package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
- package/src/engine/network/sim/ActionLog.js +47 -5
- package/src/engine/network/sim/SimActionExecutor.d.ts +42 -2
- package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
- package/src/engine/network/sim/SimActionExecutor.js +100 -4
- package/src/engine/network/sim/Snapshotter.d.ts +12 -0
- package/src/engine/network/sim/Snapshotter.d.ts.map +1 -1
- package/src/engine/network/sim/Snapshotter.js +43 -0
- package/src/engine/network/state/Baseline.d.ts +1 -1
- package/src/engine/network/state/Baseline.js +1 -1
- package/src/engine/network/state/FrameBufferRing.d.ts +25 -6
- package/src/engine/network/state/FrameBufferRing.d.ts.map +1 -1
- package/src/engine/network/state/FrameBufferRing.js +51 -6
- package/src/engine/network/state/InputRing.d.ts +4 -0
- package/src/engine/network/state/InputRing.d.ts.map +1 -1
- package/src/engine/network/state/InputRing.js +7 -0
- package/src/engine/network/transport/DeferredTransport.d.ts.map +1 -1
- package/src/engine/network/transport/DeferredTransport.js +1 -0
- package/src/engine/network/transport/LoopbackTransport.d.ts.map +1 -1
- package/src/engine/network/transport/LoopbackTransport.js +4 -2
- package/src/engine/network/transport/Transport.d.ts +7 -0
- package/src/engine/network/transport/Transport.d.ts.map +1 -1
- package/src/engine/network/transport/Transport.js +8 -0
- package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/NodeUDPTransport.js +20 -5
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +8 -0
- package/src/engine/network/transport/adapters/WebTransportTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/WebTransportTransport.js +6 -0
- package/src/engine/network/transport/connection/Connection.d.ts +191 -0
- package/src/engine/network/transport/connection/Connection.d.ts.map +1 -0
- package/src/engine/network/transport/connection/Connection.js +1262 -0
- package/src/engine/network/transport/connection/RangeSet.d.ts +94 -0
- package/src/engine/network/transport/connection/RangeSet.d.ts.map +1 -0
- package/src/engine/network/transport/connection/RangeSet.js +260 -0
- package/src/engine/network/transport/connection/RxStream.d.ts +48 -0
- package/src/engine/network/transport/connection/RxStream.d.ts.map +1 -0
- package/src/engine/network/transport/connection/RxStream.js +211 -0
- package/src/engine/network/transport/connection/TxStream.d.ts +90 -0
- package/src/engine/network/transport/connection/TxStream.d.ts.map +1 -0
- package/src/engine/network/transport/connection/TxStream.js +243 -0
- package/src/engine/network/transport/connection/bytes_copy.d.ts +17 -0
- package/src/engine/network/transport/connection/bytes_copy.d.ts.map +1 -0
- package/src/engine/network/transport/connection/bytes_copy.js +34 -0
- package/src/engine/network/transport/connection/varint.d.ts +46 -0
- package/src/engine/network/transport/connection/varint.d.ts.map +1 -0
- package/src/engine/network/transport/connection/varint.js +107 -0
- package/src/engine/physics/cloth/ecs/Cloth.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/Cloth.js +7 -5
- package/src/engine/physics/cloth/ecs/ClothCollider.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothCollider.js +7 -5
- package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts.map +1 -1
- package/src/engine/physics/cloth/ecs/ClothDynamics.js +20 -11
- package/src/engine/physics/fluid/FluidField.d.ts.map +1 -1
- package/src/engine/physics/fluid/FluidField.js +7 -5
- package/src/engine/physics/fluid/ecs/FluidComponent.d.ts.map +1 -1
- package/src/engine/physics/fluid/ecs/FluidComponent.js +19 -11
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/physics/fluid/effector/AbstractFluidEffector.d.ts +2 -2
- package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +2 -2
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +11 -7
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/ImpulseFluidEffector.js +11 -7
- package/src/engine/physics/fluid/effector/WakeFluidEffector.d.ts.map +1 -1
- package/src/engine/physics/fluid/effector/WakeFluidEffector.js +16 -9
- package/src/engine/sound/ecs/audio/AudioEmitter.d.ts +38 -7
- package/src/engine/sound/ecs/audio/AudioEmitter.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitter.js +47 -14
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.js +5 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts +18 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.js +48 -0
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +12 -2
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +173 -9
- package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts +9 -3
- package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts.map +1 -1
- package/src/engine/sound/ecs/audio/LiveEmitterSet.js +28 -8
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts +8 -0
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTrigger.js +192 -191
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.js +5 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts +18 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.js +51 -0
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts.map +1 -1
- package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.js +15 -1
- package/src/engine/sound/simulation/core/AcousticMedium.js +2 -2
- package/src/engine/sound/simulation/definition/AcousticDirectivity.js +2 -2
- package/src/engine/sound/simulation/definition/AcousticMaterial.d.ts.map +1 -1
- package/src/engine/sound/simulation/definition/AcousticMaterial.js +11 -7
- package/src/engine/sound/sopra/README.md +768 -644
- package/src/engine/sound/sopra/SopraEngine.d.ts +104 -8
- package/src/engine/sound/sopra/SopraEngine.d.ts.map +1 -1
- package/src/engine/sound/sopra/SopraEngine.js +327 -12
- package/src/engine/sound/sopra/definition/BusDefinition.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/BusDefinition.js +5 -4
- package/src/engine/sound/sopra/definition/DuckingRule.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/DuckingRule.js +7 -5
- package/src/engine/sound/sopra/definition/EventDescription.d.ts +28 -2
- package/src/engine/sound/sopra/definition/EventDescription.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/EventDescription.js +77 -17
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts +10 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +77 -55
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts +31 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.js +75 -0
- package/src/engine/sound/sopra/definition/MixerSnapshot.js +2 -2
- package/src/engine/sound/sopra/definition/ParameterDefinition.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/ParameterDefinition.js +7 -5
- package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +2 -2
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.js +4 -3
- package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.js +2 -2
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +3 -3
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +16 -9
- package/src/engine/sound/sopra/definition/clip/SilenceAudioClip.js +2 -2
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -1
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.js +2 -2
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts +38 -9
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +34 -5
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts +7 -2
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +137 -120
- package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts +3 -1
- package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/EqEffect.js +146 -128
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts +1 -0
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +8 -3
- package/src/engine/sound/sopra/legacy/SoundEmitter.d.ts.map +1 -1
- package/src/engine/sound/sopra/legacy/SoundEmitter.js +357 -352
- package/src/engine/sound/sopra/legacy/SoundTrack.d.ts.map +1 -1
- package/src/engine/sound/sopra/legacy/SoundTrack.js +281 -278
- package/src/engine/sound/sopra/runtime/BusGraph.d.ts +44 -2
- package/src/engine/sound/sopra/runtime/BusGraph.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/BusGraph.js +83 -43
- package/src/engine/sound/sopra/runtime/EffectChain.d.ts +96 -0
- package/src/engine/sound/sopra/runtime/EffectChain.d.ts.map +1 -0
- package/src/engine/sound/sopra/runtime/EffectChain.js +286 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts +123 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts.map +1 -0
- package/src/engine/sound/sopra/runtime/EffectParamOverrides.js +320 -0
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts +55 -5
- package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
- package/src/engine/sound/sopra/runtime/EventInstance.js +201 -26
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts +2 -1
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts.map +1 -1
- package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +6 -1
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts +15 -0
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts.map +1 -1
- package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +32 -0
- package/src/engine/sound/sopra/util/MockAudioContext.d.ts.map +1 -1
- package/src/engine/sound/sopra/util/MockAudioContext.js +3 -0
- package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts +13 -3
- package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts.map +1 -1
- package/src/engine/sound/sopra/util/fadeOutAndStop.js +25 -13
- package/src/format/image/png/PNGReader.js +644 -644
- package/src/format/image/png/crc32.d.ts +1 -1
- package/src/format/image/png/crc32.js +7 -8
- package/src/shade/RENDERER_CONTRACT.md +6 -3
- package/src/shade/descriptor/binding/BindGroupLayoutDescriptor.js +2 -2
- package/src/shade/descriptor/binding/BindGroupLayoutEntry.d.ts.map +1 -1
- package/src/shade/descriptor/binding/BindGroupLayoutEntry.js +12 -8
- package/src/shade/descriptor/binding/StorageTextureBindingLayout.js +2 -2
- package/src/shade/descriptor/binding/TextureBindingLayout.js +2 -2
- package/src/shade/descriptor/blend/BlendComponent.js +2 -2
- package/src/shade/descriptor/pipeline/PipelineDescriptorBase.js +2 -2
- package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.js +2 -2
- package/src/shade/descriptor/pipeline/ProgrammableStage.js +2 -2
- package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.js +2 -2
- package/src/shade/descriptor/pipeline/render/ColorTargetState.js +2 -2
- package/src/shade/descriptor/pipeline/render/FragmentState.js +2 -2
- package/src/shade/descriptor/pipeline/render/PrimitiveState.js +2 -2
- package/src/shade/descriptor/pipeline/render/RenderPipelineDescriptor.d.ts.map +1 -1
- package/src/shade/descriptor/pipeline/render/RenderPipelineDescriptor.js +8 -5
- package/src/shade/descriptor/pipeline/render/VertexAttribute.js +2 -2
- package/src/shade/descriptor/texture/SamplerDescriptor.d.ts.map +1 -1
- package/src/shade/descriptor/texture/SamplerDescriptor.js +20 -12
- package/src/shade/descriptor/texture/TextureDescriptor.d.ts.map +1 -1
- package/src/shade/descriptor/texture/TextureDescriptor.js +11 -7
- package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts +24 -0
- package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts.map +1 -0
- package/src/shade/device/SHADE_DEVICE_FLOOR.js +34 -0
- package/src/shade/device/ShadeDeviceFailure.d.ts +12 -5
- package/src/shade/device/ShadeDeviceFailure.d.ts.map +1 -1
- package/src/shade/device/ShadeDeviceFailure.js +17 -8
- package/src/shade/device/ShadeDeviceFailureReason.d.ts +1 -1
- package/src/shade/device/ShadeDeviceFailureReason.js +2 -2
- package/src/shade/device/graph/BufferResourceDescriptor.d.ts.map +1 -1
- package/src/shade/device/graph/BufferResourceDescriptor.js +196 -193
- package/src/shade/device/graph/TextureResourceDescriptor.d.ts.map +1 -1
- package/src/shade/device/graph/TextureResourceDescriptor.js +178 -173
- package/src/shade/device/shade_device_floor_unmet.d.ts +13 -0
- package/src/shade/device/shade_device_floor_unmet.d.ts.map +1 -0
- package/src/shade/device/shade_device_floor_unmet.js +32 -0
- package/src/shade/device/timing/profile/sgpt_read_header.js +1 -1
- package/src/shade/device/timing/profile/sgpt_read_records.js +1 -1
- package/src/shade/device/timing/profile/sgpt_write_header.js +1 -1
- package/src/shade/device/timing/profile/sgpt_write_record.js +1 -1
- package/src/shade/renderer/Renderer.d.ts +2 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +29 -55
- package/src/shade/renderer/binding/BufferBinding.js +2 -2
- package/src/shade/renderer/buffer/GPUBufferAllocator.d.ts.map +1 -1
- package/src/shade/renderer/buffer/GPUBufferAllocator.js +9 -0
- package/src/shade/renderer/buffer/buffer_pool_ops.d.ts +21 -12
- package/src/shade/renderer/buffer/buffer_pool_ops.d.ts.map +1 -1
- package/src/shade/renderer/buffer/buffer_pool_ops.js +18 -19
- package/src/shade/renderer/camera/Camera.js +2 -2
- package/src/shade/renderer/geometry/Attribute.js +2 -2
- package/src/shade/renderer/geometry/Geometry.js +2 -2
- package/src/shade/renderer/geometry/geometry_deduplicate_vertices.js +2 -2
- package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +2 -2
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_asset_id.js +13 -13
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_directory_block_checksum.js +41 -41
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_frame_checksum.js +41 -41
- package/src/shade/renderer/geometry/virtual/format/checksum/vgeo_compute_header_checksum.js +25 -25
- package/src/shade/renderer/gpu_primitive/test/gpu_test.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/test/gpu_test.js +8 -11
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/material/StandardShadeMaterial.d.ts.map +1 -1
- package/src/shade/renderer/material/StandardShadeMaterial.js +13 -8
- package/src/shade/renderer/pool/TransientResourcePool.d.ts +24 -0
- package/src/shade/renderer/pool/TransientResourcePool.d.ts.map +1 -1
- package/src/shade/renderer/pool/TransientResourcePool.js +41 -10
- package/src/shade/renderer/scene/optimization/geometry/internal/triangle_fingerprint_stream.d.ts.map +1 -1
- package/src/shade/renderer/scene/optimization/geometry/internal/triangle_fingerprint_stream.js +14 -5
- package/src/shade/renderer/scene/optimization/sampler2d_from_image_bitmap.js +1 -1
- package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
- package/src/shade/renderer/shader/ComputeShader.js +18 -2
- package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
- package/src/shade/renderer/shader/ImageShader.js +230 -218
- package/src/shade/renderer/shader/type/WGSLStructField.js +2 -2
- package/src/shade/renderer/shader/type/WebGPUArray.js +2 -2
- package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
- package/src/shade/renderer/shader/type/WebGPUStruct.js +9 -6
- package/src/shade/renderer/texture/ShadeTexture.d.ts.map +1 -1
- package/src/shade/renderer/texture/ShadeTexture.js +17 -10
- package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +2 -2
- package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts +4 -4
- package/src/shade/renderer/water/surface/chunk_water_surface_sample.js +6 -6
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.js +46 -55
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts +3 -3
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.js +14 -4
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts +10 -1
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.js +82 -40
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts +4 -1
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.js +8 -4
- package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts +98 -86
- package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/particles/shader_water_particles.js +440 -298
- package/src/view/common/HTMLElementCacheKey.d.ts.map +1 -1
- package/src/view/common/HTMLElementCacheKey.js +4 -3
- package/src/view/graphics/GraphicsContextFailureView.d.ts +1 -2
- package/src/view/graphics/GraphicsContextFailureView.d.ts.map +1 -1
- package/src/view/graphics/GraphicsContextFailureView.js +1 -2
- package/src/view/minimap/MinimapMarker.js +2 -2
- package/src/engine/network/transport/Channel.js +0 -336
- package/src/engine/network/transport/ReliableCommandPipeline.js +0 -335
- package/src/engine/network/transport/fragments/FragmentAssembler.js +0 -350
- package/src/engine/network/transport/fragments/FragmentRetention.js +0 -191
- package/src/engine/network/transport/fragments/fragment_send.js +0 -157
- package/src/engine/network/transport/fragments/packet_size.js +0 -107
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
import { assert } from "../../../core/assert.js";
|
|
2
|
-
import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
|
|
3
|
-
import Signal from "../../../core/events/signal/Signal.js";
|
|
4
|
-
import { MalformedPacketError } from "../core/MalformedPacketError.js";
|
|
5
|
-
import { MAX_CHANNEL_PAYLOAD_BYTES } from "./fragments/packet_size.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Largest reliable-command payload that fits in a single channel packet.
|
|
9
|
-
*
|
|
10
|
-
* A command's wire form is `uint8 packet_type + uintVar logical_seq + payload`.
|
|
11
|
-
* The pipeline sends it through {@link Channel#send} directly — it does NOT
|
|
12
|
-
* fragment — so the whole thing must fit one channel packet
|
|
13
|
-
* ({@link MAX_CHANNEL_PAYLOAD_BYTES}). We reserve 11 bytes for the framing:
|
|
14
|
-
* 1 packet-type byte + up to 10 bytes of `uintVar` for `logical_seq` (its
|
|
15
|
-
* worst case; `logical_seq` grows over the session, so we budget the maximum
|
|
16
|
-
* rather than the current value). Sending a larger payload on a UDP-style
|
|
17
|
-
* transport would emit an over-MTU datagram that the network silently drops;
|
|
18
|
-
* we reject it loudly instead.
|
|
19
|
-
*
|
|
20
|
-
* @type {number}
|
|
21
|
-
*/
|
|
22
|
-
export const MAX_RELIABLE_COMMAND_PAYLOAD_BYTES = MAX_CHANNEL_PAYLOAD_BYTES - 11;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Reliable, at-least-once delivery layer over a {@link Channel}.
|
|
26
|
-
*
|
|
27
|
-
* Used for messages that must arrive: chat, lobby/room state changes,
|
|
28
|
-
* level transitions, kick / disconnect notifications. Distinct from the
|
|
29
|
-
* action-stream traffic the rest of the netcode uses, which is UDP-style
|
|
30
|
-
* best-effort with per-frame back-fill compensating for losses.
|
|
31
|
-
*
|
|
32
|
-
* Wire layout (after the {@link Channel}'s 8-byte header and the
|
|
33
|
-
* `packet_type` byte that {@link NetworkPeer}'s dispatcher consumes):
|
|
34
|
-
*
|
|
35
|
-
* uintVar logical_seq sender-assigned, monotonically increasing
|
|
36
|
-
* bytes command_payload
|
|
37
|
-
*
|
|
38
|
-
* Reliability mechanism: each outgoing command goes out as one
|
|
39
|
-
* Channel.send call. The returned channel-level seq is tracked in
|
|
40
|
-
* `__unacked`. When the Channel later fires:
|
|
41
|
-
* - `onPacketAcked(seq)`: drop the entry — the command was delivered
|
|
42
|
-
* (the receiver de-duplicated by logical_seq, so a "duplicate" from
|
|
43
|
-
* a previous retransmit is harmless).
|
|
44
|
-
* - `onPacketLost(seq)`: the Channel's 33-packet ack window aged out
|
|
45
|
-
* this seq without seeing it acked. Re-send the same logical
|
|
46
|
-
* command on a fresh channel seq.
|
|
47
|
-
*
|
|
48
|
-
* Receiver side: a sliding-window de-dupe of recent `logical_seq` values.
|
|
49
|
-
* Anything seen within the last `max_received_history` is silently
|
|
50
|
-
* dropped; first-time arrivals fire `onCommand` with the payload bytes.
|
|
51
|
-
*
|
|
52
|
-
* Ordering: NOT guaranteed. Commands may arrive in any order. If you
|
|
53
|
-
* need in-order delivery (level transitions before subsequent chat),
|
|
54
|
-
* key payloads with a counter and reorder in the application handler.
|
|
55
|
-
*
|
|
56
|
-
* Lifecycle constraints:
|
|
57
|
-
* - `max_unacked` caps outstanding commands. Send throws when full
|
|
58
|
-
* (signals real backpressure — receiver gone or path is broken).
|
|
59
|
-
* - `max_received_history` caps the de-dupe set. A retransmit older
|
|
60
|
-
* than this window will be re-delivered as a duplicate to the
|
|
61
|
-
* application; not a problem in practice because the sender's
|
|
62
|
-
* `max_unacked` keeps the inflight set bounded too.
|
|
63
|
-
*
|
|
64
|
-
* Caveats:
|
|
65
|
-
* - Loss detection relies on the Channel's seq window, which advances
|
|
66
|
-
* when subsequent packets are sent. Under sustained no-traffic
|
|
67
|
-
* conditions a single lost reliable command may stay unacked
|
|
68
|
-
* indefinitely — by convention the action-stream's per-tick traffic
|
|
69
|
-
* keeps the window moving even when the application is otherwise
|
|
70
|
-
* idle. Real production code may want a timer-based retransmit
|
|
71
|
-
* fallback layered on top of this.
|
|
72
|
-
*
|
|
73
|
-
* @author Alex Goldring
|
|
74
|
-
* @copyright Company Named Limited (c) 2026
|
|
75
|
-
*/
|
|
76
|
-
export class ReliableCommandPipeline {
|
|
77
|
-
/**
|
|
78
|
-
* Monotonic counter for outgoing logical seqs. Receiver dedups on
|
|
79
|
-
* this value; sender retransmits use the SAME logical_seq with a
|
|
80
|
-
* new channel_seq.
|
|
81
|
-
* @type {number}
|
|
82
|
-
* @private
|
|
83
|
-
*/
|
|
84
|
-
#next_send_seq = 0;
|
|
85
|
-
#unacked;
|
|
86
|
-
#received_seqs;
|
|
87
|
-
/**
|
|
88
|
-
* FIFO order of received logical_seqs, for pruning when
|
|
89
|
-
* `__received_seqs` exceeds `max_received_history`.
|
|
90
|
-
* @type {number[]}
|
|
91
|
-
* @private
|
|
92
|
-
*/
|
|
93
|
-
#received_order = [];
|
|
94
|
-
#send_scratch;
|
|
95
|
-
#bound_acked;
|
|
96
|
-
#bound_lost;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* @param {{
|
|
100
|
-
* channel: import("./Channel.js").Channel,
|
|
101
|
-
* packet_type: number,
|
|
102
|
-
* max_unacked?: number,
|
|
103
|
-
* max_received_history?: number,
|
|
104
|
-
* max_retries?: number,
|
|
105
|
-
* }} options
|
|
106
|
-
*/
|
|
107
|
-
constructor({
|
|
108
|
-
channel,
|
|
109
|
-
packet_type,
|
|
110
|
-
max_unacked = 64,
|
|
111
|
-
max_received_history = 64,
|
|
112
|
-
max_retries = 16,
|
|
113
|
-
}) {
|
|
114
|
-
assert.ok(channel && typeof channel.send === 'function', 'channel must be a Channel');
|
|
115
|
-
assert.isNonNegativeInteger(packet_type, 'packet_type');
|
|
116
|
-
assert.ok(packet_type <= 0xFF, 'packet_type must fit in a uint8');
|
|
117
|
-
assert.isPositiveInteger(max_unacked, 'max_unacked');
|
|
118
|
-
assert.isPositiveInteger(max_received_history, 'max_received_history');
|
|
119
|
-
assert.isPositiveInteger(max_retries, 'max_retries');
|
|
120
|
-
|
|
121
|
-
/** @type {import("./Channel.js").Channel} */
|
|
122
|
-
this.channel = channel;
|
|
123
|
-
/** @type {number} @readonly */
|
|
124
|
-
this.packet_type = packet_type;
|
|
125
|
-
/** @type {number} @readonly */
|
|
126
|
-
this.max_unacked = max_unacked;
|
|
127
|
-
/** @type {number} @readonly */
|
|
128
|
-
this.max_received_history = max_received_history;
|
|
129
|
-
/**
|
|
130
|
-
* Maximum retransmissions per logical command before it is dropped
|
|
131
|
-
* and {@link onCommandAbandoned} fires. Bounded so a permanently-
|
|
132
|
-
* black-holed peer can't pin retransmit traffic on the channel
|
|
133
|
-
* forever. The default (16) tolerates roughly half a second of
|
|
134
|
-
* sustained loss at typical channel cadences before giving up.
|
|
135
|
-
* @type {number} @readonly
|
|
136
|
-
*/
|
|
137
|
-
this.max_retries = max_retries;
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Outstanding commands keyed by their CURRENT channel_seq. On
|
|
141
|
-
* retransmit, the entry is moved to the new channel_seq.
|
|
142
|
-
* `retries` counts how many times this logical command has been
|
|
143
|
-
* resent; the entry is dropped when it exceeds `max_retries`.
|
|
144
|
-
* @type {Map<number, {logical_seq: number, payload: Uint8Array, retries: number}>}
|
|
145
|
-
* @private
|
|
146
|
-
*/
|
|
147
|
-
this.#unacked = new Map();
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Logical seqs received recently. Sliding window for dedup.
|
|
151
|
-
* @type {Set<number>}
|
|
152
|
-
* @private
|
|
153
|
-
*/
|
|
154
|
-
this.#received_seqs = new Set();
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Scratch buffer for assembling outgoing packets.
|
|
158
|
-
* @type {BinaryBuffer}
|
|
159
|
-
* @private
|
|
160
|
-
*/
|
|
161
|
-
this.#send_scratch = new BinaryBuffer();
|
|
162
|
-
this.#send_scratch.setCapacity(1024);
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Fired when a brand-new logical command arrives (not a duplicate).
|
|
166
|
-
* Args: `(buf, payload_offset, payload_length)`. The buffer is
|
|
167
|
-
* shared scratch; consumers must copy bytes if they need them to
|
|
168
|
-
* outlive the handler.
|
|
169
|
-
* @type {Signal}
|
|
170
|
-
*/
|
|
171
|
-
this.onCommand = new Signal();
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Fired when a sent command has been retransmitted `max_retries`
|
|
175
|
-
* times without an ack and is being given up on. Args:
|
|
176
|
-
* `(logical_seq)`. Subscribers typically surface this as a "command
|
|
177
|
-
* failed to deliver" event to the application, or treat it as a
|
|
178
|
-
* hard signal to tear down the session.
|
|
179
|
-
* @type {Signal}
|
|
180
|
-
*/
|
|
181
|
-
this.onCommandAbandoned = new Signal();
|
|
182
|
-
|
|
183
|
-
// Channel reliability notifications.
|
|
184
|
-
this.#bound_acked = (seq) => this.#on_packet_acked(seq);
|
|
185
|
-
this.#bound_lost = (seq) => this.#on_packet_lost(seq);
|
|
186
|
-
channel.onPacketAcked.add(this.#bound_acked);
|
|
187
|
-
channel.onPacketLost.add(this.#bound_lost);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Send a logical command. Returns the assigned `logical_seq` (useful
|
|
192
|
-
* for application-level diagnostics; the receiver dedups internally
|
|
193
|
-
* regardless).
|
|
194
|
-
*
|
|
195
|
-
* @param {Uint8Array} payload
|
|
196
|
-
* @param {number} length
|
|
197
|
-
* @returns {number}
|
|
198
|
-
*/
|
|
199
|
-
send(payload, length) {
|
|
200
|
-
assert.isNonNegativeInteger(length, 'length');
|
|
201
|
-
if (length > MAX_RELIABLE_COMMAND_PAYLOAD_BYTES) {
|
|
202
|
-
// The pipeline does not fragment; an over-MTU command would be sent
|
|
203
|
-
// as one oversized datagram that UDP-style transports silently drop.
|
|
204
|
-
// Fail loudly so the caller chunks at the application layer (or uses
|
|
205
|
-
// a stream transport) rather than losing the message on the wire.
|
|
206
|
-
throw new Error(
|
|
207
|
-
`ReliableCommandPipeline.send: payload of ${length} bytes exceeds the ` +
|
|
208
|
-
`${MAX_RELIABLE_COMMAND_PAYLOAD_BYTES}-byte single-packet limit; this layer does not ` +
|
|
209
|
-
`fragment — chunk the message at the application layer or send it over a stream transport`
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
if (this.#unacked.size >= this.max_unacked) {
|
|
213
|
-
throw new Error(
|
|
214
|
-
`ReliableCommandPipeline.send: ${this.#unacked.size} commands unacked ` +
|
|
215
|
-
`(max ${this.max_unacked}); receiver likely disconnected or path saturated`
|
|
216
|
-
);
|
|
217
|
-
}
|
|
218
|
-
const logical_seq = this.#next_send_seq;
|
|
219
|
-
this.#next_send_seq++;
|
|
220
|
-
|
|
221
|
-
// Defensive copy: the caller's buffer may be reused after this call.
|
|
222
|
-
const payload_copy = new Uint8Array(length);
|
|
223
|
-
payload_copy.set(payload.subarray(0, length));
|
|
224
|
-
|
|
225
|
-
const channel_seq = this.#transmit(logical_seq, payload_copy);
|
|
226
|
-
this.#unacked.set(channel_seq, { logical_seq, payload: payload_copy, retries: 0 });
|
|
227
|
-
return logical_seq;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Called by the orchestrator (typically {@link NetworkPeer}'s
|
|
232
|
-
* dispatcher) when a packet of this pipeline's type arrives. `buf`
|
|
233
|
-
* is positioned just past the packet-type byte.
|
|
234
|
-
*
|
|
235
|
-
* @param {BinaryBuffer} buf
|
|
236
|
-
* @param {number} total_length full transport-level payload length,
|
|
237
|
-
* including the consumed packet-type byte
|
|
238
|
-
*/
|
|
239
|
-
handle_inbound(buf, total_length) {
|
|
240
|
-
// Bounds-checked before the read, like every other parse path in the dispatcher.
|
|
241
|
-
//
|
|
242
|
-
// `#recv_buffer` is a reused scratch: `writeBytes(bytes, 0, length)` overwrites only the
|
|
243
|
-
// first `length` bytes, so everything past them is the previous, longer packet's content.
|
|
244
|
-
// A truncated RELIABLE_COMMAND - just the type byte, from a peer or a truncating middlebox
|
|
245
|
-
// - let `readUintVar` walk into those leftovers and produce a bogus `logical_seq`, which
|
|
246
|
-
// was then recorded in the received set, so a genuine later command carrying that seq was
|
|
247
|
-
// silently dropped as a duplicate.
|
|
248
|
-
if (buf.position >= total_length) {
|
|
249
|
-
throw new MalformedPacketError(`ReliableCommandPipeline: RELIABLE_COMMAND truncated before its sequence number`);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
const logical_seq = buf.readUintVar();
|
|
253
|
-
|
|
254
|
-
// ...and after it: `readUintVar` may have consumed bytes past the packet even when one was
|
|
255
|
-
// available to start with. Subtracting gave a negative payload length and an offset into an
|
|
256
|
-
// unrelated packet's bytes, handed straight to the application.
|
|
257
|
-
if (buf.position > total_length) {
|
|
258
|
-
throw new MalformedPacketError(`ReliableCommandPipeline: RELIABLE_COMMAND sequence number runs past the packet`);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (this.#received_seqs.has(logical_seq)) {
|
|
262
|
-
// Duplicate from a retransmit; ignore.
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
this.#received_seqs.add(logical_seq);
|
|
266
|
-
this.#received_order.push(logical_seq);
|
|
267
|
-
// Prune. Oldest-first.
|
|
268
|
-
while (this.#received_order.length > this.max_received_history) {
|
|
269
|
-
const oldest = this.#received_order.shift();
|
|
270
|
-
this.#received_seqs.delete(oldest);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const payload_offset = buf.position;
|
|
274
|
-
const payload_length = total_length - payload_offset;
|
|
275
|
-
this.onCommand.send3(buf, payload_offset, payload_length);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Number of commands sent that haven't yet been acked. Useful for
|
|
280
|
-
* backpressure / progress UI.
|
|
281
|
-
* @returns {number}
|
|
282
|
-
*/
|
|
283
|
-
unacked_count() {
|
|
284
|
-
return this.#unacked.size;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Drop subscriptions and clear state. Required before discarding a
|
|
289
|
-
* pipeline whose channel will outlive it.
|
|
290
|
-
*/
|
|
291
|
-
dispose() {
|
|
292
|
-
this.channel.onPacketAcked.remove(this.#bound_acked);
|
|
293
|
-
this.channel.onPacketLost.remove(this.#bound_lost);
|
|
294
|
-
this.#unacked.clear();
|
|
295
|
-
this.#received_seqs.clear();
|
|
296
|
-
this.#received_order.length = 0;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
/** @private */
|
|
300
|
-
#transmit(logical_seq, payload) {
|
|
301
|
-
const buf = this.#send_scratch;
|
|
302
|
-
// Worst-case capacity: type byte + 10 bytes for varint + payload.
|
|
303
|
-
const needed = 1 + 10 + payload.length;
|
|
304
|
-
if (buf.raw_bytes.length < needed) buf.setCapacity(needed);
|
|
305
|
-
buf.position = 0;
|
|
306
|
-
buf.writeUint8(this.packet_type);
|
|
307
|
-
buf.writeUintVar(logical_seq);
|
|
308
|
-
buf.writeBytes(payload, 0, payload.length);
|
|
309
|
-
return this.channel.send(buf.raw_bytes, buf.position);
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/** @private */
|
|
313
|
-
#on_packet_acked(channel_seq) {
|
|
314
|
-
this.#unacked.delete(channel_seq);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
/** @private */
|
|
318
|
-
#on_packet_lost(channel_seq) {
|
|
319
|
-
const entry = this.#unacked.get(channel_seq);
|
|
320
|
-
if (entry === undefined) return;
|
|
321
|
-
this.#unacked.delete(channel_seq);
|
|
322
|
-
|
|
323
|
-
if (entry.retries >= this.max_retries) {
|
|
324
|
-
// Permanently black-holed peer or fundamentally broken path —
|
|
325
|
-
// surface the failure to the application and stop retrying.
|
|
326
|
-
this.onCommandAbandoned.send1(entry.logical_seq);
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// Retransmit on a fresh channel_seq. Same logical_seq, same payload.
|
|
331
|
-
entry.retries++;
|
|
332
|
-
const new_channel_seq = this.#transmit(entry.logical_seq, entry.payload);
|
|
333
|
-
this.#unacked.set(new_channel_seq, entry);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
import { assert } from "../../../../core/assert.js";
|
|
2
|
-
import {
|
|
3
|
-
FRAGMENT_NACK_INITIAL_DELAY_MS,
|
|
4
|
-
FRAGMENT_NACK_MAX_ROUNDS,
|
|
5
|
-
FRAGMENT_NACK_RESEND_INTERVAL_MS,
|
|
6
|
-
MAX_CHUNKS_PER_MESSAGE,
|
|
7
|
-
} from "./packet_size.js";
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Per-peer reassembly buffer for fragmented packets.
|
|
11
|
-
*
|
|
12
|
-
* When a sender's logical payload exceeds the MTU it gets split into
|
|
13
|
-
* multiple {@link NetworkPacketType.FRAGMENT} packets, each carrying a
|
|
14
|
-
* `(message_id, chunk_index, total_chunks, chunk_bytes)` tuple. The
|
|
15
|
-
* receiver feeds those tuples to {@link receive}; once all chunks for a
|
|
16
|
-
* `message_id` have arrived, `receive` returns the reassembled bytes
|
|
17
|
-
* for the upper layer to dispatch.
|
|
18
|
-
*
|
|
19
|
-
* Loss recovery via NACK: a partially-received message that doesn't
|
|
20
|
-
* complete within `nack_initial_delay_ms` triggers a NACK back to the
|
|
21
|
-
* sender listing the missing chunk indices. NACK rounds repeat at
|
|
22
|
-
* `nack_resend_interval_ms` up to `nack_max_rounds`; after that the
|
|
23
|
-
* receiver gives up on the message and the sender's retention ages
|
|
24
|
-
* out independently. Wire the per-tick driver via {@link service}.
|
|
25
|
-
*
|
|
26
|
-
* Loss-of-last-resort model: if even the NACK retries fail (link is
|
|
27
|
-
* dead, sender retention was evicted, etc.) the message never
|
|
28
|
-
* completes and FIFO-evicts when the pending cap is hit. Upper layers
|
|
29
|
-
* are still expected to tolerate occasional missing messages — NACK
|
|
30
|
-
* recovers most loss, not all.
|
|
31
|
-
*
|
|
32
|
-
* @author Alex Goldring
|
|
33
|
-
* @copyright Company Named Limited (c) 2026
|
|
34
|
-
*/
|
|
35
|
-
export class FragmentAssembler {
|
|
36
|
-
#pending;
|
|
37
|
-
/**
|
|
38
|
-
* FIFO of message_ids in insertion order, for eviction when the
|
|
39
|
-
* pending map exceeds `max_pending_messages`.
|
|
40
|
-
* @type {number[]}
|
|
41
|
-
* @private
|
|
42
|
-
*/
|
|
43
|
-
#order = [];
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Message ids delivered recently, so a late or retransmitted chunk for one is dropped rather
|
|
47
|
-
* than opening a fresh reassembly slot. See the note in {@link receive}.
|
|
48
|
-
*
|
|
49
|
-
* Bounded by {@link #completed_order}, which spans the same window `#order` does.
|
|
50
|
-
*
|
|
51
|
-
* @type {Set<number>}
|
|
52
|
-
*/
|
|
53
|
-
#completed = new Set();
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Insertion order of {@link #completed}, for FIFO eviction.
|
|
57
|
-
* @type {number[]}
|
|
58
|
-
*/
|
|
59
|
-
#completed_order = [];
|
|
60
|
-
#nack_scratch;
|
|
61
|
-
/**
|
|
62
|
-
* Scratch for message_ids to drop after a {@link service} pass.
|
|
63
|
-
* Collected during iteration so the underlying map isn't mutated
|
|
64
|
-
* mid-iteration.
|
|
65
|
-
* @type {number[]}
|
|
66
|
-
* @private
|
|
67
|
-
*/
|
|
68
|
-
#drop_scratch = [];
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* @param {{
|
|
72
|
-
* max_pending_messages?: number,
|
|
73
|
-
* max_message_size?: number,
|
|
74
|
-
* nack_initial_delay_ms?: number,
|
|
75
|
-
* nack_resend_interval_ms?: number,
|
|
76
|
-
* nack_max_rounds?: number,
|
|
77
|
-
* }} [options]
|
|
78
|
-
* `max_pending_messages` caps the number of in-flight reassembly
|
|
79
|
-
* slots; when full, the oldest pending message is evicted to make
|
|
80
|
-
* room. `max_message_size` rejects reassembled messages larger than
|
|
81
|
-
* this many bytes (defensive against a malicious peer or a wedged
|
|
82
|
-
* sender). The `nack_*` knobs control the retransmit-request timer
|
|
83
|
-
* driven by {@link service}.
|
|
84
|
-
*/
|
|
85
|
-
constructor({
|
|
86
|
-
max_pending_messages = 8,
|
|
87
|
-
max_message_size = 65536,
|
|
88
|
-
nack_initial_delay_ms = FRAGMENT_NACK_INITIAL_DELAY_MS,
|
|
89
|
-
nack_resend_interval_ms = FRAGMENT_NACK_RESEND_INTERVAL_MS,
|
|
90
|
-
nack_max_rounds = FRAGMENT_NACK_MAX_ROUNDS,
|
|
91
|
-
} = {}) {
|
|
92
|
-
assert.isPositiveInteger(max_pending_messages, 'max_pending_messages');
|
|
93
|
-
assert.isPositiveInteger(max_message_size, 'max_message_size');
|
|
94
|
-
assert.isPositiveInteger(nack_initial_delay_ms, 'nack_initial_delay_ms');
|
|
95
|
-
assert.isPositiveInteger(nack_resend_interval_ms, 'nack_resend_interval_ms');
|
|
96
|
-
assert.isPositiveInteger(nack_max_rounds, 'nack_max_rounds');
|
|
97
|
-
|
|
98
|
-
/** @type {number} @readonly */
|
|
99
|
-
this.max_pending_messages = max_pending_messages;
|
|
100
|
-
/** @type {number} @readonly */
|
|
101
|
-
this.max_message_size = max_message_size;
|
|
102
|
-
/** @type {number} @readonly */
|
|
103
|
-
this.nack_initial_delay_ms = nack_initial_delay_ms;
|
|
104
|
-
/** @type {number} @readonly */
|
|
105
|
-
this.nack_resend_interval_ms = nack_resend_interval_ms;
|
|
106
|
-
/** @type {number} @readonly */
|
|
107
|
-
this.nack_max_rounds = nack_max_rounds;
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Pending reassembly state per `message_id`. Each entry:
|
|
111
|
-
* total_chunks: uint8 — number of chunks expected
|
|
112
|
-
* chunks: (Uint8Array | undefined)[] — chunk_index → bytes
|
|
113
|
-
* received_count: number of distinct chunks present
|
|
114
|
-
* total_bytes: sum of chunk lengths received so far
|
|
115
|
-
* first_seen_at: ms timestamp of the first {@link service} tick
|
|
116
|
-
* that observed this entry; -1 until stamped
|
|
117
|
-
* last_nack_at: ms timestamp of the most recent NACK emitted
|
|
118
|
-
* for this entry; -1 if never
|
|
119
|
-
* nack_rounds: number of NACKs emitted for this entry
|
|
120
|
-
* @type {Map<number, {total_chunks: number, chunks: Uint8Array[], received_count: number, total_bytes: number, first_seen_at: number, last_nack_at: number, nack_rounds: number}>}
|
|
121
|
-
* @private
|
|
122
|
-
*/
|
|
123
|
-
this.#pending = new Map();
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Reusable scratch for the missing-index list passed to the NACK
|
|
127
|
-
* callback. Sized to the wire maximum so the callback can always
|
|
128
|
-
* iterate `count` entries safely.
|
|
129
|
-
* @type {Uint8Array}
|
|
130
|
-
* @private
|
|
131
|
-
*/
|
|
132
|
-
this.#nack_scratch = new Uint8Array(MAX_CHUNKS_PER_MESSAGE);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Process an incoming fragment. Returns the reassembled bytes when
|
|
137
|
-
* the message is complete, or null otherwise.
|
|
138
|
-
*
|
|
139
|
-
* @param {number} message_id sender-assigned message id
|
|
140
|
-
* @param {number} chunk_index 0-based
|
|
141
|
-
* @param {number} total_chunks expected number of chunks (>= 1)
|
|
142
|
-
* @param {Uint8Array} chunk_bytes source byte array
|
|
143
|
-
* @param {number} chunk_offset start offset within `chunk_bytes`
|
|
144
|
-
* @param {number} chunk_length number of bytes from `chunk_offset` to consume
|
|
145
|
-
* @returns {Uint8Array|null}
|
|
146
|
-
*/
|
|
147
|
-
receive(message_id, chunk_index, total_chunks, chunk_bytes, chunk_offset, chunk_length) {
|
|
148
|
-
assert.isNonNegativeInteger(message_id, 'message_id');
|
|
149
|
-
assert.isNonNegativeInteger(chunk_index, 'chunk_index');
|
|
150
|
-
assert.isPositiveInteger(total_chunks, 'total_chunks');
|
|
151
|
-
assert.isNonNegativeInteger(chunk_offset, 'chunk_offset');
|
|
152
|
-
assert.isNonNegativeInteger(chunk_length, 'chunk_length');
|
|
153
|
-
|
|
154
|
-
if (chunk_index >= total_chunks) {
|
|
155
|
-
// Malformed; discard silently.
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (this.#completed.has(message_id)) {
|
|
160
|
-
// Already delivered. Without this the chunk misses the duplicate guard below - which
|
|
161
|
-
// lives INSIDE an entry, and completion erases the entry - and opens a fresh, and
|
|
162
|
-
// therefore incomplete, reassembly slot. `service()` then NACKs the sender for chunks
|
|
163
|
-
// of a message it has already delivered, the sender's retention honours the NACK, and
|
|
164
|
-
// the message completes and is dispatched a second time: STATE_BURST straight to the
|
|
165
|
-
// application handler, INITIAL_SYNC re-applying a whole snapshot. It is the ordinary
|
|
166
|
-
// NACK/late-chunk race, not an adversarial input - `packet_size.js` sizes the delay
|
|
167
|
-
// expecting the receiver to "occasionally NACK chunks still in transit".
|
|
168
|
-
return null;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let entry = this.#pending.get(message_id);
|
|
172
|
-
if (entry === undefined) {
|
|
173
|
-
if (this.#pending.size >= this.max_pending_messages) {
|
|
174
|
-
// Evict the oldest pending message (FIFO).
|
|
175
|
-
const oldest = this.#order.shift();
|
|
176
|
-
if (oldest !== undefined) this.#pending.delete(oldest);
|
|
177
|
-
}
|
|
178
|
-
entry = {
|
|
179
|
-
total_chunks,
|
|
180
|
-
chunks: new Array(total_chunks),
|
|
181
|
-
received_count: 0,
|
|
182
|
-
total_bytes: 0,
|
|
183
|
-
first_seen_at: -1,
|
|
184
|
-
last_nack_at: -1,
|
|
185
|
-
nack_rounds: 0,
|
|
186
|
-
};
|
|
187
|
-
this.#pending.set(message_id, entry);
|
|
188
|
-
this.#order.push(message_id);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (entry.total_chunks !== total_chunks) {
|
|
192
|
-
// Conflicting metadata across fragments for the same message_id.
|
|
193
|
-
// Drop the whole assembly — something is very wrong upstream.
|
|
194
|
-
this.#delete(message_id);
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
if (entry.chunks[chunk_index] !== undefined) {
|
|
199
|
-
// Duplicate fragment; ignore (network may have retransmitted).
|
|
200
|
-
return null;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Defensive: refuse to allocate more memory than max_message_size for
|
|
204
|
-
// this assembly. Check incrementally so a malicious sender can't
|
|
205
|
-
// claim 255 chunks of 65 KB each.
|
|
206
|
-
if (entry.total_bytes + chunk_length > this.max_message_size) {
|
|
207
|
-
this.#delete(message_id);
|
|
208
|
-
return null;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const copy = new Uint8Array(chunk_length);
|
|
212
|
-
copy.set(chunk_bytes.subarray(chunk_offset, chunk_offset + chunk_length));
|
|
213
|
-
entry.chunks[chunk_index] = copy;
|
|
214
|
-
entry.received_count++;
|
|
215
|
-
entry.total_bytes += chunk_length;
|
|
216
|
-
|
|
217
|
-
if (entry.received_count < entry.total_chunks) return null;
|
|
218
|
-
|
|
219
|
-
// Complete — assemble.
|
|
220
|
-
const reassembled = new Uint8Array(entry.total_bytes);
|
|
221
|
-
let offset = 0;
|
|
222
|
-
for (let i = 0; i < entry.total_chunks; i++) {
|
|
223
|
-
const c = entry.chunks[i];
|
|
224
|
-
reassembled.set(c, offset);
|
|
225
|
-
offset += c.length;
|
|
226
|
-
}
|
|
227
|
-
this.#delete(message_id);
|
|
228
|
-
|
|
229
|
-
this.#remember_completed(message_id);
|
|
230
|
-
|
|
231
|
-
return reassembled;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Record a delivered message id, evicting the oldest when the window is full.
|
|
236
|
-
*
|
|
237
|
-
* The window is the same size as the pending one: a chunk arriving later than that many
|
|
238
|
-
* distinct messages after its own completed is beyond anything the retransmit machinery
|
|
239
|
-
* would still be holding.
|
|
240
|
-
*
|
|
241
|
-
* @param {number} message_id
|
|
242
|
-
*/
|
|
243
|
-
#remember_completed(message_id) {
|
|
244
|
-
if (this.#completed.has(message_id)) {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
this.#completed.add(message_id);
|
|
249
|
-
this.#completed_order.push(message_id);
|
|
250
|
-
|
|
251
|
-
while (this.#completed_order.length > this.max_pending_messages) {
|
|
252
|
-
const oldest = this.#completed_order.shift();
|
|
253
|
-
|
|
254
|
-
this.#completed.delete(oldest);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Per-tick maintenance: drive NACK emission for pending messages
|
|
260
|
-
* whose initial delay has elapsed and whose resend interval has
|
|
261
|
-
* lapsed, and drop messages whose NACK budget is exhausted.
|
|
262
|
-
*
|
|
263
|
-
* On the first service tick that observes a pending entry, its
|
|
264
|
-
* arrival is timestamped (so the initial delay is measured from
|
|
265
|
-
* `now_ms` of that tick rather than from the actual wall-clock
|
|
266
|
-
* arrival of the first chunk — at typical tick rates the difference
|
|
267
|
-
* is one tick, negligible vs. the 100 ms initial delay).
|
|
268
|
-
*
|
|
269
|
-
* `on_nack` is invoked once per eligible message with
|
|
270
|
-
* `(message_id, indices, count)`: the missing chunks in index order, but
|
|
271
|
-
* never more of them than the message has delivered. The `indices` argument is a
|
|
272
|
-
* reused internal Uint8Array; only `indices[0..count)` is valid
|
|
273
|
-
* and only for the duration of the call.
|
|
274
|
-
*
|
|
275
|
-
* @param {number} now_ms
|
|
276
|
-
* @param {function(number, Uint8Array, number): void} on_nack
|
|
277
|
-
*/
|
|
278
|
-
service(now_ms, on_nack) {
|
|
279
|
-
assert.isNumber(now_ms, 'now_ms');
|
|
280
|
-
assert.isFunction(on_nack, 'on_nack');
|
|
281
|
-
if (this.#pending.size === 0) return;
|
|
282
|
-
|
|
283
|
-
this.#drop_scratch.length = 0;
|
|
284
|
-
|
|
285
|
-
for (const [message_id, entry] of this.#pending) {
|
|
286
|
-
if (entry.first_seen_at < 0) {
|
|
287
|
-
entry.first_seen_at = now_ms;
|
|
288
|
-
continue;
|
|
289
|
-
}
|
|
290
|
-
if (now_ms - entry.first_seen_at < this.nack_initial_delay_ms) continue;
|
|
291
|
-
if (entry.last_nack_at >= 0
|
|
292
|
-
&& now_ms - entry.last_nack_at < this.nack_resend_interval_ms) continue;
|
|
293
|
-
|
|
294
|
-
// Build the missing-chunk list, no longer than the chunks the
|
|
295
|
-
// message has delivered. `total_chunks` is the sender's claim, so
|
|
296
|
-
// it cannot bound the list by itself: one empty fragment claiming
|
|
297
|
-
// 255 chunks drew a NACK of the other 254 each round, some fifty
|
|
298
|
-
// times its own size. A message that has lost more than it has
|
|
299
|
-
// delivered is asked for the rest over the following rounds.
|
|
300
|
-
let missing_count = 0;
|
|
301
|
-
for (let i = 0; i < entry.total_chunks && missing_count < entry.received_count; i++) {
|
|
302
|
-
if (entry.chunks[i] === undefined) {
|
|
303
|
-
this.#nack_scratch[missing_count++] = i;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
// Defensive: a fully-received entry should have been deleted
|
|
307
|
-
// in `receive`, but a torn-down concurrent state could leave
|
|
308
|
-
// a zero-missing slot here. Skip rather than emit an empty NACK.
|
|
309
|
-
if (missing_count === 0) continue;
|
|
310
|
-
|
|
311
|
-
on_nack(message_id, this.#nack_scratch, missing_count);
|
|
312
|
-
entry.last_nack_at = now_ms;
|
|
313
|
-
entry.nack_rounds++;
|
|
314
|
-
|
|
315
|
-
if (entry.nack_rounds >= this.nack_max_rounds) {
|
|
316
|
-
this.#drop_scratch.push(message_id);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
for (let i = 0; i < this.#drop_scratch.length; i++) {
|
|
321
|
-
this.#delete(this.#drop_scratch[i]);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/**
|
|
326
|
-
* Number of in-flight reassembly slots currently held.
|
|
327
|
-
* @returns {number}
|
|
328
|
-
*/
|
|
329
|
-
pending_count() {
|
|
330
|
-
return this.#pending.size;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Drop all in-flight reassembly state. Useful on peer disconnect.
|
|
335
|
-
*/
|
|
336
|
-
clear() {
|
|
337
|
-
this.#pending.clear();
|
|
338
|
-
this.#order.length = 0;
|
|
339
|
-
|
|
340
|
-
this.#completed.clear();
|
|
341
|
-
this.#completed_order.length = 0;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/** @private */
|
|
345
|
-
#delete(message_id) {
|
|
346
|
-
this.#pending.delete(message_id);
|
|
347
|
-
const idx = this.#order.indexOf(message_id);
|
|
348
|
-
if (idx >= 0) this.#order.splice(idx, 1);
|
|
349
|
-
}
|
|
350
|
-
}
|