@woosh/meep-engine 3.46.0 → 3.49.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.
Files changed (827) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
  3. package/editor/actions/concrete/TransformModifyAction.js +10 -2
  4. package/editor/ecs/component/FieldDescriptor.js +15 -0
  5. package/editor/ecs/component/createObjectEditor.js +41 -12
  6. package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts.map +1 -1
  7. package/editor/ecs/component/editors/ecs/Transform64Editor.js +77 -70
  8. package/editor/particles/effect/ParticleReferenceSimulation.d.ts +1 -2
  9. package/editor/particles/effect/ParticleReferenceSimulation.d.ts.map +1 -1
  10. package/editor/particles/effect/ParticleReferenceSimulation.js +2 -4
  11. package/editor/tools/TransformToolV2.d.ts.map +1 -1
  12. package/editor/tools/TransformToolV2.js +4 -0
  13. package/editor/tools/v2/TransformControls.d.ts.map +1 -1
  14. package/editor/tools/v2/TransformControls.js +7 -0
  15. package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
  16. package/editor/view/ecs/EntityEditor.js +4 -2
  17. package/editor/view/particles/effect/ParticleGraphEditorView.js +1 -1
  18. package/package.json +1 -1
  19. package/src/core/collection/array/array_insert_at.d.ts +16 -0
  20. package/src/core/collection/array/array_insert_at.d.ts.map +1 -0
  21. package/src/core/collection/array/array_insert_at.js +32 -0
  22. package/src/core/collection/array/array_remove_at.d.ts +15 -0
  23. package/src/core/collection/array/array_remove_at.d.ts.map +1 -0
  24. package/src/core/collection/array/array_remove_at.js +30 -0
  25. package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts +37 -0
  26. package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts.map +1 -0
  27. package/src/core/geom/3d/quaternion/quat3_look_rotation.js +172 -0
  28. package/src/core/geom/Quaternion.d.ts +1 -0
  29. package/src/core/geom/Quaternion.d.ts.map +1 -1
  30. package/src/core/geom/Quaternion.js +12 -48
  31. package/src/engine/Engine.d.ts.map +1 -1
  32. package/src/engine/Engine.js +9 -4
  33. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +27 -11
  34. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  35. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +21 -0
  36. package/src/engine/ecs/transform/t64_look_rotation.d.ts +4 -4
  37. package/src/engine/ecs/transform/t64_look_rotation.d.ts.map +1 -1
  38. package/src/engine/ecs/transform/t64_look_rotation.js +7 -18
  39. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  40. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +30 -1
  41. package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts +2 -2
  42. package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts.map +1 -1
  43. package/src/engine/graphics/ecs/particles/ParticleEffect.js +7 -7
  44. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts +8 -0
  45. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.d.ts.map +1 -1
  46. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationAdapter.js +11 -3
  47. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_1_2.d.ts +26 -0
  48. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_1_2.d.ts.map +1 -0
  49. package/src/engine/graphics/ecs/particles/ParticleEffectSerializationUpgrader_1_2.js +91 -0
  50. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +6 -0
  51. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  52. package/src/engine/graphics3/GPUParticleEmitterSystem.js +72 -29
  53. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  54. package/src/engine/graphics3/GraphicsEngine.js +8 -1
  55. package/src/engine/graphics3/ParticipatingMedia.d.ts +84 -1
  56. package/src/engine/graphics3/ParticipatingMedia.d.ts.map +1 -1
  57. package/src/engine/graphics3/ParticipatingMedia.js +139 -5
  58. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts +6 -2
  59. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.d.ts.map +1 -1
  60. package/src/engine/graphics3/ParticipatingMediaSerializationAdapter.js +29 -4
  61. package/src/engine/graphics3/ParticipatingMediaSerializationUpgrader_0_1.d.ts +10 -0
  62. package/src/engine/graphics3/ParticipatingMediaSerializationUpgrader_0_1.d.ts.map +1 -0
  63. package/src/engine/graphics3/ParticipatingMediaSerializationUpgrader_0_1.js +38 -0
  64. package/src/engine/graphics3/ParticipatingMediaSystem.d.ts.map +1 -1
  65. package/src/engine/graphics3/ParticipatingMediaSystem.js +46 -3
  66. package/src/engine/input/devices/KeyboardDevice.d.ts +1 -0
  67. package/src/engine/input/devices/KeyboardDevice.d.ts.map +1 -1
  68. package/src/engine/input/devices/KeyboardDevice.js +7 -6
  69. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
  70. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +15 -2
  71. package/src/engine/network/CONGESTION_CONTROL.md +9 -9
  72. package/src/engine/network/MESSAGE_TRANSPORT.md +364 -0
  73. package/src/engine/network/NetworkSession.d.ts +47 -16
  74. package/src/engine/network/NetworkSession.d.ts.map +1 -1
  75. package/src/engine/network/NetworkSession.js +556 -292
  76. package/src/engine/network/README.md +104 -28
  77. package/src/engine/network/SMOOTHNESS_DESIGN.md +13 -5
  78. package/src/engine/network/diagnostics/BandwidthMeter.d.ts +1 -1
  79. package/src/engine/network/diagnostics/BandwidthMeter.js +1 -1
  80. package/src/engine/network/orchestrator/HostJoin.d.ts +111 -0
  81. package/src/engine/network/orchestrator/HostJoin.d.ts.map +1 -0
  82. package/src/engine/network/orchestrator/HostJoin.js +271 -0
  83. package/src/engine/network/orchestrator/NetworkPeer.d.ts +151 -112
  84. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  85. package/src/engine/network/orchestrator/NetworkPeer.js +450 -499
  86. package/src/engine/network/orchestrator/PendingSlices.d.ts +30 -0
  87. package/src/engine/network/orchestrator/PendingSlices.d.ts.map +1 -0
  88. package/src/engine/network/orchestrator/PendingSlices.js +83 -0
  89. package/src/engine/network/orchestrator/ReliableFrames.d.ts +34 -0
  90. package/src/engine/network/orchestrator/ReliableFrames.d.ts.map +1 -0
  91. package/src/engine/network/orchestrator/ReliableFrames.js +96 -0
  92. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +77 -10
  93. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
  94. package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +236 -61
  95. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +59 -23
  96. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
  97. package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +213 -198
  98. package/src/engine/network/replication/Replicator.d.ts +15 -4
  99. package/src/engine/network/replication/Replicator.d.ts.map +1 -1
  100. package/src/engine/network/replication/Replicator.js +57 -17
  101. package/src/engine/network/sim/ActionLog.d.ts +32 -5
  102. package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
  103. package/src/engine/network/sim/ActionLog.js +47 -5
  104. package/src/engine/network/sim/SimActionExecutor.d.ts +42 -2
  105. package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
  106. package/src/engine/network/sim/SimActionExecutor.js +100 -4
  107. package/src/engine/network/sim/Snapshotter.d.ts +12 -0
  108. package/src/engine/network/sim/Snapshotter.d.ts.map +1 -1
  109. package/src/engine/network/sim/Snapshotter.js +43 -0
  110. package/src/engine/network/state/Baseline.d.ts +1 -1
  111. package/src/engine/network/state/Baseline.js +1 -1
  112. package/src/engine/network/state/FrameBufferRing.d.ts +25 -6
  113. package/src/engine/network/state/FrameBufferRing.d.ts.map +1 -1
  114. package/src/engine/network/state/FrameBufferRing.js +51 -6
  115. package/src/engine/network/state/InputRing.d.ts +4 -0
  116. package/src/engine/network/state/InputRing.d.ts.map +1 -1
  117. package/src/engine/network/state/InputRing.js +7 -0
  118. package/src/engine/network/state/RecordArrivalRing.d.ts +57 -0
  119. package/src/engine/network/state/RecordArrivalRing.d.ts.map +1 -0
  120. package/src/engine/network/state/RecordArrivalRing.js +144 -0
  121. package/src/engine/network/transport/DeferredTransport.d.ts.map +1 -1
  122. package/src/engine/network/transport/DeferredTransport.js +1 -0
  123. package/src/engine/network/transport/LoopbackTransport.d.ts.map +1 -1
  124. package/src/engine/network/transport/LoopbackTransport.js +4 -2
  125. package/src/engine/network/transport/Transport.d.ts +7 -0
  126. package/src/engine/network/transport/Transport.d.ts.map +1 -1
  127. package/src/engine/network/transport/Transport.js +8 -0
  128. package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts.map +1 -1
  129. package/src/engine/network/transport/adapters/NodeUDPTransport.js +20 -5
  130. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts.map +1 -1
  131. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +8 -0
  132. package/src/engine/network/transport/adapters/WebTransportTransport.d.ts.map +1 -1
  133. package/src/engine/network/transport/adapters/WebTransportTransport.js +6 -0
  134. package/src/engine/network/transport/connection/Connection.d.ts +191 -0
  135. package/src/engine/network/transport/connection/Connection.d.ts.map +1 -0
  136. package/src/engine/network/transport/connection/Connection.js +1262 -0
  137. package/src/engine/network/transport/connection/RangeSet.d.ts +94 -0
  138. package/src/engine/network/transport/connection/RangeSet.d.ts.map +1 -0
  139. package/src/engine/network/transport/connection/RangeSet.js +260 -0
  140. package/src/engine/network/transport/connection/RxStream.d.ts +48 -0
  141. package/src/engine/network/transport/connection/RxStream.d.ts.map +1 -0
  142. package/src/engine/network/transport/connection/RxStream.js +211 -0
  143. package/src/engine/network/transport/connection/TxStream.d.ts +90 -0
  144. package/src/engine/network/transport/connection/TxStream.d.ts.map +1 -0
  145. package/src/engine/network/transport/connection/TxStream.js +243 -0
  146. package/src/engine/network/transport/connection/bytes_copy.d.ts +17 -0
  147. package/src/engine/network/transport/connection/bytes_copy.d.ts.map +1 -0
  148. package/src/engine/network/transport/connection/bytes_copy.js +34 -0
  149. package/src/engine/network/transport/connection/varint.d.ts +46 -0
  150. package/src/engine/network/transport/connection/varint.d.ts.map +1 -0
  151. package/src/engine/network/transport/connection/varint.js +107 -0
  152. package/src/engine/physics/cloth/MEASUREMENTS.md +354 -0
  153. package/src/engine/physics/cloth/PLAN.md +85 -5
  154. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +0 -12
  155. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  156. package/src/engine/physics/cloth/ecs/ClothInstance.js +0 -13
  157. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +2 -1
  158. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  159. package/src/engine/physics/cloth/ecs/ClothSystem.js +7 -1
  160. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  161. package/src/engine/physics/cloth/ecs/ClothWorld.js +4 -15
  162. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +2 -1
  163. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -1
  164. package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +6 -1
  165. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
  166. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +20 -10
  167. package/src/engine/physics/cloth/playground/collide.html +5 -3
  168. package/src/engine/physics/cloth/playground/collide_main.js +14 -13
  169. package/src/engine/physics/cloth/solver/ClothState.d.ts +30 -2
  170. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
  171. package/src/engine/physics/cloth/solver/ClothState.js +62 -4
  172. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts +11 -3
  173. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts.map +1 -1
  174. package/src/engine/physics/cloth/solver/cloth_self_find.js +52 -11
  175. package/src/engine/physics/cloth/solver/cloth_self_radius.d.ts +42 -0
  176. package/src/engine/physics/cloth/solver/cloth_self_radius.d.ts.map +1 -0
  177. package/src/engine/physics/cloth/solver/cloth_self_radius.js +64 -0
  178. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +99 -24
  179. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  180. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +102 -25
  181. package/src/engine/physics/cloth/solver/cloth_step.d.ts +5 -1
  182. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
  183. package/src/engine/physics/cloth/solver/cloth_step.js +13 -3
  184. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts +0 -1
  185. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts.map +1 -1
  186. package/src/engine/physics/cloth/solver/cloth_step_instance.js +12 -14
  187. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts +23 -4
  188. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -1
  189. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +36 -8
  190. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts.map +1 -1
  191. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.js +4 -2
  192. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  193. package/src/engine/sound/ecs/audio/AudioEmitter.d.ts +38 -7
  194. package/src/engine/sound/ecs/audio/AudioEmitter.d.ts.map +1 -1
  195. package/src/engine/sound/ecs/audio/AudioEmitter.js +35 -7
  196. package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.d.ts.map +1 -1
  197. package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.js +5 -1
  198. package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts +18 -0
  199. package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts.map +1 -0
  200. package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.js +48 -0
  201. package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +12 -2
  202. package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -1
  203. package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +173 -9
  204. package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts +9 -3
  205. package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts.map +1 -1
  206. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +28 -8
  207. package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts +8 -0
  208. package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts.map +1 -1
  209. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.d.ts.map +1 -1
  210. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.js +5 -1
  211. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts +18 -0
  212. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts.map +1 -0
  213. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.js +51 -0
  214. package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts.map +1 -1
  215. package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.js +15 -1
  216. package/src/engine/sound/sopra/README.md +768 -644
  217. package/src/engine/sound/sopra/SopraEngine.d.ts +104 -8
  218. package/src/engine/sound/sopra/SopraEngine.d.ts.map +1 -1
  219. package/src/engine/sound/sopra/SopraEngine.js +327 -12
  220. package/src/engine/sound/sopra/definition/EventDescription.d.ts +28 -2
  221. package/src/engine/sound/sopra/definition/EventDescription.d.ts.map +1 -1
  222. package/src/engine/sound/sopra/definition/EventDescription.js +55 -5
  223. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts +10 -0
  224. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts.map +1 -1
  225. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +77 -55
  226. package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts +31 -0
  227. package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts.map +1 -0
  228. package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.js +75 -0
  229. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts +38 -9
  230. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts.map +1 -1
  231. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +34 -5
  232. package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts +7 -2
  233. package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts.map +1 -1
  234. package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +137 -122
  235. package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts +3 -1
  236. package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts.map +1 -1
  237. package/src/engine/sound/sopra/definition/effect/EqEffect.js +146 -131
  238. package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts +1 -0
  239. package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts.map +1 -1
  240. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +6 -1
  241. package/src/engine/sound/sopra/runtime/BusGraph.d.ts +44 -2
  242. package/src/engine/sound/sopra/runtime/BusGraph.d.ts.map +1 -1
  243. package/src/engine/sound/sopra/runtime/BusGraph.js +83 -43
  244. package/src/engine/sound/sopra/runtime/EffectChain.d.ts +96 -0
  245. package/src/engine/sound/sopra/runtime/EffectChain.d.ts.map +1 -0
  246. package/src/engine/sound/sopra/runtime/EffectChain.js +286 -0
  247. package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts +123 -0
  248. package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts.map +1 -0
  249. package/src/engine/sound/sopra/runtime/EffectParamOverrides.js +320 -0
  250. package/src/engine/sound/sopra/runtime/EventInstance.d.ts +55 -5
  251. package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
  252. package/src/engine/sound/sopra/runtime/EventInstance.js +201 -26
  253. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts +2 -1
  254. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts.map +1 -1
  255. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +6 -1
  256. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts +15 -0
  257. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts.map +1 -1
  258. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +32 -0
  259. package/src/engine/sound/sopra/util/MockAudioContext.d.ts.map +1 -1
  260. package/src/engine/sound/sopra/util/MockAudioContext.js +3 -0
  261. package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts +13 -3
  262. package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts.map +1 -1
  263. package/src/engine/sound/sopra/util/fadeOutAndStop.js +25 -13
  264. package/src/shade/RENDERER_CONTRACT.md +6 -3
  265. package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts +24 -0
  266. package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts.map +1 -0
  267. package/src/shade/device/SHADE_DEVICE_FLOOR.js +34 -0
  268. package/src/shade/device/ShadeDeviceFailure.d.ts +12 -5
  269. package/src/shade/device/ShadeDeviceFailure.d.ts.map +1 -1
  270. package/src/shade/device/ShadeDeviceFailure.js +17 -8
  271. package/src/shade/device/ShadeDeviceFailureReason.d.ts +1 -1
  272. package/src/shade/device/ShadeDeviceFailureReason.js +2 -2
  273. package/src/shade/device/mock/SoftwareGPUDevice.d.ts +15 -0
  274. package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
  275. package/src/shade/device/mock/SoftwareGPUDevice.js +21 -0
  276. package/src/shade/device/pipeline/ComputePipelineManager.d.ts +20 -0
  277. package/src/shade/device/pipeline/ComputePipelineManager.d.ts.map +1 -1
  278. package/src/shade/device/pipeline/ComputePipelineManager.js +80 -6
  279. package/src/shade/device/pipeline/PipelineEnquiry.d.ts +52 -0
  280. package/src/shade/device/pipeline/PipelineEnquiry.d.ts.map +1 -0
  281. package/src/shade/device/pipeline/PipelineEnquiry.js +63 -0
  282. package/src/shade/device/pipeline/RenderPipelineManager.d.ts +21 -0
  283. package/src/shade/device/pipeline/RenderPipelineManager.d.ts.map +1 -1
  284. package/src/shade/device/pipeline/RenderPipelineManager.js +84 -4
  285. package/src/shade/device/shade_device_floor_unmet.d.ts +13 -0
  286. package/src/shade/device/shade_device_floor_unmet.d.ts.map +1 -0
  287. package/src/shade/device/shade_device_floor_unmet.js +32 -0
  288. package/src/shade/playground/contact_shadows/ContactShadowLab.d.ts +137 -0
  289. package/src/shade/playground/contact_shadows/ContactShadowLab.d.ts.map +1 -0
  290. package/src/shade/playground/contact_shadows/ContactShadowLab.js +654 -0
  291. package/src/shade/playground/contact_shadows/README.md +126 -0
  292. package/src/shade/playground/contact_shadows/index.html +191 -0
  293. package/src/shade/playground/contact_shadows/main.d.ts +2 -0
  294. package/src/shade/playground/contact_shadows/main.d.ts.map +1 -0
  295. package/src/shade/playground/contact_shadows/main.js +888 -0
  296. package/src/shade/playground/contact_shadows/scenes.d.ts +48 -0
  297. package/src/shade/playground/contact_shadows/scenes.d.ts.map +1 -0
  298. package/src/shade/playground/contact_shadows/scenes.js +620 -0
  299. package/src/shade/playground/contact_shadows/shader_contact_lab_composite.d.ts +36 -0
  300. package/src/shade/playground/contact_shadows/shader_contact_lab_composite.d.ts.map +1 -0
  301. package/src/shade/playground/contact_shadows/shader_contact_lab_composite.js +245 -0
  302. package/src/shade/playground/contact_shadows/shader_contact_lab_reference.d.ts +15 -0
  303. package/src/shade/playground/contact_shadows/shader_contact_lab_reference.d.ts.map +1 -0
  304. package/src/shade/playground/contact_shadows/shader_contact_lab_reference.js +120 -0
  305. package/src/shade/playground/contact_shadows/shader_contact_lab_stats.d.ts +55 -0
  306. package/src/shade/playground/contact_shadows/shader_contact_lab_stats.d.ts.map +1 -0
  307. package/src/shade/playground/contact_shadows/shader_contact_lab_stats.js +216 -0
  308. package/src/shade/playground/contact_shadows/shader_contact_lab_view.d.ts +20 -0
  309. package/src/shade/playground/contact_shadows/shader_contact_lab_view.d.ts.map +1 -0
  310. package/src/shade/playground/contact_shadows/shader_contact_lab_view.js +114 -0
  311. package/src/shade/playground/contact_shadows/shader_contact_shadow_march.d.ts +11 -0
  312. package/src/shade/playground/contact_shadows/shader_contact_shadow_march.d.ts.map +1 -0
  313. package/src/shade/playground/contact_shadows/shader_contact_shadow_march.js +90 -0
  314. package/src/shade/playground/contact_shadows/textures.d.ts +16 -0
  315. package/src/shade/playground/contact_shadows/textures.d.ts.map +1 -0
  316. package/src/shade/playground/contact_shadows/textures.js +123 -0
  317. package/src/shade/playground/particle_ecs/README.md +1 -1
  318. package/src/shade/playground/particle_ecs/effects/flame.json +1932 -1881
  319. package/src/shade/playground/particle_ecs/effects/smoke.json +99 -1
  320. package/src/shade/playground/particle_ecs/effects/soot.json +152 -145
  321. package/src/shade/playground/tonemap_sdr/README.md +90 -0
  322. package/src/shade/playground/tonemap_sdr/ToneMapCompare.d.ts +105 -0
  323. package/src/shade/playground/tonemap_sdr/ToneMapCompare.d.ts.map +1 -0
  324. package/src/shade/playground/tonemap_sdr/ToneMapCompare.js +380 -0
  325. package/src/shade/playground/tonemap_sdr/chunk_tonemap_gt7_sdr_reference.d.ts +32 -0
  326. package/src/shade/playground/tonemap_sdr/chunk_tonemap_gt7_sdr_reference.d.ts.map +1 -0
  327. package/src/shade/playground/tonemap_sdr/chunk_tonemap_gt7_sdr_reference.js +104 -0
  328. package/src/shade/playground/tonemap_sdr/index.html +90 -0
  329. package/src/shade/playground/tonemap_sdr/main.d.ts +2 -0
  330. package/src/shade/playground/tonemap_sdr/main.d.ts.map +1 -0
  331. package/src/shade/playground/tonemap_sdr/main.js +306 -0
  332. package/src/shade/playground/tonemap_sdr/scenes.d.ts +26 -0
  333. package/src/shade/playground/tonemap_sdr/scenes.d.ts.map +1 -0
  334. package/src/shade/playground/tonemap_sdr/scenes.js +404 -0
  335. package/src/shade/playground/volumetrics_froxel/main.d.ts.map +1 -1
  336. package/src/shade/playground/volumetrics_froxel/main.js +85 -4
  337. package/src/shade/playground/water_refraction/README.md +26 -18
  338. package/src/shade/playground/water_refraction/index.html +1 -1
  339. package/src/shade/renderer/Renderer.d.ts +37 -3
  340. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  341. package/src/shade/renderer/Renderer.js +237 -67
  342. package/src/shade/renderer/ShadeToneMappingMode.d.ts +14 -0
  343. package/src/shade/renderer/ShadeToneMappingMode.d.ts.map +1 -0
  344. package/src/shade/renderer/ShadeToneMappingMode.js +30 -0
  345. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +9 -0
  346. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  347. package/src/shade/renderer/animation/GPUAnimationManager.js +14 -0
  348. package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.d.ts.map +1 -1
  349. package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.js +5 -3
  350. package/src/shade/renderer/buffer/GPUBufferAllocator.d.ts.map +1 -1
  351. package/src/shade/renderer/buffer/GPUBufferAllocator.js +9 -0
  352. package/src/shade/renderer/buffer/buffer_pool_ops.d.ts +21 -12
  353. package/src/shade/renderer/buffer/buffer_pool_ops.d.ts.map +1 -1
  354. package/src/shade/renderer/buffer/buffer_pool_ops.js +18 -19
  355. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts +7 -0
  356. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
  357. package/src/shade/renderer/buffer/table/GPUDatabase.js +12 -0
  358. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts +8 -0
  359. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
  360. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +12 -0
  361. package/src/shade/renderer/camera/GPUCameraContext.d.ts.map +1 -1
  362. package/src/shade/renderer/camera/GPUCameraContext.js +19 -8
  363. package/src/shade/renderer/deferred/chunk_read_gBuffer_material.d.ts.map +1 -1
  364. package/src/shade/renderer/deferred/chunk_read_gBuffer_material.js +4 -0
  365. package/src/shade/renderer/deferred/chunk_shade_standard_material_direct.d.ts.map +1 -1
  366. package/src/shade/renderer/deferred/chunk_shade_standard_material_direct.js +4 -0
  367. package/src/shade/renderer/deferred/chunk_shade_standard_material_direct_restir.d.ts.map +1 -1
  368. package/src/shade/renderer/deferred/chunk_shade_standard_material_direct_restir.js +4 -0
  369. package/src/shade/renderer/deferred/shader_deferred_main.d.ts.map +1 -1
  370. package/src/shade/renderer/deferred/shader_deferred_main.js +3 -0
  371. package/src/shade/renderer/deferred/shader_tonemap_HDR.d.ts +6 -0
  372. package/src/shade/renderer/deferred/shader_tonemap_HDR.d.ts.map +1 -1
  373. package/src/shade/renderer/deferred/shader_tonemap_HDR.js +13 -3
  374. package/src/shade/renderer/deferred/shader_tonemap_HDR_none.d.ts +9 -0
  375. package/src/shade/renderer/deferred/shader_tonemap_HDR_none.d.ts.map +1 -0
  376. package/src/shade/renderer/deferred/shader_tonemap_HDR_none.js +63 -0
  377. package/src/shade/renderer/deferred/shader_tonemap_HDR_pbr_neutral.d.ts +10 -0
  378. package/src/shade/renderer/deferred/shader_tonemap_HDR_pbr_neutral.d.ts.map +1 -0
  379. package/src/shade/renderer/deferred/shader_tonemap_HDR_pbr_neutral.js +87 -0
  380. package/src/shade/renderer/deferred/shader_tonemap_LDR.d.ts +6 -0
  381. package/src/shade/renderer/deferred/shader_tonemap_LDR.d.ts.map +1 -1
  382. package/src/shade/renderer/deferred/shader_tonemap_LDR.js +23 -5
  383. package/src/shade/renderer/deferred/shader_tonemap_LDR_none.d.ts +9 -0
  384. package/src/shade/renderer/deferred/shader_tonemap_LDR_none.d.ts.map +1 -0
  385. package/src/shade/renderer/deferred/shader_tonemap_LDR_none.js +68 -0
  386. package/src/shade/renderer/deferred/shader_tonemap_LDR_pbr_neutral.d.ts +10 -0
  387. package/src/shade/renderer/deferred/shader_tonemap_LDR_pbr_neutral.d.ts.map +1 -0
  388. package/src/shade/renderer/deferred/shader_tonemap_LDR_pbr_neutral.js +73 -0
  389. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  390. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +58 -9
  391. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +35 -25
  392. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  393. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +97 -196
  394. package/src/shade/renderer/geometry/sdf/shader_build_sdf.d.ts.map +1 -1
  395. package/src/shade/renderer/geometry/sdf/shader_build_sdf.js +121 -129
  396. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.d.ts.map +1 -1
  397. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.js +3 -0
  398. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.d.ts.map +1 -1
  399. package/src/shade/renderer/global_illumination/brick4/gpu/bake/shader_brick4_bake_probes.js +4 -0
  400. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_indirect_lighting.d.ts.map +1 -1
  401. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_indirect_lighting.js +4 -0
  402. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts.map +1 -1
  403. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.js +3 -0
  404. package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.d.ts.map +1 -1
  405. package/src/shade/renderer/global_illumination/lpv/deprecated/probe_trace_compute_shader_code.js +4 -0
  406. package/src/shade/renderer/global_illumination/probe/octahedral/graph_sample_light_probes.d.ts.map +1 -1
  407. package/src/shade/renderer/global_illumination/probe/octahedral/graph_sample_light_probes.js +4 -0
  408. package/src/shade/renderer/global_illumination/shader_resolve_indirect_lighting.d.ts.map +1 -1
  409. package/src/shade/renderer/global_illumination/shader_resolve_indirect_lighting.js +4 -0
  410. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +131 -1
  411. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.d.ts +7 -0
  412. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.d.ts.map +1 -1
  413. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_delta.js +45 -38
  414. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.d.ts +2 -0
  415. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.d.ts.map +1 -1
  416. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_determine_range.js +81 -79
  417. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.d.ts +2 -0
  418. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.d.ts.map +1 -1
  419. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_find_split.js +5 -3
  420. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_internal_children.d.ts +21 -0
  421. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_internal_children.d.ts.map +1 -0
  422. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_internal_children.js +42 -0
  423. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_morton_code.d.ts +19 -0
  424. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_morton_code.d.ts.map +1 -0
  425. package/src/shade/renderer/gpu_primitive/bvh/chunk_bvh_morton_code.js +44 -0
  426. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts +23 -5
  427. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts.map +1 -1
  428. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.js +139 -23
  429. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_build_single_workgroup.d.ts +25 -0
  430. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_build_single_workgroup.d.ts.map +1 -0
  431. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_build_single_workgroup.js +179 -0
  432. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts +2 -2
  433. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts.map +1 -1
  434. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.js +7 -16
  435. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts +3 -4
  436. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts.map +1 -1
  437. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.js +6 -23
  438. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts +11 -0
  439. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts.map +1 -1
  440. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.js +14 -3
  441. package/src/shade/renderer/gpu_primitive/indirect/build_indirect_draw_command.d.ts +10 -0
  442. package/src/shade/renderer/gpu_primitive/indirect/build_indirect_draw_command.d.ts.map +1 -1
  443. package/src/shade/renderer/gpu_primitive/indirect/build_indirect_draw_command.js +13 -3
  444. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts +10 -0
  445. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts.map +1 -1
  446. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js +14 -0
  447. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_comparator.d.ts +26 -0
  448. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_comparator.d.ts.map +1 -0
  449. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_comparator.js +52 -0
  450. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_workgroup.d.ts +32 -0
  451. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_workgroup.d.ts.map +1 -0
  452. package/src/shade/renderer/gpu_primitive/sort/chunk_sort_bitonic_workgroup.js +89 -0
  453. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts +10 -0
  454. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts.map +1 -1
  455. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.js +20 -1
  456. package/src/shade/renderer/gpu_primitive/test/gpu_test.d.ts.map +1 -1
  457. package/src/shade/renderer/gpu_primitive/test/gpu_test.js +8 -11
  458. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +10 -0
  459. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  460. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +294 -281
  461. package/src/shade/renderer/light/GPULightCollection.d.ts +8 -0
  462. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  463. package/src/shade/renderer/light/GPULightCollection.js +18 -1
  464. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts +8 -0
  465. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts.map +1 -1
  466. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.js +234 -218
  467. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts +7 -0
  468. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts.map +1 -1
  469. package/src/shade/renderer/light/cluster/graph_assign_clsuters.js +13 -0
  470. package/src/shade/renderer/light/cluster/graph_build_light_clusters.d.ts +7 -0
  471. package/src/shade/renderer/light/cluster/graph_build_light_clusters.d.ts.map +1 -1
  472. package/src/shade/renderer/light/cluster/graph_build_light_clusters.js +16 -2
  473. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +10 -0
  474. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
  475. package/src/shade/renderer/light/environment/execute_filter_environment_map.js +13 -0
  476. package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
  477. package/src/shade/renderer/lightmap/GPULightMap.js +3 -0
  478. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.d.ts.map +1 -1
  479. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_irradiance_flat.js +4 -0
  480. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.d.ts.map +1 -1
  481. package/src/shade/renderer/lightmap/bake/shader_lightmap_viz_to_sh.js +4 -0
  482. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  483. package/src/shade/renderer/material/GPUMaterialManager.d.ts +16 -4
  484. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  485. package/src/shade/renderer/material/GPUMaterialManager.js +38 -8
  486. package/src/shade/renderer/particles/DESIGN.md +49 -7
  487. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +453 -0
  488. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +5 -1
  489. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
  490. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +13 -5
  491. package/src/shade/renderer/particles/data/particle_emitter_record.js +2 -2
  492. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.d.ts.map +1 -1
  493. package/src/shade/renderer/particles/graph/groups/particle_builtin_groups.js +12 -0
  494. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts +60 -0
  495. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.d.ts.map +1 -1
  496. package/src/shade/renderer/particles/graph/groups/particle_groups_geometry.js +156 -0
  497. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +2 -2
  498. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
  499. package/src/shade/renderer/particles/runtime/ParticleEmitter.js +8 -8
  500. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.d.ts.map +1 -1
  501. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.js +6 -4
  502. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts +7 -6
  503. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts.map +1 -1
  504. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +91 -58
  505. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
  506. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +10 -0
  507. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.d.ts.map +1 -1
  508. package/src/shade/renderer/path_tracer/accumulating/shader_accumulating_path_tracer.js +5 -1
  509. package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts.map +1 -1
  510. package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +6 -0
  511. package/src/shade/renderer/pool/TransientResourcePool.d.ts +24 -0
  512. package/src/shade/renderer/pool/TransientResourcePool.d.ts.map +1 -1
  513. package/src/shade/renderer/pool/TransientResourcePool.js +41 -10
  514. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.d.ts +7 -0
  515. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.d.ts.map +1 -1
  516. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +18 -0
  517. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.d.ts.map +1 -1
  518. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.js +1 -0
  519. package/src/shade/renderer/postprocess/denoise/shader_temporal_reprojection_luma.d.ts.map +1 -1
  520. package/src/shade/renderer/postprocess/denoise/shader_temporal_reprojection_luma.js +6 -4
  521. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts +11 -0
  522. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts.map +1 -1
  523. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.js +16 -0
  524. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts +10 -0
  525. package/src/shade/renderer/postprocess/gtao/GTAO.d.ts.map +1 -1
  526. package/src/shade/renderer/postprocess/gtao/GTAO.js +16 -0
  527. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.d.ts.map +1 -1
  528. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao.js +32 -10
  529. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  530. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts.map +1 -1
  531. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +3 -0
  532. package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.d.ts.map +1 -1
  533. package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.js +6 -3
  534. package/src/shade/renderer/postprocess/taa/TAA.d.ts +12 -0
  535. package/src/shade/renderer/postprocess/taa/TAA.d.ts.map +1 -1
  536. package/src/shade/renderer/postprocess/taa/TAA.js +15 -0
  537. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts +7 -0
  538. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.d.ts.map +1 -1
  539. package/src/shade/renderer/postprocess/velocity/graph_add_per_object_velocity.js +13 -0
  540. package/src/shade/renderer/rasterize/TemplatePassDescriptor.d.ts +31 -3
  541. package/src/shade/renderer/rasterize/TemplatePassDescriptor.d.ts.map +1 -1
  542. package/src/shade/renderer/rasterize/TemplatePassDescriptor.js +79 -4
  543. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.d.ts +17 -0
  544. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.d.ts.map +1 -1
  545. package/src/shade/renderer/rasterize/bucket/graph_meshlets_rasterize_by_bucket.js +35 -4
  546. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts +7 -0
  547. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts.map +1 -1
  548. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.js +182 -169
  549. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.d.ts +10 -0
  550. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.d.ts.map +1 -1
  551. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.js +20 -1
  552. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_extract_meshlets_from_bucket.d.ts +10 -0
  553. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_extract_meshlets_from_bucket.d.ts.map +1 -1
  554. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_extract_meshlets_from_bucket.js +18 -1
  555. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts +10 -0
  556. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts.map +1 -1
  557. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.js +20 -1
  558. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts +7 -0
  559. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts.map +1 -1
  560. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.js +187 -170
  561. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts +10 -0
  562. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts.map +1 -1
  563. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.js +19 -1
  564. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts +13 -0
  565. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  566. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +25 -1
  567. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts +10 -0
  568. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts.map +1 -1
  569. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.js +20 -0
  570. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  571. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.js +6 -0
  572. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  573. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.js +6 -0
  574. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +3 -2
  575. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
  576. package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +3 -2
  577. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +7 -2
  578. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  579. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +12 -4
  580. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_brick4.d.ts.map +1 -1
  581. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_brick4.js +4 -3
  582. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_ibl.d.ts.map +1 -1
  583. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_draw_ibl.js +4 -3
  584. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts +2 -1
  585. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
  586. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +11 -3
  587. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.d.ts.map +1 -1
  588. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.js +4 -3
  589. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.d.ts.map +1 -1
  590. package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +4 -3
  591. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts +15 -0
  592. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  593. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +35 -2
  594. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts +14 -0
  595. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  596. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +32 -4
  597. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts +17 -0
  598. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  599. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +34 -4
  600. package/src/shade/renderer/restir/di/chunk_restir_resolve.d.ts +5 -3
  601. package/src/shade/renderer/restir/di/chunk_restir_resolve.d.ts.map +1 -1
  602. package/src/shade/renderer/restir/di/chunk_restir_resolve.js +8 -3
  603. package/src/shade/renderer/restir/di/graph_restir_di.d.ts.map +1 -1
  604. package/src/shade/renderer/restir/di/graph_restir_di.js +1 -0
  605. package/src/shade/renderer/restir/di/shader_restir_di_resolve.d.ts.map +1 -1
  606. package/src/shade/renderer/restir/di/shader_restir_di_resolve.js +5 -1
  607. package/src/shade/renderer/restir/reservoir/shader.d.ts.map +1 -1
  608. package/src/shade/renderer/restir/reservoir/shader.js +7 -4
  609. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts +7 -0
  610. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts.map +1 -1
  611. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +13 -1
  612. package/src/shade/renderer/scene/GPUSceneContext.d.ts +10 -0
  613. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  614. package/src/shade/renderer/scene/GPUSceneContext.js +19 -0
  615. package/src/shade/renderer/scene/SceneManager.d.ts +9 -0
  616. package/src/shade/renderer/scene/SceneManager.d.ts.map +1 -1
  617. package/src/shade/renderer/scene/SceneManager.js +11 -0
  618. package/src/shade/renderer/scene/hierarchy/graph_nodes_update_transform_hierarchy.d.ts +11 -0
  619. package/src/shade/renderer/scene/hierarchy/graph_nodes_update_transform_hierarchy.d.ts.map +1 -1
  620. package/src/shade/renderer/scene/hierarchy/graph_nodes_update_transform_hierarchy.js +17 -0
  621. package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.d.ts +11 -0
  622. package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.d.ts.map +1 -1
  623. package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy.js +18 -1
  624. package/src/shade/renderer/shader/ComputeShader.d.ts +12 -0
  625. package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
  626. package/src/shade/renderer/shader/ComputeShader.js +71 -2
  627. package/src/shade/renderer/shader/ImageShader.d.ts +27 -8
  628. package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
  629. package/src/shade/renderer/shader/ImageShader.js +299 -218
  630. package/src/shade/renderer/shader/ShaderDescriptor.d.ts +21 -0
  631. package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
  632. package/src/shade/renderer/shader/ShaderDescriptor.js +71 -1
  633. package/src/shade/renderer/shader/chunk/camera/chunk_project_view_position_from_depth.d.ts +17 -0
  634. package/src/shade/renderer/shader/chunk/camera/chunk_project_view_position_from_depth.d.ts.map +1 -0
  635. package/src/shade/renderer/shader/chunk/camera/chunk_project_view_position_from_depth.js +45 -0
  636. package/src/shade/renderer/shader/chunk/camera/chunk_screen_coordinate_to_view.js +3 -3
  637. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.d.ts +1 -1
  638. package/src/shade/renderer/shader/chunk/color/iCtCp/chunk_eotf_st2084.js +62 -62
  639. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +318 -0
  640. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +237 -0
  641. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_PAPER_WHITE_NITS.d.ts +17 -0
  642. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_PAPER_WHITE_NITS.d.ts.map +1 -0
  643. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_PAPER_WHITE_NITS.js +22 -0
  644. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_khronos_pbr.d.ts +14 -1
  645. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_khronos_pbr.d.ts.map +1 -1
  646. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_khronos_pbr.js +28 -15
  647. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts +12 -0
  648. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.d.ts.map +1 -1
  649. package/src/shade/renderer/shader/chunk/color/tonemap/gt7/chunk_tonemap_gt7.js +128 -110
  650. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_offset_ray_from_depth.d.ts +20 -0
  651. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_offset_ray_from_depth.d.ts.map +1 -0
  652. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_offset_ray_from_depth.js +33 -0
  653. package/src/shade/renderer/shader/chunk/light/chunk_compute_multiscattering_specular.d.ts +4 -0
  654. package/src/shade/renderer/shader/chunk/light/chunk_compute_multiscattering_specular.d.ts.map +1 -1
  655. package/src/shade/renderer/shader/chunk/light/chunk_compute_multiscattering_specular.js +12 -1
  656. package/src/shade/renderer/shader/chunk/material/MATERIAL_SHADING_INFO_STRUCT.d.ts.map +1 -1
  657. package/src/shade/renderer/shader/chunk/material/MATERIAL_SHADING_INFO_STRUCT.js +7 -0
  658. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +108 -0
  659. package/src/shade/renderer/shader/chunk/material/chunk_compute_indirect_specular.d.ts.map +1 -1
  660. package/src/shade/renderer/shader/chunk/material/chunk_compute_indirect_specular.js +23 -4
  661. package/src/shade/renderer/shader/chunk/material/chunk_re_direct_multiscatter.d.ts +18 -0
  662. package/src/shade/renderer/shader/chunk/material/chunk_re_direct_multiscatter.d.ts.map +1 -0
  663. package/src/shade/renderer/shader/chunk/material/chunk_re_direct_multiscatter.js +56 -0
  664. package/src/shade/renderer/shader/chunk/material/chunk_re_indirect_specular_physical.d.ts.map +1 -1
  665. package/src/shade/renderer/shader/chunk/material/chunk_re_indirect_specular_physical.js +2 -0
  666. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_linear_ray.d.ts +43 -0
  667. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_linear_ray.d.ts.map +1 -0
  668. package/src/shade/renderer/shader/chunk/screen_space/chunk_screen_space_linear_ray.js +319 -0
  669. package/src/shade/renderer/shader/compiler/CodeChunk.d.ts +33 -0
  670. package/src/shade/renderer/shader/compiler/CodeChunk.d.ts.map +1 -1
  671. package/src/shade/renderer/shader/compiler/CodeChunk.js +140 -0
  672. package/src/shade/renderer/shader/compiler/CodeChunkChoice.d.ts +74 -0
  673. package/src/shade/renderer/shader/compiler/CodeChunkChoice.d.ts.map +1 -0
  674. package/src/shade/renderer/shader/compiler/CodeChunkChoice.js +166 -0
  675. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +680 -0
  676. package/src/shade/renderer/shader/compiler/ShaderSwitch.d.ts +86 -0
  677. package/src/shade/renderer/shader/compiler/ShaderSwitch.d.ts.map +1 -0
  678. package/src/shade/renderer/shader/compiler/ShaderSwitch.js +178 -0
  679. package/src/shade/renderer/shader/compiler/ShaderSwitchSet.d.ts +67 -0
  680. package/src/shade/renderer/shader/compiler/ShaderSwitchSet.d.ts.map +1 -0
  681. package/src/shade/renderer/shader/compiler/ShaderSwitchSet.js +205 -0
  682. package/src/shade/renderer/shadow/contact/CONTACT_SHADOW_WAVE_SIZE.d.ts +14 -0
  683. package/src/shade/renderer/shadow/contact/CONTACT_SHADOW_WAVE_SIZE.d.ts.map +1 -0
  684. package/src/shade/renderer/shadow/contact/CONTACT_SHADOW_WAVE_SIZE.js +13 -0
  685. package/src/shade/renderer/shadow/contact/ContactShadowDispatchList.d.ts +65 -0
  686. package/src/shade/renderer/shadow/contact/ContactShadowDispatchList.d.ts.map +1 -0
  687. package/src/shade/renderer/shadow/contact/ContactShadowDispatchList.js +252 -0
  688. package/src/shade/renderer/shadow/contact/FINDINGS.md +413 -0
  689. package/src/shade/renderer/shadow/contact/chunk_contact_shadow_wavefront_extents.d.ts +29 -0
  690. package/src/shade/renderer/shadow/contact/chunk_contact_shadow_wavefront_extents.d.ts.map +1 -0
  691. package/src/shade/renderer/shadow/contact/chunk_contact_shadow_wavefront_extents.js +105 -0
  692. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.d.ts +29 -0
  693. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.d.ts.map +1 -0
  694. package/src/shade/renderer/shadow/contact/graph_contact_shadow_linear.js +39 -0
  695. package/src/shade/renderer/shadow/contact/graph_contact_shadow_wavefront.d.ts +29 -0
  696. package/src/shade/renderer/shadow/contact/graph_contact_shadow_wavefront.d.ts.map +1 -0
  697. package/src/shade/renderer/shadow/contact/graph_contact_shadow_wavefront.js +92 -0
  698. package/src/shade/renderer/shadow/contact/shader_contact_shadow_linear.d.ts +29 -0
  699. package/src/shade/renderer/shadow/contact/shader_contact_shadow_linear.d.ts.map +1 -0
  700. package/src/shade/renderer/shadow/contact/shader_contact_shadow_linear.js +128 -0
  701. package/src/shade/renderer/shadow/contact/shader_contact_shadow_wavefront.d.ts +52 -0
  702. package/src/shade/renderer/shadow/contact/shader_contact_shadow_wavefront.d.ts.map +1 -0
  703. package/src/shade/renderer/shadow/contact/shader_contact_shadow_wavefront.js +358 -0
  704. package/src/shade/renderer/shadow/map/ShadowDirectionalTechnique.d.ts +10 -0
  705. package/src/shade/renderer/shadow/map/ShadowDirectionalTechnique.d.ts.map +1 -0
  706. package/src/shade/renderer/shadow/map/ShadowDirectionalTechnique.js +12 -0
  707. package/src/shade/renderer/shadow/map/shader/SHADOW_DIRECTIONAL_TECHNIQUE.d.ts +13 -0
  708. package/src/shade/renderer/shadow/map/shader/SHADOW_DIRECTIONAL_TECHNIQUE.d.ts.map +1 -0
  709. package/src/shade/renderer/shadow/map/shader/SHADOW_DIRECTIONAL_TECHNIQUE.js +14 -0
  710. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts +14 -8
  711. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.d.ts.map +1 -1
  712. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional.js +20 -86
  713. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional_cascades.d.ts +14 -0
  714. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional_cascades.d.ts.map +1 -0
  715. package/src/shade/renderer/shadow/map/shader/chunk_shadowmap_sample_directional_cascades.js +79 -0
  716. package/src/shade/renderer/shadow/ray/shader_ray_shadow_code.d.ts.map +1 -1
  717. package/src/shade/renderer/shadow/ray/shader_ray_shadow_code.js +12 -12
  718. package/src/shade/renderer/shadow/ray/staged/gen/shader_generate_rays.d.ts.map +1 -1
  719. package/src/shade/renderer/shadow/ray/staged/gen/shader_generate_rays.js +153 -151
  720. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_directional_virtual.d.ts +13 -0
  721. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_directional_virtual.d.ts.map +1 -0
  722. package/src/shade/renderer/shadow/vsm/shader/chunk_shadowmap_sample_directional_virtual.js +48 -0
  723. package/src/shade/renderer/texture/GPUTextureContext.d.ts +10 -0
  724. package/src/shade/renderer/texture/GPUTextureContext.d.ts.map +1 -1
  725. package/src/shade/renderer/texture/GPUTextureContext.js +24 -0
  726. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts +219 -0
  727. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.d.ts.map +1 -0
  728. package/src/shade/renderer/texture/atlas/GPUTileAtlas3D.js +671 -0
  729. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts +26 -2
  730. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts.map +1 -1
  731. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +108 -4
  732. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +5 -1
  733. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  734. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +6 -0
  735. package/src/shade/renderer/volumetrics/NOTES.md +50 -0
  736. package/src/shade/renderer/volumetrics/PARTICIPATING_MEDIA_VOLUME_STRUCT.d.ts.map +1 -1
  737. package/src/shade/renderer/volumetrics/PARTICIPATING_MEDIA_VOLUME_STRUCT.js +30 -1
  738. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +51 -13
  739. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
  740. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +98 -13
  741. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts +9 -0
  742. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts.map +1 -1
  743. package/src/shade/renderer/volumetrics/SceneVolumetrics.js +78 -68
  744. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +326 -0
  745. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts +7 -4
  746. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.d.ts.map +1 -1
  747. package/src/shade/renderer/volumetrics/chunk_participating_media_volume_sample.js +21 -9
  748. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_uvw_from_clip.d.ts +19 -0
  749. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_uvw_from_clip.d.ts.map +1 -0
  750. package/src/shade/renderer/volumetrics/chunk_volumetrics_froxel_uvw_from_clip.js +53 -0
  751. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts +12 -3
  752. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.d.ts.map +1 -1
  753. package/src/shade/renderer/volumetrics/chunk_volumetrics_sample_froxel_extinction.js +85 -48
  754. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts +4 -1
  755. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts.map +1 -1
  756. package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.js +4 -1
  757. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +4 -1
  758. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  759. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +21 -1
  760. package/src/shade/renderer/volumetrics/noise/PARTICIPATING_MEDIA_NOISE.d.ts +98 -0
  761. package/src/shade/renderer/volumetrics/noise/PARTICIPATING_MEDIA_NOISE.d.ts.map +1 -0
  762. package/src/shade/renderer/volumetrics/noise/PARTICIPATING_MEDIA_NOISE.js +115 -0
  763. package/src/shade/renderer/volumetrics/noise/VOLUMETRICS_NOISE_TILE_BAKE_SETTINGS_STRUCT.d.ts +9 -0
  764. package/src/shade/renderer/volumetrics/noise/VOLUMETRICS_NOISE_TILE_BAKE_SETTINGS_STRUCT.d.ts.map +1 -0
  765. package/src/shade/renderer/volumetrics/noise/VOLUMETRICS_NOISE_TILE_BAKE_SETTINGS_STRUCT.js +24 -0
  766. package/src/shade/renderer/volumetrics/noise/bake_volumetrics_noise_tile.d.ts +27 -0
  767. package/src/shade/renderer/volumetrics/noise/bake_volumetrics_noise_tile.d.ts.map +1 -0
  768. package/src/shade/renderer/volumetrics/noise/bake_volumetrics_noise_tile.js +91 -0
  769. package/src/shade/renderer/volumetrics/noise/chunk_participating_media_noise.d.ts +35 -0
  770. package/src/shade/renderer/volumetrics/noise/chunk_participating_media_noise.d.ts.map +1 -0
  771. package/src/shade/renderer/volumetrics/noise/chunk_participating_media_noise.js +92 -0
  772. package/src/shade/renderer/volumetrics/noise/shader_volumetrics_noise_tile_bake.d.ts +10 -0
  773. package/src/shade/renderer/volumetrics/noise/shader_volumetrics_noise_tile_bake.d.ts.map +1 -0
  774. package/src/shade/renderer/volumetrics/noise/shader_volumetrics_noise_tile_bake.js +121 -0
  775. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.d.ts.map +1 -1
  776. package/src/shade/renderer/volumetrics/shader_volumetrics_build_participating_media.js +38 -3
  777. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.d.ts.map +1 -1
  778. package/src/shade/renderer/volumetrics/shader_volumetrics_build_sun_shadow_volume.js +311 -290
  779. package/src/shade/renderer/volumetrics/volumetrics_position_world_to_froxel_uvw.d.ts.map +1 -1
  780. package/src/shade/renderer/volumetrics/volumetrics_position_world_to_froxel_uvw.js +28 -49
  781. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +3 -1
  782. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  783. package/src/shade/renderer/water/GPUWaterRenderer.js +24 -18
  784. package/src/shade/renderer/water/chunk_water_transport.d.ts +0 -1
  785. package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -1
  786. package/src/shade/renderer/water/chunk_water_transport.js +0 -17
  787. package/src/shade/renderer/water/shader_water.d.ts +4 -2
  788. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  789. package/src/shade/renderer/water/shader_water.js +27 -16
  790. package/src/shade/renderer/water/shader_water_avboit.d.ts +0 -4
  791. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  792. package/src/shade/renderer/water/shader_water_avboit.js +0 -63
  793. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  794. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +43 -9
  795. package/src/view/graphics/GraphicsContextFailureView.d.ts +1 -2
  796. package/src/view/graphics/GraphicsContextFailureView.d.ts.map +1 -1
  797. package/src/view/graphics/GraphicsContextFailureView.js +1 -2
  798. package/src/engine/network/transport/Channel.d.ts +0 -85
  799. package/src/engine/network/transport/Channel.d.ts.map +0 -1
  800. package/src/engine/network/transport/Channel.js +0 -336
  801. package/src/engine/network/transport/ReliableCommandPipeline.d.ts +0 -155
  802. package/src/engine/network/transport/ReliableCommandPipeline.d.ts.map +0 -1
  803. package/src/engine/network/transport/ReliableCommandPipeline.js +0 -335
  804. package/src/engine/network/transport/fragments/FragmentAssembler.d.ts +0 -105
  805. package/src/engine/network/transport/fragments/FragmentAssembler.d.ts.map +0 -1
  806. package/src/engine/network/transport/fragments/FragmentAssembler.js +0 -350
  807. package/src/engine/network/transport/fragments/FragmentRetention.d.ts +0 -103
  808. package/src/engine/network/transport/fragments/FragmentRetention.d.ts.map +0 -1
  809. package/src/engine/network/transport/fragments/FragmentRetention.js +0 -191
  810. package/src/engine/network/transport/fragments/fragment_send.d.ts +0 -61
  811. package/src/engine/network/transport/fragments/fragment_send.d.ts.map +0 -1
  812. package/src/engine/network/transport/fragments/fragment_send.js +0 -157
  813. package/src/engine/network/transport/fragments/packet_size.d.ts +0 -99
  814. package/src/engine/network/transport/fragments/packet_size.d.ts.map +0 -1
  815. package/src/engine/network/transport/fragments/packet_size.js +0 -107
  816. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_RRTAndODTFit.d.ts +0 -6
  817. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_RRTAndODTFit.d.ts.map +0 -1
  818. package/src/shade/renderer/shader/chunk/color/sRGB/chunk_RRTAndODTFit.js +0 -16
  819. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_aces.d.ts +0 -7
  820. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_aces.d.ts.map +0 -1
  821. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_aces.js +0 -46
  822. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_agx.d.ts +0 -10
  823. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_agx.d.ts.map +0 -1
  824. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_agx.js +0 -127
  825. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_pbr_neutral.d.ts +0 -7
  826. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_pbr_neutral.d.ts.map +0 -1
  827. package/src/shade/renderer/shader/chunk/color/tonemap/chunk_tonemap_pbr_neutral.js +0 -39
@@ -2,7 +2,6 @@ import { assert } from "../../../core/assert.js";
2
2
  import { BinaryBuffer } from "../../../core/binary/BinaryBuffer.js";
3
3
  import Signal from "../../../core/events/signal/Signal.js";
4
4
  import { MalformedPacketError } from "../core/MalformedPacketError.js";
5
- import { seq16_advance } from "../core/sequence/seq16.js";
6
5
  import { Replicator } from "../replication/Replicator.js";
7
6
  import { AlwaysRelevantScope } from "../replication/ScopeFilter.js";
8
7
  import { ActionLog } from "../sim/ActionLog.js";
@@ -14,12 +13,9 @@ import { Baseline } from "../state/Baseline.js";
14
13
  import { ChangedEntitySet } from "../state/ChangedEntitySet.js";
15
14
  import { MutationLedger } from "../state/MutationLedger.js";
16
15
  import { ReplicationSlotTable } from "../state/ReplicationSlotTable.js";
17
- import { Channel } from "../transport/Channel.js";
18
- import { fragment_chunk_count, resend_fragment_chunk, send_fragmented } from "../transport/fragments/fragment_send.js";
19
- import { FragmentAssembler } from "../transport/fragments/FragmentAssembler.js";
20
- import { FragmentRetention } from "../transport/fragments/FragmentRetention.js";
21
- import { MAX_CHANNEL_PAYLOAD_BYTES, MAX_CHUNKS_PER_MESSAGE } from "../transport/fragments/packet_size.js";
22
- import { ReliableCommandPipeline } from "../transport/ReliableCommandPipeline.js";
16
+ import { Connection, Lane } from "../transport/connection/Connection.js";
17
+ import { PendingSlices } from "./PendingSlices.js";
18
+ import { ReliableFrames } from "./ReliableFrames.js";
23
19
 
24
20
  // DISCONNECT packets carry a short UTF-8 reason label. The encoder/decoder
25
21
  // pair are stateless after construction; sharing module-level singletons
@@ -73,42 +69,18 @@ export const NetworkPacketType = {
73
69
  * like this at frame F."
74
70
  */
75
71
  AUTH_STATE: 3,
72
+ // 4 was FRAGMENT: messages of any size are the connection's business now
76
73
  /**
77
- * One chunk of a fragmented logical payload. Used transparently by the
78
- * peer when an outgoing message exceeds the channel's single-packet
79
- * capacity. The fragment header (uint16 message_id, uint8 chunk_index,
80
- * uint8 total_chunks) follows this type byte; the chunk payload follows
81
- * that. The reassembled bytes are re-dispatched through this same
82
- * switch (so any of the other packet types can be carried fragmented).
83
- */
84
- FRAGMENT: 4,
85
- /**
86
- * Reliable command. Carries a uintVar logical_seq followed by a
87
- * caller-defined payload. Delivered at-least-once with sender-side
88
- * retransmit and receiver-side dedup via {@link ReliableCommandPipeline}.
89
- * Used for chat, level transitions, lobby state — anything where
74
+ * Reliable command: a caller-defined payload, on the connection's USER lane. Arrives once, in
75
+ * order with the other commands. For chat, level transitions, lobby state — anything where
90
76
  * loss-tolerance of the action-stream model isn't acceptable.
91
77
  */
92
78
  RELIABLE_COMMAND: 5,
93
- /**
94
- * Selective-retransmit request from the receiver of a fragmented
95
- * message. Wire layout (after the leading type byte):
96
- *
97
- * uint16 message_id (LE)
98
- * uint8 missing_count
99
- * uint8 missing_indices[missing_count]
100
- *
101
- * The sender's {@link FragmentRetention} looks up the retained
102
- * source bytes and re-emits the listed chunks via
103
- * {@link resend_fragment_chunk} — each chunk of the message once,
104
- * however many times the list names it, and nothing for an index the
105
- * message does not have. Receiver drives NACK timing
106
- * via {@link FragmentAssembler#service}.
107
- */
108
- NACK_FRAGMENT: 6,
79
+ // 6 was NACK_FRAGMENT
109
80
  /**
110
81
  * Server → client time-dilation feedback. Wire layout:
111
82
  * int16 buffer_depth (LE)
83
+ * varint frame_number the server's sim frame as it sent this
112
84
  *
113
85
  * `buffer_depth = max_received_frame - current_sim_frame` from the
114
86
  * server's POV. Client feeds it to {@link TimeDilation} and dilates
@@ -116,15 +88,18 @@ export const NetworkPacketType = {
116
88
  */
117
89
  TIME_DILATION: 7,
118
90
  /**
119
- * Server → newly-connected peer initial-state snapshot. Wire layout:
91
+ * Server → joining peer: one page of the world. Wire layout:
120
92
  * 16 bytes session_token (UUID v1, identifies this peer-session
121
93
  * for later RESUME_HELLO authentication)
122
- * varint frame_number (server sim_frame this snapshot represents)
123
- * bytes payload (Snapshotter wire format)
94
+ * varint frame_number (server sim_frame the page's state is from)
95
+ * uint8 flags ({@link InitialSyncPage})
96
+ * uint32 entity_count
97
+ * bytes entities (Snapshotter entity records)
124
98
  *
125
- * Typically exceeds MTU; fragment-send handles chunking. Sent more than
126
- * once, against loss: a client applies the first to reach it on a
127
- * connection, and takes the rest for the copies they are.
99
+ * A join is a run of pages on the connection's BULK lane, each built when
100
+ * it is sent: the world once, then what changed while it was going out,
101
+ * then an empty FINAL page that names the frame the action stream resumes
102
+ * after. The first page carries FIRST.
128
103
  */
129
104
  INITIAL_SYNC: 8,
130
105
  /**
@@ -183,6 +158,18 @@ export const NetworkPacketType = {
183
158
  FRAME_MARKER: 13,
184
159
  };
185
160
 
161
+ /**
162
+ * Flags of an INITIAL_SYNC page.
163
+ * @readonly
164
+ * @enum {number}
165
+ */
166
+ export const InitialSyncPage = Object.freeze({
167
+ /** The first page of a join: it issues the session token, and replaces the receiver's world. */
168
+ FIRST: 1,
169
+ /** The last page: every entity's state at its frame is now with the receiver. */
170
+ FINAL: 2,
171
+ });
172
+
186
173
  /**
187
174
  * Reason codes for {@link NetworkPacketType.RESUME_REJECT}.
188
175
  * @readonly @enum {number}
@@ -211,40 +198,20 @@ export const ResumeRejectReason = Object.freeze({
211
198
  NoSessionToken: 5,
212
199
  });
213
200
 
214
- /**
215
- * One outbound action-stream send, awaiting confirmation.
216
- *
217
- * A send occupies one transport seq when it fits the MTU and N consecutive
218
- * seqs when it has to be fragmented. All of those seqs map to the SAME record:
219
- * the receiver reassembles a fragmented message all-or-nothing, so the frame
220
- * range is confirmed only when `unacked` reaches zero without `abandoned`
221
- * having been set by a loss along the way.
222
- *
223
- * A tick may make several sends to one peer, each carrying the next slice of
224
- * the owed range (see {@link NetworkPeer#flush_outbound}). They confirm
225
- * independently, and the baseline advances only through slices confirmed in
226
- * order — a later slice landing says nothing about an earlier one still in
227
- * flight. `head` marks the first slice of a tick, whose start is the pack
228
- * start itself; see {@link NetworkPeer#__credit_confirmed}.
229
- *
230
- * @typedef {{frame_start: number, frame_end: number, head: boolean, unacked: number, abandoned: boolean}} PendingAck
231
- * @property {number} frame_start first frame in the packed range
232
- * @property {number} frame_end last frame in the packed range
233
- * @property {boolean} head this send began at the pack start, so nothing
234
- * sendable lies before it and its confirmation credits outright
235
- * @property {number} unacked seqs of this send not yet acked or declared lost
236
- * @property {boolean} abandoned at least one seq was declared lost, so the
237
- * message is forfeit and must never advance the baseline
238
- */
239
-
240
201
  /**
241
202
  * Per-tick orchestrator that wires the action infrastructure to one or more
242
203
  * peers over a Transport.
243
204
  *
244
205
  * Holds a single world (`EntityComponentDataset`) plus the action machinery
245
206
  * (executor, replicator, action log). For each connected peer it owns a
246
- * `Channel` and a `seq` → {@link PendingAck} map so that ack notifications
247
- * advance the `Baseline.last_acked(peer_id)` watermark.
207
+ * {@link Connection} and the action-stream slices sent on it and not yet
208
+ * resolved ({@link PendingSlices}), so that their acknowledgements advance the
209
+ * `Baseline.last_acked(peer_id)` watermark.
210
+ *
211
+ * Every `send_*` queues; nothing reaches a transport until {@link send_queued}
212
+ * (or {@link flush_peer}), which {@link end_tick} calls last. One tick's
213
+ * traffic to a peer thereby leaves together, packed into as few datagrams as
214
+ * fit.
248
215
  *
249
216
  * Use as both server (many connected peers) and client (one peer = the server).
250
217
  * The asymmetry is in *who* connects — the orchestrator itself is symmetric.
@@ -255,9 +222,9 @@ export const ResumeRejectReason = Object.freeze({
255
222
  * - `begin_tick(frame_number)` — open the action log for this frame.
256
223
  * - User code runs game logic, calling `executor.execute(action)` for each
257
224
  * replicated state mutation. Inbound actions from peers also flow into
258
- * the executor automatically (via the channel.onPayload wiring).
225
+ * the executor automatically (via the connection's onMessage wiring).
259
226
  * - `end_tick()` — close the frame and dispatch outgoing packets to every peer.
260
- * 3. `disconnect_peer(peer_id)` — release the channel + baseline state.
227
+ * 3. `disconnect_peer(peer_id)` — release the connection + baseline state.
261
228
  *
262
229
  * @author Alex Goldring
263
230
  * @copyright Company Named Limited (c) 2026
@@ -267,17 +234,35 @@ export class NetworkPeer {
267
234
  #peers;
268
235
  #send_buffer;
269
236
  #recv_buffer;
270
- #fragment_scratch;
237
+ /** @type {function(): number} */
238
+ #clock;
271
239
  /**
272
- * Monotonically-increasing identifier for fragmented messages.
273
- * Wraps at uint16. The receiver's FragmentAssembler keys reassembly
274
- * state on this; collisions would only matter if more than 65536
275
- * messages were in flight simultaneously, which is impossible
276
- * under any realistic transport pressure.
277
- * @type {number}
278
- * @private
240
+ * Options every connection is made with; see the constructor.
241
+ * @type {{send_rate_bytes_per_second: number, max_message_bytes: number, receive_window_bytes: number, max_queued_bytes: number, max_ack_delay_ms: number}}
279
242
  */
280
- #next_fragment_message_id = 0;
243
+ #connection_options;
244
+ /**
245
+ * Frame each network id was last changed by an action this peer executed or applied, -1 for
246
+ * never. What a joining peer's catch-up compares the frame its state was sent at against.
247
+ * @type {Float64Array}
248
+ */
249
+ #last_changed = new Float64Array(256).fill(-1);
250
+ /** frame {@link #stamp_changed} writes */
251
+ #stamp_frame = 0;
252
+ /**
253
+ * Bound once: stamps one changed network id with {@link #stamp_frame}.
254
+ * @type {function(number): void}
255
+ */
256
+ #stamp_changed = (network_id) => {
257
+ if (network_id >= this.#last_changed.length) {
258
+ let size = this.#last_changed.length * 2;
259
+ while (size <= network_id) size *= 2;
260
+ const next = new Float64Array(size).fill(-1);
261
+ next.set(this.#last_changed);
262
+ this.#last_changed = next;
263
+ }
264
+ this.#last_changed[network_id] = this.#stamp_frame;
265
+ };
281
266
  /** @type {boolean} @private */
282
267
  #tick_open = false;
283
268
  #current_frame;
@@ -291,16 +276,6 @@ export class NetworkPeer {
291
276
  */
292
277
  #malformed_packets_dropped = 0;
293
278
 
294
- /**
295
- * Scratch list of the transport seqs one send produced — one entry for a
296
- * single-packet send, one per fragment for a fragmented one. Reused across
297
- * sends so the common path allocates nothing; only valid until the next
298
- * `__send_to_peer` call.
299
- * @type {number[]}
300
- * @private
301
- */
302
- #sent_seqs = [];
303
-
304
279
  /**
305
280
  * Scratch for the slice header of the ACTION_STREAM packet being
306
281
  * dispatched, handed to `Replicator.unpack_from_peer`, which copies what
@@ -310,14 +285,6 @@ export class NetworkPeer {
310
285
  */
311
286
  #slice = { frame_start: 0, frame_end: 0, head: false };
312
287
 
313
- /**
314
- * Scratch for the NACK_FRAGMENT being dispatched: 1 at a chunk index the
315
- * NACK has already had re-sent. Only valid for the duration of that case.
316
- * @type {Uint8Array}
317
- * @private
318
- */
319
- #nack_requested = new Uint8Array(MAX_CHUNKS_PER_MESSAGE);
320
-
321
288
  /**
322
289
  * @param {{
323
290
  * world: EntityComponentDataset,
@@ -331,7 +298,18 @@ export class NetworkPeer {
331
298
  * mutation_ledger_retention_frames?: number,
332
299
  * changed_set_capacity?: number,
333
300
  * max_packets_per_tick?: number,
301
+ * clock: function(): number,
302
+ * tick_period_ms: number,
303
+ * send_rate_bytes_per_second?: number,
304
+ * max_message_bytes?: number,
305
+ * receive_window_bytes?: number,
306
+ * max_queued_bytes?: number,
334
307
  * }} options
308
+ * `clock` returns milliseconds and never runs backwards: every network timer (loss, probe,
309
+ * pacing) runs on it, and nothing in the stack reads a clock of its own. `tick_period_ms` is
310
+ * how often this peer sends: the longest it sits on an acknowledgement, and the resolution
311
+ * of its timers. The remaining four configure every {@link Connection}; see there.
312
+ * `max_message_bytes` and `receive_window_bytes` must be the same on both ends.
335
313
  */
336
314
  constructor({
337
315
  world,
@@ -345,12 +323,30 @@ export class NetworkPeer {
345
323
  mutation_ledger_retention_frames = 600,
346
324
  changed_set_capacity = 256,
347
325
  max_packets_per_tick = 8,
326
+ clock,
327
+ tick_period_ms,
328
+ send_rate_bytes_per_second = 262144,
329
+ max_message_bytes = 1 << 20,
330
+ receive_window_bytes = 256 * 1024,
331
+ max_queued_bytes = 4 << 20,
348
332
  }) {
349
333
  assert.ok(world && typeof world.createEntity === 'function', 'world must be an EntityComponentDataset');
350
334
  assert.ok(binary_registry, 'binary_registry required');
351
335
  assert.isFunction(replicated_components.forEach, 'replicated_components must be iterable');
352
336
  assert.isFunction(action_classes.forEach, 'action_classes must be iterable');
353
337
  assert.isPositiveInteger(max_packets_per_tick, 'max_packets_per_tick');
338
+ assert.isFunction(clock, 'clock');
339
+ assert.isNumber(tick_period_ms, 'tick_period_ms');
340
+ assert.greaterThan(tick_period_ms, 0, 'tick_period_ms');
341
+
342
+ this.#clock = clock;
343
+ this.#connection_options = {
344
+ send_rate_bytes_per_second,
345
+ max_message_bytes,
346
+ receive_window_bytes,
347
+ max_queued_bytes,
348
+ max_ack_delay_ms: tick_period_ms,
349
+ };
354
350
 
355
351
  /** @type {EntityComponentDataset} */
356
352
  this.world = world;
@@ -384,17 +380,9 @@ export class NetworkPeer {
384
380
  * fit one packet; past that the owed range grows until it meets the
385
381
  * ring floor and frames are lost.
386
382
  *
387
- * It is also the bandwidth ceiling under catch-up — this many MTUs per
388
- * tick per peer — and it has to stay inside the channel's ack window.
389
- * A peer's ack header covers its latest 33 seqs, so with a peer that
390
- * replies once a tick, `33 / max_packets_per_tick` consecutive lost
391
- * replies leave a delivered packet outside the window and it is
392
- * declared lost: a wasted re-send rather than a defect, since the
393
- * receiver skips frames it has already applied. Eight tolerates three
394
- * such losses in a row (one tick in sixteen at 50% loss, one in ten
395
- * thousand at 10%), and lets a 60 Hz stream at 100 ms RTT carry a full
396
- * packet of actions per frame. Lower it for peers that reply less often
397
- * than every tick.
383
+ * It is also the bandwidth ceiling under catch-up — this many datagrams
384
+ * of actions per tick per peer. Eight lets a 60 Hz stream at 100 ms RTT
385
+ * carry a full datagram of actions per frame.
398
386
  *
399
387
  * @type {number}
400
388
  */
@@ -424,15 +412,13 @@ export class NetworkPeer {
424
412
  this.mutation_ledger_retention_frames = mutation_ledger_retention_frames;
425
413
 
426
414
  /**
427
- * Per-tick "what mutated this tick" sink, used only when
428
- * {@link mutation_ledger} is set. Cleared at the start of each tick;
429
- * compacted into the ledger at end-of-tick.
430
- * @type {ChangedEntitySet|null}
415
+ * Per-tick "what mutated this tick" sink. Cleared at the start of each tick; at its end,
416
+ * folded into {@link #last_changed} and, when a {@link mutation_ledger} is set, compacted
417
+ * into the ledger.
418
+ * @type {ChangedEntitySet}
431
419
  * @private
432
420
  */
433
- this.#changed_entities = mutation_ledger === null
434
- ? null
435
- : new ChangedEntitySet({ capacity: changed_set_capacity });
421
+ this.#changed_entities = new ChangedEntitySet({ capacity: changed_set_capacity });
436
422
 
437
423
  /** @type {SimActionExecutor} */
438
424
  this.executor = new SimActionExecutor({
@@ -457,8 +443,8 @@ export class NetworkPeer {
457
443
  this.baseline = new Baseline();
458
444
 
459
445
  /**
460
- * peer_id → { channel, transport, seq_to_pending, confirmed, on_payload, on_acked }
461
- * @type {Map<number, { channel: Channel, transport: Object, seq_to_pending: Map<number, PendingAck>, confirmed: Int32Array, on_payload: Function, on_acked: Function }>}
446
+ * peer_id → its connection and action-stream bookkeeping.
447
+ * @type {Map<number, { connection: Connection, transport: Object, pending: PendingSlices, confirmed: Int32Array, on_receive: Function, inbound_count: number, outbound_floor: number, reliable_frames: ReliableFrames }>}
462
448
  * @private
463
449
  */
464
450
  this.#peers = new Map();
@@ -478,14 +464,6 @@ export class NetworkPeer {
478
464
  */
479
465
  this.#recv_buffer = new BinaryBuffer();
480
466
 
481
- /**
482
- * Reusable scratch buffer for outgoing fragment packets. Sized for one
483
- * channel-level packet (MTU minus channel header).
484
- * @type {Uint8Array}
485
- * @private
486
- */
487
- this.#fragment_scratch = new Uint8Array(MAX_CHANNEL_PAYLOAD_BYTES);
488
-
489
467
  /**
490
468
  * Fired when an inbound packet is dropped as malformed. Args:
491
469
  * `(peer_id, error)`. Lets callers log / rate-limit / disconnect an
@@ -495,6 +473,26 @@ export class NetworkPeer {
495
473
  */
496
474
  this.onMalformedPacket = new Signal();
497
475
 
476
+ /**
477
+ * The connection to a peer is no longer whole: `(peer_id, reason)`. Its connection said so
478
+ * (Connection.onProtocolError: a reliable lane broke its limits and stopped, or the peer
479
+ * acknowledged a packet never sent), or more of the peer's action stream arrived during
480
+ * {@link hold_inbound} than may be held. Nothing more can be trusted to arrive whole on it:
481
+ * give the connection up.
482
+ * @type {Signal}
483
+ */
484
+ this.onProtocolError = new Signal();
485
+
486
+ /**
487
+ * An action-stream frame larger than any message the connection may carry
488
+ * (`max_message_bytes`) was given up rather than sent: `(peer_id, frame_start, frame_end,
489
+ * bytes)`. It can never be delivered, so it is stepped over — the peer never applies it —
490
+ * and the stream goes on behind it. Raise `max_message_bytes` on both ends for frames that
491
+ * large.
492
+ * @type {Signal}
493
+ */
494
+ this.onFrameTooLarge = new Signal();
495
+
498
496
  /**
499
497
  * Fired when an inbound packet is a `RECOVERY_REQUEST` (peer is asking
500
498
  * which entities mutated in a tick range so it can request their state).
@@ -528,7 +526,8 @@ export class NetworkPeer {
528
526
 
529
527
  /**
530
528
  * Fires on TIME_DILATION packet arrival.
531
- * Args: `(peer_id, buffer_depth)` — signed int16.
529
+ * Args: `(peer_id, buffer_depth, frame_number)` — signed int16, and the sender's sim frame
530
+ * as it sent it: the freshest word of where the host is, every tick.
532
531
  * @type {Signal}
533
532
  */
534
533
  this.onTimeDilationFeedback = new Signal();
@@ -540,10 +539,11 @@ export class NetworkPeer {
540
539
  this.onFrameMarker = new Signal();
541
540
 
542
541
  /**
543
- * Fires on INITIAL_SYNC packet arrival.
544
- * Args: `(peer_id, session_token, frame_number, buffer, payload_end)`.
545
- * `session_token` is a `Uint8Array(16)` (UUID v1 raw bytes); the
546
- * buffer is positioned at the start of the Snapshotter payload.
542
+ * Fires on each INITIAL_SYNC page.
543
+ * Args: `(peer_id, session_token, frame_number, flags, buffer, payload_end)`.
544
+ * `session_token` is a `Uint8Array(16)` (UUID v1 raw bytes); `flags`
545
+ * is {@link InitialSyncPage}; the buffer is positioned at the page's
546
+ * uint32 entity count.
547
547
  * @type {Signal}
548
548
  */
549
549
  this.onInitialSync = new Signal();
@@ -584,7 +584,7 @@ export class NetworkPeer {
584
584
 
585
585
  /**
586
586
  * Fired when a reliable command arrives from a peer (delivered via
587
- * {@link ReliableCommandPipeline}, dedup'd against retransmits).
587
+ * the connection's USER lane, once and in order).
588
588
  *
589
589
  * Args: `(peer_id, buffer, payload_offset, payload_length)`. The
590
590
  * buffer is the per-peer scratch and may be reused after the handler
@@ -600,7 +600,7 @@ export class NetworkPeer {
600
600
  }
601
601
 
602
602
  /**
603
- * Wire `transport` to the peer identified by `peer_id`. Creates a Channel
603
+ * Wire `transport` to the peer identified by `peer_id`. Creates a Connection
604
604
  * over the transport and connects ack/payload notifications to the local
605
605
  * Baseline / Replicator.
606
606
  *
@@ -616,69 +616,34 @@ export class NetworkPeer {
616
616
  // first rollback replay of its frame, so refuse the connection rather
617
617
  // than lose its input later. Real runtime check — asserts strip in prod.
618
618
  if (peer_id > MAX_PEER_ID) {
619
- throw new Error(`NetworkPeer.connect_peer: peer id ${peer_id} is out of range; must be in [0, ${MAX_PEER_ID}] — 253, 254 and 255 are reserved provenance markers in the action log`);
619
+ throw new Error(`NetworkPeer.connect_peer: peer id ${peer_id} is out of range; must be in [0, ${MAX_PEER_ID}] — 252 to 255 are reserved provenance markers in the action log`);
620
620
  }
621
621
  if (this.#peers.has(peer_id)) {
622
622
  throw new Error(`NetworkPeer.connect_peer: peer ${peer_id} already connected`);
623
623
  }
624
624
 
625
- const channel = new Channel({ transport });
626
- const seq_to_pending = new Map();
625
+ const connection = new Connection({
626
+ transport,
627
+ clock: this.#clock,
628
+ ...this.#connection_options,
629
+ });
630
+ const pending = new PendingSlices();
627
631
  // Frames this peer has confirmed that the baseline has not reached yet,
628
632
  // because a slice before them is still in flight. A ring indexed by
629
633
  // frame modulo capacity holding the frame number — see
630
634
  // __credit_confirmed.
631
635
  const confirmed = new Int32Array(this.action_log.frame_capacity).fill(-1);
632
- // The reassembly window is sized from the burst. A frame too big for
633
- // one packet goes out as a fragmented slice, and a tick may send
634
- // `max_packets_per_tick` of them; jitter that holds a message's later
635
- // fragments behind the next ticks' first fragments leaves that many
636
- // partial messages per tick in flight at once. The assembler evicts
637
- // the oldest partial when its window is full, both fragments of the
638
- // evicted message are still acked, and the sender credits the slice —
639
- // a silent loss. Four ticks of burst keeps the window ahead of any
640
- // jitter the NACK timer would not already have caught.
641
- const fragment_assembler = new FragmentAssembler({
642
- max_pending_messages: Math.max(8, 4 * this.max_packets_per_tick),
643
- });
644
- const fragment_retention = new FragmentRetention();
645
- const reliable_pipeline = new ReliableCommandPipeline({
646
- channel,
647
- packet_type: NetworkPacketType.RELIABLE_COMMAND,
648
- });
649
636
 
650
- // Closure captured once at connect time so the per-tick service
651
- // call doesn't allocate. Builds a NACK_FRAGMENT packet inside
652
- // the orchestrator's shared __send_buffer and ships it over the
653
- // peer's channel. `indices[0..count)` is a reused internal
654
- // scratch from the assembler — only valid for the call.
655
- const on_nack = (message_id, indices, count) => {
656
- this.#send_buffer.position = 0;
657
- this.#send_buffer.writeUint8(NetworkPacketType.NACK_FRAGMENT);
658
- this.#send_buffer.writeUint16LE(message_id);
659
- this.#send_buffer.writeUint8(count);
660
- for (let i = 0; i < count; i++) {
661
- this.#send_buffer.writeUint8(indices[i]);
662
- }
663
- channel.send(this.#send_buffer.raw_bytes, this.#send_buffer.position);
664
- };
665
- // Forward pipeline deliveries to the user-facing signal, attributing
666
- // them to the originating peer.
667
- reliable_pipeline.onCommand.add((buf, payload_offset, payload_length) => {
668
- this.onReliableCommand.send4(peer_id, buf, payload_offset, payload_length);
669
- });
670
-
671
- // Recursive dispatch helper. Used for both incoming transport bytes
672
- // AND for reassembled FRAGMENT payloads (so the same switch handles
673
- // both transparent and fragmented delivery of any packet type).
674
- const dispatch = (bytes, length) => {
637
+ // Every message the connection hands over, unreliable or from a lane, is one of
638
+ // NetworkPacketType: its first byte picks the path.
639
+ const dispatch = (bytes, offset, length) => {
675
640
  if (length < 1) {
676
641
  // malformed: no type byte
677
642
  return;
678
643
  }
679
644
 
680
645
  this.#recv_buffer.position = 0;
681
- this.#recv_buffer.writeBytes(bytes, 0, length);
646
+ this.#recv_buffer.writeBytes(bytes, offset, length);
682
647
  this.#recv_buffer.position = 0;
683
648
 
684
649
  const packet_type = this.#recv_buffer.readUint8();
@@ -718,70 +683,14 @@ export class NetworkPeer {
718
683
  this.onAuthState.send4(peer_id, frame_number, network_id, this.#recv_buffer);
719
684
  break;
720
685
  }
721
- case NetworkPacketType.FRAGMENT: {
722
- // Fragment header layout (after the leading type byte):
723
- // uint16 message_id (LE), uint8 chunk_index, uint8 total_chunks
724
- const message_id = this.#recv_buffer.readUint16LE();
725
- const chunk_index = this.#recv_buffer.readUint8();
726
- const total_chunks = this.#recv_buffer.readUint8();
727
- const chunk_offset = this.#recv_buffer.position;
728
- const chunk_length = length - chunk_offset;
729
- const reassembled = fragment_assembler.receive(
730
- message_id, chunk_index, total_chunks,
731
- bytes, chunk_offset, chunk_length,
732
- );
733
- if (reassembled !== null) {
734
- // Defence-in-depth: don't allow nested fragmentation
735
- // (would indicate a sender bug; ignore silently).
736
- if (reassembled[0] !== NetworkPacketType.FRAGMENT) {
737
- dispatch(reassembled, reassembled.length);
738
- }
739
- }
740
- break;
741
- }
742
- case NetworkPacketType.NACK_FRAGMENT: {
743
- // Receiver is asking for specific chunks of a
744
- // previously-sent fragmented message.
745
- // uint16 message_id (LE), uint8 missing_count,
746
- // uint8 missing_indices[missing_count]
747
- const message_id = this.#recv_buffer.readUint16LE();
748
- const missing_count = this.#recv_buffer.readUint8();
749
- const entry = fragment_retention.consume_nack(message_id);
750
- if (entry === null) {
751
- // Either we never retained this id (already evicted /
752
- // never sent), or the retry budget for it is exhausted.
753
- // Either way: nothing to retransmit. Drop silently.
754
- break;
755
- }
756
- // The list is the peer's word. A NACK re-sends each chunk
757
- // of the message at most once, and nothing for an index
758
- // the message does not have: what one costs is bounded by
759
- // the message we chose to send, not by what is asked. It
760
- // used to re-send a chunk as often as it was named — 255
761
- // times in one NACK, three NACKs for each INITIAL_SYNC.
762
- const total_chunks = fragment_chunk_count(entry.length);
763
- const requested = this.#nack_requested;
764
- requested.fill(0, 0, total_chunks);
765
- for (let i = 0; i < missing_count; i++) {
766
- const chunk_index = this.#recv_buffer.readUint8();
767
- if (chunk_index >= total_chunks || requested[chunk_index] !== 0) continue;
768
- requested[chunk_index] = 1;
769
- resend_fragment_chunk(
770
- entry.payload, entry.length, message_id, chunk_index,
771
- NetworkPacketType.FRAGMENT, this.#fragment_scratch,
772
- (chunk_bytes, chunk_length) => channel.send(chunk_bytes, chunk_length),
773
- );
774
- }
775
- break;
776
- }
777
686
  case NetworkPacketType.RELIABLE_COMMAND:
778
- // Pipeline reads logical_seq, dedups, fires its onCommand
779
- // (which we forward to the user's onReliableCommand above).
780
- reliable_pipeline.handle_inbound(this.#recv_buffer, length);
687
+ // the payload is everything after the type byte
688
+ this.onReliableCommand.send4(peer_id, this.#recv_buffer, 1, length - 1);
781
689
  break;
782
690
  case NetworkPacketType.TIME_DILATION: {
783
691
  const depth = this.#recv_buffer.readInt16();
784
- this.onTimeDilationFeedback.send2(peer_id, depth);
692
+ const frame_number = this.#recv_buffer.readUintVar();
693
+ this.onTimeDilationFeedback.send3(peer_id, depth, frame_number);
785
694
  break;
786
695
  }
787
696
  case NetworkPacketType.INITIAL_SYNC: {
@@ -791,7 +700,8 @@ export class NetworkPeer {
791
700
  const session_token = new Uint8Array(16);
792
701
  this.#recv_buffer.readBytes(session_token, 0, 16);
793
702
  const frame_number = this.#recv_buffer.readUintVar();
794
- this.onInitialSync.send5(peer_id, session_token, frame_number, this.#recv_buffer, length);
703
+ const flags = this.#recv_buffer.readUint8();
704
+ this.onInitialSync.send6(peer_id, session_token, frame_number, flags, this.#recv_buffer, length);
795
705
  break;
796
706
  }
797
707
  case NetworkPacketType.RESUME_HELLO: {
@@ -840,61 +750,45 @@ export class NetworkPeer {
840
750
  }
841
751
  };
842
752
 
843
- // Ack: a send is confirmed once every seq it went out under has been
844
- // acked. For a single-packet send that is the one seq. For a
845
- // fragmented send it is all N fragments, because the receiver applies
846
- // nothing until it has reassembled the whole message; advancing on the
847
- // first fragment's ack would retire a frame range the receiver never
848
- // saw, from the only mechanism that could still re-send it. What a
849
- // confirmed send credits to the baseline is decided in order, by
850
- // __credit_confirmed.
851
- const on_acked = (seq) => {
852
- const pending = seq_to_pending.get(seq);
853
- if (pending === undefined) return;
854
- seq_to_pending.delete(seq);
855
- pending.unacked--;
856
- if (pending.unacked === 0 && !pending.abandoned) {
857
- this.#credit_confirmed(peer_id, confirmed, pending);
858
- }
753
+ // A slice is one unreliable message, so one datagram: its acknowledgement is its delivery.
754
+ // What a delivered slice credits to the baseline is decided in order, by
755
+ // #credit_confirmed. A lost one credits nothing: the frames stay owed and the back-fill
756
+ // packs them again. Every slice is resolved exactly once, acked or lost.
757
+ const on_acked = (token) => {
758
+ this.#credit_confirmed(peer_id, confirmed, pending.frame_start(token), pending.frame_end(token), pending.head(token));
759
+ pending.release(token);
859
760
  };
860
-
861
- // Loss: a packet that aged out of the ack window without being acked
862
- // leaves a dead seq→record entry. The baseline must NOT advance (the
863
- // frame range was never confirmed; the back-fill re-sends it under a
864
- // fresh seq), and the record is marked abandoned so that siblings
865
- // acking afterwards cannot complete it — one lost fragment forfeits the
866
- // whole message. The mapping is reclaimed either way, otherwise
867
- // lost-packet entries accumulate across the session until the seq value
868
- // is reused on wrap.
869
- //
870
- // Every outstanding seq is resolved by Channel as exactly one of acked
871
- // or lost — via the ack bitfield, the 33-seq window age-out, or the
872
- // max_in_flight eviction — so no record can be stranded by a seq that
873
- // never reports.
874
- const on_lost = (seq) => {
875
- const pending = seq_to_pending.get(seq);
876
- if (pending === undefined) return;
877
- seq_to_pending.delete(seq);
878
- pending.unacked--;
879
- pending.abandoned = true;
761
+ const on_lost = (token) => {
762
+ pending.release(token);
880
763
  };
881
764
 
882
765
  // Untrusted-input boundary. A structurally-malformed or truncated
883
- // packet (truncated datagram, spoofed/corrupt bytes, mismatched peer
884
- // build) is dropped and counted — the "discard the bad record, keep the
885
- // connection" posture — rather than throwing out of the transport's
886
- // receive callback and crashing the process. A RangeError is a
887
- // BinaryBuffer read past its backing store, i.e. the same malformed-
888
- // input symptom. Any OTHER error is a genuine bug in a packet handler
889
- // and is rethrown so it is not silently swallowed.
890
- const payload_handler = (bytes, length) => {
891
- // Liveness: any packet that reaches dispatch — including empty-ack,
892
- // and malformed ones dropped below — is evidence the peer/transport
893
- // is alive. Bump the per-peer inbound counter idle-detection polls.
894
- const conn = this.#peers.get(peer_id);
895
- if (conn !== undefined) conn.inbound_count++;
766
+ // message (spoofed/corrupt bytes, mismatched peer build) is dropped and
767
+ // counted — the "discard the bad record, keep the connection" posture —
768
+ // rather than throwing out of the transport's receive callback and
769
+ // crashing the process. A RangeError is a BinaryBuffer read past its
770
+ // backing store, i.e. the same malformed-input symptom. Any OTHER error
771
+ // is a genuine bug in a packet handler and is rethrown so it is not
772
+ // silently swallowed.
773
+ const on_message = (bytes, offset, length) => {
774
+ const held = this.#peers.get(peer_id);
775
+ if (held !== undefined && held.inbound_held && length > 0) {
776
+ const type = bytes[offset];
777
+ if (type === NetworkPacketType.ACTION_STREAM || type === NetworkPacketType.AUTH_STATE) {
778
+ // about a world this peer is still being given: kept for release_inbound
779
+ if (held.inbound_held_bytes + length > this.#connection_options.max_queued_bytes) {
780
+ // Dropped, it would be a frame missing from the stream for good: the
781
+ // connection can no longer give this peer a whole world.
782
+ this.onProtocolError.send2(peer_id, `held action stream past max_queued_bytes ${this.#connection_options.max_queued_bytes} while the world arrives`);
783
+ return;
784
+ }
785
+ held.inbound_queue.push(bytes.slice(offset, offset + length));
786
+ held.inbound_held_bytes += length;
787
+ return;
788
+ }
789
+ }
896
790
  try {
897
- dispatch(bytes, length);
791
+ dispatch(bytes, offset, length);
898
792
  } catch (e) {
899
793
  if (e instanceof MalformedPacketError || e instanceof RangeError) {
900
794
  this.#malformed_packets_dropped++;
@@ -905,23 +799,45 @@ export class NetworkPeer {
905
799
  }
906
800
  };
907
801
 
908
- channel.onPayload.add(payload_handler);
909
- channel.onPacketAcked.add(on_acked);
910
- channel.onPacketLost.add(on_lost);
802
+ connection.onMessage.add(on_message);
803
+ connection.onMessageAcked.add(on_acked);
804
+ connection.onMessageLost.add(on_lost);
805
+ connection.onMalformedDatagram.add((reason) => {
806
+ this.#malformed_packets_dropped++;
807
+ this.onMalformedPacket.send2(peer_id, new MalformedPacketError(`NetworkPeer: datagram dropped: ${reason}`));
808
+ });
809
+ connection.onProtocolError.add((reason) => {
810
+ this.onProtocolError.send2(peer_id, reason);
811
+ });
911
812
 
912
- this.#peers.set(peer_id, {
913
- channel, transport, seq_to_pending, confirmed,
914
- on_payload: payload_handler, on_acked, on_lost, on_nack,
915
- fragment_assembler, fragment_retention,
916
- reliable_pipeline,
813
+ // Liveness: any datagram at all — an ACK alone, a malformed one — is evidence the peer is
814
+ // there. Counted at the transport, ahead of the connection.
815
+ const conn = {
816
+ connection, transport, pending, confirmed,
817
+ on_receive: null,
917
818
  inbound_count: 0,
918
819
  // the first frame the action stream may carry to this peer; see hold_outbound
919
820
  outbound_floor: 0,
920
- });
821
+ // action-stream frames too large for a datagram, in flight on Lane.FRAMES
822
+ reliable_frames: new ReliableFrames(),
823
+ // see hold_inbound
824
+ inbound_held: false,
825
+ /** @type {Uint8Array[]} */
826
+ inbound_queue: [],
827
+ inbound_held_bytes: 0,
828
+ on_message,
829
+ };
830
+ conn.on_receive = () => {
831
+ conn.inbound_count++;
832
+ };
833
+ transport.onReceive.add(conn.on_receive);
834
+
835
+ this.#peers.set(peer_id, conn);
921
836
  }
922
837
 
923
838
  /**
924
- * Unwire and forget a peer.
839
+ * Unwire and forget a peer. Sends nothing: the transport may already be gone. To say a last
840
+ * word first — a DISCONNECT — queue it and call {@link flush_peer}.
925
841
  * @param {number} peer_id
926
842
  */
927
843
  disconnect_peer(peer_id) {
@@ -929,25 +845,11 @@ export class NetworkPeer {
929
845
  const conn = this.#peers.get(peer_id);
930
846
  if (conn === undefined) return;
931
847
 
932
- conn.channel.onPayload.remove(conn.on_payload);
933
- conn.channel.onPacketAcked.remove(conn.on_acked);
934
- conn.channel.onPacketLost.remove(conn.on_lost);
935
- // Drop the channel's subscription on the transport. Required for the
936
- // common case of reusing the same transport on a subsequent reconnect:
937
- // without this, the orphaned channel keeps consuming inbound packets
938
- // and corrupts ack state in parallel with whatever channel replaces it.
939
- conn.channel.dispose();
940
-
941
- // Drop any in-flight fragment reassemblies for this peer — a fresh
942
- // connection on the same peer_id should start clean.
943
- conn.fragment_assembler.clear();
944
- // Same for the sender-side retention buffer: nothing about the
945
- // previous connection's retransmit history should leak across.
946
- conn.fragment_retention.clear();
947
-
948
- // Detach the reliable pipeline (drops its channel subscriptions and
949
- // any unacked / received state).
950
- conn.reliable_pipeline.dispose();
848
+ // Detach from the transport — it may be reused by the next connection on this peer id,
849
+ // and an orphaned connection would keep consuming its packets — and drop everything queued
850
+ // or in flight. Safe from inside the connection's own dispatch: it stops at the next message.
851
+ conn.transport.onReceive.remove(conn.on_receive);
852
+ conn.connection.dispose();
951
853
 
952
854
  this.#peers.delete(peer_id);
953
855
  this.baseline.forget(peer_id);
@@ -989,28 +891,27 @@ export class NetworkPeer {
989
891
  }
990
892
 
991
893
  /**
992
- * Number of outbound action-stream seqs for `peer_id` still awaiting an
894
+ * Number of action-stream slices sent to `peer_id` still awaiting an
993
895
  * ack-or-lost verdict. Returns 0 for an unknown peer.
994
896
  *
995
- * Diagnostics: this is bounded by the channel's in-flight window and must
996
- * fall back to 0 once a stream of acks has resolved everything sent. A
997
- * value that only grows is a leak in the ack accounting.
897
+ * Diagnostics: this must fall back to 0 once a stream of acks has resolved
898
+ * everything sent. A value that only grows is a leak in the ack accounting.
998
899
  * @param {number} peer_id
999
900
  * @returns {number}
1000
901
  */
1001
902
  pending_ack_count(peer_id) {
1002
903
  const conn = this.#peers.get(peer_id);
1003
- return conn === undefined ? 0 : conn.seq_to_pending.size;
904
+ return conn === undefined ? 0 : conn.pending.size;
1004
905
  }
1005
906
 
1006
907
  /**
1007
- * Direct access to a peer's channel (for sending acks-only packets, raw payloads, etc.).
908
+ * A peer's connection, for diagnostics: round-trip time, statistics, what is in flight.
1008
909
  * @param {number} peer_id
1009
- * @returns {Channel|undefined}
910
+ * @returns {Connection|undefined}
1010
911
  */
1011
- channel_for(peer_id) {
912
+ connection_for(peer_id) {
1012
913
  const conn = this.#peers.get(peer_id);
1013
- return conn === undefined ? undefined : conn.channel;
914
+ return conn === undefined ? undefined : conn.connection;
1014
915
  }
1015
916
 
1016
917
  /**
@@ -1049,6 +950,49 @@ export class NetworkPeer {
1049
950
  conn.outbound_floor = from_frame;
1050
951
  }
1051
952
 
953
+ /**
954
+ * Keep the action stream and AUTH_STATE that `peer_id` sends, unapplied, until
955
+ * {@link release_inbound}; everything else it sends is handled as ever.
956
+ *
957
+ * For a client being given a world in pages. The host releases its action stream from the frame
958
+ * after the last page, so the stream's datagrams can overtake the last pages on the way; what
959
+ * they carry is about entities the client may not hold yet. Held, they are applied after the
960
+ * world is complete, in the order they came.
961
+ *
962
+ * Held messages are copies, bounded by `max_queued_bytes`; past that the connection can no
963
+ * longer give the peer a whole stream, and says so on {@link onProtocolError}.
964
+ *
965
+ * @param {number} peer_id
966
+ */
967
+ hold_inbound(peer_id) {
968
+ assert.isNonNegativeInteger(peer_id, 'peer_id');
969
+ const conn = this.#peers.get(peer_id);
970
+ if (conn === undefined) throw new Error(`NetworkPeer.hold_inbound: peer ${peer_id} not connected`);
971
+
972
+ conn.inbound_held = true;
973
+ }
974
+
975
+ /**
976
+ * Stop holding `peer_id`'s action stream and AUTH_STATE, and handle what was held, in the order
977
+ * it arrived.
978
+ *
979
+ * @param {number} peer_id
980
+ */
981
+ release_inbound(peer_id) {
982
+ assert.isNonNegativeInteger(peer_id, 'peer_id');
983
+ const conn = this.#peers.get(peer_id);
984
+ if (conn === undefined) throw new Error(`NetworkPeer.release_inbound: peer ${peer_id} not connected`);
985
+
986
+ conn.inbound_held = false;
987
+ const queue = conn.inbound_queue;
988
+ conn.inbound_queue = [];
989
+ conn.inbound_held_bytes = 0;
990
+ for (let i = 0; i < queue.length; i++) {
991
+ conn.on_message(queue[i], 0, queue[i].length);
992
+ if (!this.#peers.has(peer_id)) return;
993
+ }
994
+ }
995
+
1052
996
  /**
1053
997
  * Open the action log for `frame_number`. Call before running game logic
1054
998
  * for the tick. The user's code (or the engine's `EntityManager.simulate`)
@@ -1070,13 +1014,8 @@ export class NetworkPeer {
1070
1014
  }
1071
1015
 
1072
1016
  /**
1073
- * Close the current tick and send pending action records to every peer.
1074
- * For each peer, packs frames `[Baseline.last_acked(peer) + 1, current_frame]`
1075
- * and sends via the channel; records the seqs the send went out under so
1076
- * that acks for all of them advance the baseline.
1077
- *
1078
- * If a {@link MutationLedger} was supplied at construction, this tick's
1079
- * accumulated changed-entity set is also compacted into the ledger.
1017
+ * Close the current tick, queue the action stream for every peer (see
1018
+ * {@link flush_outbound}), and send everything queued ({@link send_queued}).
1080
1019
  */
1081
1020
  end_tick() {
1082
1021
  if (!this.#tick_open) {
@@ -1086,6 +1025,38 @@ export class NetworkPeer {
1086
1025
  this.#tick_open = false;
1087
1026
 
1088
1027
  this.flush_outbound(this.#current_frame);
1028
+ this.send_queued();
1029
+ }
1030
+
1031
+ /**
1032
+ * Send what is queued for every peer: one flush of each connection, packing the tick's
1033
+ * messages into as few datagrams as fit, and running its loss and probe timers. Also sends a
1034
+ * bare acknowledgement to a peer that has sent something since the last one.
1035
+ */
1036
+ send_queued() {
1037
+ for (const conn of this.#peers.values()) {
1038
+ conn.connection.flush();
1039
+ }
1040
+ }
1041
+
1042
+ /**
1043
+ * Send what is queued for one peer now. For a last word before {@link disconnect_peer}:
1044
+ * queue the DISCONNECT, flush, then disconnect.
1045
+ * @param {number} peer_id
1046
+ */
1047
+ flush_peer(peer_id) {
1048
+ const conn = this.#peers.get(peer_id);
1049
+ if (conn !== undefined) conn.connection.flush();
1050
+ }
1051
+
1052
+ /**
1053
+ * The frame `network_id` was last changed by an action this peer executed or applied, or -1.
1054
+ * Stamped at the end of each tick, by {@link flush_outbound}.
1055
+ * @param {number} network_id
1056
+ * @returns {number}
1057
+ */
1058
+ last_changed_frame(network_id) {
1059
+ return network_id < this.#last_changed.length ? this.#last_changed[network_id] : -1;
1089
1060
  }
1090
1061
 
1091
1062
  /**
@@ -1101,9 +1072,12 @@ export class NetworkPeer {
1101
1072
  * MutationLedger (if configured), then packs frames
1102
1073
  * `[last_acked+1, current_frame]` for each peer via
1103
1074
  * `Replicator.pack_for_peer` into up to {@link max_packets_per_tick}
1104
- * channel packets, each the next slice of that range. Empty packs (no
1105
- * in-scope actions) are not sent. Clears the per-tick changed-entity set
1106
- * after recording.
1075
+ * slices, each the next part of that range and one datagram at most; a
1076
+ * frame too large for a datagram goes on {@link Lane.FRAMES}. Empty packs
1077
+ * (no in-scope actions) are not sent. Stamps and clears the per-tick
1078
+ * changed-entity set.
1079
+ *
1080
+ * Only queues: {@link send_queued} sends.
1107
1081
  *
1108
1082
  * @param {number} current_frame
1109
1083
  */
@@ -1114,7 +1088,10 @@ export class NetworkPeer {
1114
1088
  // (Order doesn't strictly matter for correctness, but keeping the ledger
1115
1089
  // up-to-date as soon as possible reduces edge-case race windows for
1116
1090
  // recovery requests that arrive immediately after.)
1117
- if (this.mutation_ledger !== null && this.#changed_entities !== null) {
1091
+ this.#stamp_frame = current_frame;
1092
+ this.#changed_entities.for_each(this.#stamp_changed);
1093
+
1094
+ if (this.mutation_ledger !== null) {
1118
1095
  this.mutation_ledger.record_tick(current_frame, this.#changed_entities);
1119
1096
 
1120
1097
  // ...and trim. The ledger documents that "caller invokes drop_through periodically",
@@ -1175,7 +1152,7 @@ export class NetworkPeer {
1175
1152
  //
1176
1153
  // Last, the peer's outbound floor: nothing before it is ever sent,
1177
1154
  // and a held peer's floor is past every frame (see hold_outbound).
1178
- const start = Math.max(
1155
+ let start = Math.max(
1179
1156
  last_acked + 1,
1180
1157
  current_frame - this.action_log.frame_capacity + 1,
1181
1158
  0,
@@ -1183,42 +1160,62 @@ export class NetworkPeer {
1183
1160
  );
1184
1161
  if (start > current_frame) continue;
1185
1162
 
1186
- // Up to `max_packets_per_tick` sends, each bounded to one channel
1187
- // packet and each taking the next slice of the owed range.
1163
+ // Frames too large for a datagram went on Lane.FRAMES, which delivers them in order:
1164
+ // credit each once it has arrived. Until then the pack skips the frames one covers.
1165
+ const reliable_frames = conn.reliable_frames;
1166
+ while (reliable_frames.count > 0 && conn.connection.is_delivered(Lane.FRAMES, reliable_frames.front_mark())) {
1167
+ this.#credit_confirmed(peer_id, conn.confirmed,
1168
+ reliable_frames.front_start(), reliable_frames.front_end(), reliable_frames.front_head());
1169
+ reliable_frames.shift();
1170
+ }
1171
+
1172
+ let from = start;
1173
+ let at_pack_start = true;
1174
+
1175
+ // Up to `max_packets_per_tick` slices, each bounded to one datagram
1176
+ // and each taking the next part of the owed range.
1188
1177
  //
1189
- // Bounding a send to one packet is load-bearing. A fragmented
1190
- // action stream is confirmed only if EVERY fragment survives, so a
1178
+ // Bounding a slice to one datagram is load-bearing. A multi-datagram
1179
+ // unreliable message is confirmed only if EVERY datagram survives, so a
1191
1180
  // send that grows with the owed range is self-defeating under
1192
- // loss: one lost fragment pins the baseline, which widens the next
1193
- // pack, which raises the fragment count N, which drops
1181
+ // loss: one lost datagram pins the baseline, which widens the next
1182
+ // pack, which raises the datagram count N, which drops
1194
1183
  // P(all arrive) = (1-p)^N geometrically — the range then widens
1195
1184
  // faster than it drains and delivery collapses. Split into whole
1196
- // packets instead, each slice is confirmed on its own and a loss
1185
+ // datagrams instead, each slice is confirmed on its own and a loss
1197
1186
  // forfeits one slice, not the tick.
1198
1187
  //
1199
1188
  // Sending more than one is what keeps the stream up with the
1200
1189
  // simulation. A slice is re-sent every tick until its ack returns,
1201
1190
  // so the baseline moves by at most a tick's worth of slices per
1202
- // round trip, while the sim produces a frame per tick. One packet
1191
+ // round trip, while the sim produces a frame per tick. One datagram
1203
1192
  // a tick therefore keeps up only while a round trip's frames fit
1204
- // one packet; past that the owed range grows until it meets the
1193
+ // one datagram; past that the owed range grows until it meets the
1205
1194
  // ring floor, a ring of frames is lost crossing it, and everything
1206
1195
  // after arrives a ring late — on a link that loses nothing. The
1207
1196
  // pack is oldest-owed-first, which is what the range order already
1208
1197
  // gives.
1209
1198
  //
1210
- // The budget is what is left of one payload after the prefix and
1211
- // the slice header, read from the buffer rather than written as a
1212
- // constant: `pack_for_peer` measures its budget from the current
1213
- // position, so a hard-coded allowance would have to be edited in
1214
- // step with every byte added here, and getting that wrong puts
1215
- // each full pack one byte over the limit and quietly fragments it
1216
- // again.
1217
- let from = start;
1199
+ // The budget is what is left of one unreliable message after the
1200
+ // prefix and the slice header, read from the buffer rather than
1201
+ // written as a constant: `pack_for_peer` measures its budget from the
1202
+ // current position.
1203
+ const budget = conn.connection.max_unreliable_message_bytes;
1218
1204
  for (let n = 0; n < this.max_packets_per_tick && from <= current_frame; n++) {
1205
+ // Skip what is already on its way reliably. Whatever follows it is not a
1206
+ // head: the receiver holds it until the reliable frame lands.
1207
+ for (let covered = reliable_frames.covering_end(from); covered >= 0; covered = reliable_frames.covering_end(from)) {
1208
+ from = covered + 1;
1209
+ at_pack_start = false;
1210
+ }
1211
+ if (from > current_frame) break;
1212
+
1213
+ // A slice is a head when it begins at the pack start: nothing the
1214
+ // sender can still send lies before it.
1215
+ const head = at_pack_start && from === start;
1219
1216
  this.#send_buffer.position = 0;
1220
1217
  // Packet-type prefix, then the slice header: the range this
1221
- // packet covers and whether it is the tick's first, which is
1218
+ // packet covers and whether it is a head, which is
1222
1219
  // what lets the receiver apply slices in frame order (see
1223
1220
  // Replicator.unpack_from_peer). frame_end is back-patched once
1224
1221
  // the pack has decided how far it got.
@@ -1226,10 +1223,10 @@ export class NetworkPeer {
1226
1223
  this.#send_buffer.writeUint32(from);
1227
1224
  const frame_end_position = this.#send_buffer.position;
1228
1225
  this.#send_buffer.writeUint32(0);
1229
- this.#send_buffer.writeUint8(n === 0 ? 1 : 0);
1226
+ this.#send_buffer.writeUint8(head ? 1 : 0);
1230
1227
  const packed_through = this.replicator.pack_for_peer(
1231
1228
  peer_id, from, current_frame, this.#send_buffer,
1232
- MAX_CHANNEL_PAYLOAD_BYTES - this.#send_buffer.position,
1229
+ budget - this.#send_buffer.position,
1233
1230
  );
1234
1231
  const length = this.#send_buffer.position;
1235
1232
  // Just the prefix and header → nothing in scope left to send
@@ -1239,35 +1236,44 @@ export class NetworkPeer {
1239
1236
  this.#send_buffer.writeUint32(packed_through);
1240
1237
  this.#send_buffer.position = length;
1241
1238
 
1242
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, length, this.#sent_seqs);
1239
+ if (length > budget) {
1240
+ // One frame larger than a datagram: `pack_for_peer` writes a first group
1241
+ // that cannot fit anywhere, since no smaller pack could carry it. It goes
1242
+ // reliably on Lane.FRAMES, once: re-sending a frame of this size every tick
1243
+ // until its ack returns is what the back-fill would do with it. It keeps the
1244
+ // head flag the slice was given, so one that does not start the pack is held
1245
+ // by the receiver until the frames before it land. A lane already holding
1246
+ // all it may waits for room.
1247
+ if (length > conn.connection.max_message_bytes) {
1248
+ // Larger than any message: it can never be delivered. Given up — said so —
1249
+ // once it is the pack start, so the stream goes on behind it instead of
1250
+ // stopping at it until the ring retires it. Never a throw: one peer's
1251
+ // frame must not stop the tick for every other peer.
1252
+ if (!head) break;
1253
+ this.onFrameTooLarge.send4(peer_id, from, packed_through, length);
1254
+ this.#credit_confirmed(peer_id, conn.confirmed, from, packed_through, true);
1255
+ start = packed_through + 1;
1256
+ from = start;
1257
+ continue;
1258
+ }
1259
+ if (conn.connection.unacked_bytes(Lane.FRAMES) + length > this.#connection_options.max_queued_bytes) break;
1260
+ const mark = conn.connection.send_reliable(Lane.FRAMES, this.#send_buffer.raw_bytes, 0, length);
1261
+ reliable_frames.push(from, packed_through, head, mark);
1262
+ from = packed_through + 1;
1263
+ at_pack_start = false;
1264
+ continue;
1265
+ }
1243
1266
 
1244
- // One record shared by every seq the send produced. Frames
1245
- // [from, packed_through] are confirmed only when all of those
1246
- // seqs ack; a loss on any of them abandons the record and
1247
- // leaves the range owed, so a later tick's pack picks it up
1248
- // again.
1249
- //
1250
1267
  // frame_end is what the pack actually held, NOT current_frame:
1251
1268
  // the budget may have stopped it short, and crediting frames
1252
- // that were left out of the packet would retire them from the
1253
- // back-fill unsent — the same defect as crediting a whole
1254
- // message to its first fragment, one layer up.
1269
+ // that were left out of the slice would retire them from the
1270
+ // back-fill unsent.
1255
1271
  //
1256
- // Only the first slice starts at the pack start. The rest start
1257
- // where the previous slice stopped, and their confirmation is
1258
- // held until everything before them is confirmed too — see
1259
- // __credit_confirmed.
1260
- const seq_count = this.#sent_seqs.length;
1261
- const pending = {
1262
- frame_start: from,
1263
- frame_end: packed_through,
1264
- head: n === 0,
1265
- unacked: seq_count,
1266
- abandoned: false,
1267
- };
1268
- for (let i = 0; i < seq_count; i++) {
1269
- conn.seq_to_pending.set(this.#sent_seqs[i], pending);
1270
- }
1272
+ // Only a head starts at the pack start. The rest start where the
1273
+ // previous slice stopped, and their confirmation is held until
1274
+ // everything before them is confirmed too — see #credit_confirmed.
1275
+ const token = conn.pending.acquire(from, packed_through, head);
1276
+ conn.connection.send_unreliable(this.#send_buffer.raw_bytes, 0, length, token);
1271
1277
 
1272
1278
  from = packed_through + 1;
1273
1279
  }
@@ -1275,41 +1281,25 @@ export class NetworkPeer {
1275
1281
 
1276
1282
  // Reset the per-tick changed-entity set so the next tick starts clean.
1277
1283
  // (begin_tick also clears it; the dual call is harmless.)
1278
- if (this.#changed_entities !== null) this.#changed_entities.clear();
1279
-
1280
- // Per-tick fragment maintenance: age out sender-side retention and drive NACK emission for
1281
- // any incomplete inbound messages. Runs AFTER the action-stream send loop so the shared
1282
- // #send_buffer is free for the on_nack closure to reuse.
1283
- //
1284
- // Here and not in `end_tick`: a host never calls `end_tick`. ServerAuthoritativeServer
1285
- // bypasses it and drives `flush_outbound` directly because it manages action_log frames
1286
- // itself across the rollback replay, so with these two calls one level up, NACK recovery and
1287
- // retention age-out were inoperative in the client -> host direction — the direction that
1288
- // carries player input.
1289
- const now_ms = performance.now();
1290
- for (const [, conn] of this.#peers) {
1291
- conn.fragment_retention.service(now_ms);
1292
- conn.fragment_assembler.service(now_ms, conn.on_nack);
1293
- }
1284
+ this.#changed_entities.clear();
1294
1285
  }
1295
1286
 
1296
1287
  /**
1297
- * A send has been confirmed in full — every seq it went out under acked,
1298
- * none lost — so the receiver has applied every frame it carried. Record
1299
- * those frames, then advance the peer's baseline as far as the frames
1300
- * confirmed so far run contiguously.
1288
+ * A slice has been delivered, so the receiver has applied every frame it
1289
+ * carried. Record those frames, then advance the peer's baseline as far as
1290
+ * the frames confirmed so far run contiguously.
1301
1291
  *
1302
1292
  * The order matters because a tick may send several slices and they
1303
1293
  * confirm independently. A later slice landing while an earlier one is
1304
1294
  * still in flight must not credit the earlier one's frames: they are
1305
1295
  * still owed, and crediting them would retire them from the back-fill
1306
1296
  * unsent. So a slice is credited only once the baseline has reached the
1307
- * frame before it. The exception is the head of a tick's burst. Its start
1308
- * IS the pack start — `last_acked + 1`, the ring floor or the peer's
1309
- * outbound floor, whichever is newest — so nothing sendable lies before it
1310
- * and its confirmation credits outright. That is also how the baseline
1311
- * gets past frames the ring has already retired, and frames below the
1312
- * outbound floor, which no ack could ever confirm.
1297
+ * frame before it. The exception is a head. Its start IS the pack start —
1298
+ * `last_acked + 1`, the ring floor or the peer's outbound floor, whichever
1299
+ * is newest — so nothing sendable lies before it and its confirmation
1300
+ * credits outright. That is also how the baseline gets past frames the ring
1301
+ * has already retired, and frames below the outbound floor, which no ack
1302
+ * could ever confirm.
1313
1303
  *
1314
1304
  * `confirmed` is a ring indexed by frame modulo capacity that stores the
1315
1305
  * frame number, so a slot whose frame has since been overwritten reads as
@@ -1321,19 +1311,21 @@ export class NetworkPeer {
1321
1311
  *
1322
1312
  * @param {number} peer_id
1323
1313
  * @param {Int32Array} confirmed the peer's confirmed-frame ring
1324
- * @param {PendingAck} pending the record that just drained
1314
+ * @param {number} frame_start first frame of the delivered slice
1315
+ * @param {number} frame_end last frame of the delivered slice
1316
+ * @param {boolean} head the slice began at the pack start
1325
1317
  * @private
1326
1318
  */
1327
- #credit_confirmed(peer_id, confirmed, pending) {
1319
+ #credit_confirmed(peer_id, confirmed, frame_start, frame_end, head) {
1328
1320
  const capacity = confirmed.length;
1329
- for (let f = pending.frame_start; f <= pending.frame_end; f++) {
1321
+ for (let f = frame_start; f <= frame_end; f++) {
1330
1322
  confirmed[f % capacity] = f;
1331
1323
  }
1332
1324
 
1333
1325
  const before = this.baseline.last_acked(peer_id);
1334
1326
  let last_acked = before;
1335
- if (pending.head && pending.frame_end > last_acked) {
1336
- last_acked = pending.frame_end;
1327
+ if (head && frame_end > last_acked) {
1328
+ last_acked = frame_end;
1337
1329
  }
1338
1330
  while (confirmed[(last_acked + 1) % capacity] === last_acked + 1) {
1339
1331
  last_acked++;
@@ -1343,60 +1335,13 @@ export class NetworkPeer {
1343
1335
  }
1344
1336
  }
1345
1337
 
1346
- /**
1347
- * Send a logical payload to a connected peer, fragmenting transparently
1348
- * if it exceeds the channel's single-packet capacity.
1349
- *
1350
- * Fragments either all arrive (and the receiver reassembles) or the
1351
- * message is lost — there is no partial delivery, and the receiver never
1352
- * applies a message it could not reassemble.
1353
- *
1354
- * A caller that ties delivery state to the ack must therefore wait on
1355
- * EVERY seq the send produced, not just the first. The action stream's
1356
- * back-fill does recover a lost message, but only for as long as the
1357
- * frames stay owed: crediting the range to the first fragment's ack
1358
- * retires it from the back-fill, and a message whose later fragments were
1359
- * dropped is then never re-sent and never applied. Pass `seqs_out` to
1360
- * collect the seqs; {@link NetworkPeer#flush_outbound} does exactly that.
1361
- *
1362
- * @param {object} conn entry from __peers
1363
- * @param {Uint8Array} payload starts with the packet-type byte
1364
- * @param {number} length
1365
- * @param {number[]} [seqs_out] when given, cleared and then filled with the
1366
- * seq of every transport packet emitted, in send order
1367
- * @private
1368
- */
1369
- #send_to_peer(conn, payload, length, seqs_out = null) {
1370
- if (seqs_out !== null) seqs_out.length = 0;
1371
- if (length <= MAX_CHANNEL_PAYLOAD_BYTES) {
1372
- const seq = conn.channel.send(payload, length);
1373
- if (seqs_out !== null) seqs_out.push(seq);
1374
- return;
1375
- }
1376
- const message_id = this.#next_fragment_message_id;
1377
- this.#next_fragment_message_id = seq16_advance(this.#next_fragment_message_id);
1378
- // Retain the source bytes so we can satisfy NACK retransmits.
1379
- // Copy happens inside retain(); `payload` is the orchestrator's
1380
- // shared send buffer and will be overwritten by the next call.
1381
- conn.fragment_retention.retain(message_id, payload, length, performance.now());
1382
- send_fragmented(
1383
- payload, length, message_id,
1384
- NetworkPacketType.FRAGMENT, this.#fragment_scratch,
1385
- (chunk_bytes, chunk_length) => {
1386
- const seq = conn.channel.send(chunk_bytes, chunk_length);
1387
- if (seqs_out !== null) seqs_out.push(seq);
1388
- },
1389
- );
1390
- }
1391
-
1392
1338
  /**
1393
1339
  * Send a recovery request to `peer_id`: "tell me the current state of every
1394
1340
  * entity that mutated between `start_tick` and `end_tick`." The peer's
1395
1341
  * server-side handler ({@link onRecoveryRequest}) is expected to respond by
1396
1342
  * calling {@link send_state_burst_for_range}.
1397
1343
  *
1398
- * Best-effort delivery via the same UDP-style channel — if the request is
1399
- * lost, the client is expected to retry.
1344
+ * Reliable, on the CONTROL lane.
1400
1345
  *
1401
1346
  * @param {number} peer_id
1402
1347
  * @param {number} start_tick
@@ -1413,7 +1358,7 @@ export class NetworkPeer {
1413
1358
  this.#send_buffer.writeUint8(NetworkPacketType.RECOVERY_REQUEST);
1414
1359
  this.#send_buffer.writeUintVar(start_tick);
1415
1360
  this.#send_buffer.writeUintVar(end_tick);
1416
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1361
+ conn.connection.send_reliable(Lane.CONTROL, this.#send_buffer.raw_bytes, 0, this.#send_buffer.position);
1417
1362
  }
1418
1363
 
1419
1364
  /**
@@ -1470,10 +1415,9 @@ export class NetworkPeer {
1470
1415
  });
1471
1416
  },
1472
1417
  });
1473
- // STATE_BURST is the primary path that can exceed MTU — a recovery
1474
- // response covering many entities easily lands in the multi-KB range.
1475
- // The fragmenting wrapper splits it across packets transparently.
1476
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1418
+ // a recovery response covering many entities easily lands in the
1419
+ // multi-KB range: the BULK lane carries it at the paced rate
1420
+ return conn.connection.send_reliable(Lane.BULK, this.#send_buffer.raw_bytes, 0, this.#send_buffer.position);
1477
1421
  }
1478
1422
 
1479
1423
  /**
@@ -1506,34 +1450,37 @@ export class NetworkPeer {
1506
1450
  this.#send_buffer.writeUintVar(frame_number);
1507
1451
  this.#send_buffer.writeUintVar(network_id);
1508
1452
  write_fn(this.#send_buffer);
1509
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1453
+ conn.connection.send_unreliable(this.#send_buffer.raw_bytes, 0, this.#send_buffer.position, -1);
1510
1454
  }
1511
1455
 
1512
1456
  /**
1513
- * Send an INITIAL_SYNC packet. `write_fn` receives the send-buffer
1514
- * positioned just past the `(packet_type, session_token, frame_number)`
1515
- * header and writes the Snapshotter payload. Fragments transparently.
1457
+ * Queue one INITIAL_SYNC page on the BULK lane: the header, then `length` bytes of
1458
+ * `entities` — a uint32 entity count and that many Snapshotter entity records.
1516
1459
  *
1517
1460
  * @param {number} peer_id
1518
1461
  * @param {Uint8Array} session_token 16-byte UUID v1 identifying this peer-session
1519
- * @param {number} frame_number server sim frame this snapshot represents
1520
- * @param {function(BinaryBuffer): void} write_fn
1462
+ * @param {number} frame_number server sim frame the page's state is from
1463
+ * @param {number} flags {@link InitialSyncPage}
1464
+ * @param {Uint8Array} entities
1465
+ * @param {number} length
1466
+ * @returns {number} delivery mark on {@link Lane.BULK}
1521
1467
  */
1522
- send_initial_sync(peer_id, session_token, frame_number, write_fn) {
1468
+ send_sync_page(peer_id, session_token, frame_number, flags, entities, length) {
1523
1469
  assert.isNonNegativeInteger(peer_id, 'peer_id');
1524
1470
  assert.ok(session_token instanceof Uint8Array && session_token.length === 16,
1525
1471
  'session_token must be a 16-byte Uint8Array');
1526
1472
  assert.isNonNegativeInteger(frame_number, 'frame_number');
1527
- assert.isFunction(write_fn, 'write_fn');
1473
+ assert.isNonNegativeInteger(flags, 'flags');
1528
1474
  const conn = this.#peers.get(peer_id);
1529
- if (conn === undefined) throw new Error(`NetworkPeer.send_initial_sync: peer ${peer_id} not connected`);
1475
+ if (conn === undefined) throw new Error(`NetworkPeer.send_sync_page: peer ${peer_id} not connected`);
1530
1476
 
1531
1477
  this.#send_buffer.position = 0;
1532
1478
  this.#send_buffer.writeUint8(NetworkPacketType.INITIAL_SYNC);
1533
1479
  this.#send_buffer.writeBytes(session_token, 0, 16);
1534
1480
  this.#send_buffer.writeUintVar(frame_number);
1535
- write_fn(this.#send_buffer);
1536
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1481
+ this.#send_buffer.writeUint8(flags);
1482
+ this.#send_buffer.writeBytes(entities, 0, length);
1483
+ return conn.connection.send_reliable(Lane.BULK, this.#send_buffer.raw_bytes, 0, this.#send_buffer.position);
1537
1484
  }
1538
1485
 
1539
1486
  /**
@@ -1563,7 +1510,7 @@ export class NetworkPeer {
1563
1510
  this.#send_buffer.writeUintVar(last_acked_frame);
1564
1511
  this.#send_buffer.writeUint8(has_token ? 1 : 0);
1565
1512
  this.#send_buffer.writeBytes(session_token, 0, 16);
1566
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1513
+ conn.connection.send_reliable(Lane.CONTROL, this.#send_buffer.raw_bytes, 0, this.#send_buffer.position);
1567
1514
  }
1568
1515
 
1569
1516
  /**
@@ -1577,7 +1524,7 @@ export class NetworkPeer {
1577
1524
 
1578
1525
  this.#send_buffer.position = 0;
1579
1526
  this.#send_buffer.writeUint8(NetworkPacketType.RESUME_ACCEPT);
1580
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1527
+ conn.connection.send_reliable(Lane.CONTROL, this.#send_buffer.raw_bytes, 0, this.#send_buffer.position);
1581
1528
  }
1582
1529
 
1583
1530
  /**
@@ -1594,7 +1541,7 @@ export class NetworkPeer {
1594
1541
  this.#send_buffer.position = 0;
1595
1542
  this.#send_buffer.writeUint8(NetworkPacketType.RESUME_REJECT);
1596
1543
  this.#send_buffer.writeUint8(reason_code & 0xFF);
1597
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1544
+ conn.connection.send_reliable(Lane.CONTROL, this.#send_buffer.raw_bytes, 0, this.#send_buffer.position);
1598
1545
  }
1599
1546
 
1600
1547
  /**
@@ -1617,7 +1564,7 @@ export class NetworkPeer {
1617
1564
  this.#send_buffer.writeUint8(NetworkPacketType.DISCONNECT);
1618
1565
  this.#send_buffer.writeUint8(len);
1619
1566
  if (len > 0) this.#send_buffer.writeBytes(encoded, 0, len);
1620
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1567
+ conn.connection.send_reliable(Lane.CONTROL, this.#send_buffer.raw_bytes, 0, this.#send_buffer.position);
1621
1568
  }
1622
1569
 
1623
1570
  /**
@@ -1627,10 +1574,12 @@ export class NetworkPeer {
1627
1574
  *
1628
1575
  * @param {number} peer_id
1629
1576
  * @param {number} buffer_depth signed integer; clamped to int16
1577
+ * @param {number} frame_number this side's sim frame
1630
1578
  */
1631
- send_time_dilation_feedback(peer_id, buffer_depth) {
1579
+ send_time_dilation_feedback(peer_id, buffer_depth, frame_number) {
1632
1580
  assert.isNonNegativeInteger(peer_id, 'peer_id');
1633
1581
  assert.isNumber(buffer_depth, 'buffer_depth');
1582
+ assert.isNonNegativeInteger(frame_number, 'frame_number');
1634
1583
  const conn = this.#peers.get(peer_id);
1635
1584
  if (conn === undefined) throw new Error(`NetworkPeer.send_time_dilation_feedback: peer ${peer_id} not connected`);
1636
1585
 
@@ -1641,13 +1590,14 @@ export class NetworkPeer {
1641
1590
  this.#send_buffer.position = 0;
1642
1591
  this.#send_buffer.writeUint8(NetworkPacketType.TIME_DILATION);
1643
1592
  this.#send_buffer.writeInt16(depth);
1644
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1593
+ this.#send_buffer.writeUintVar(frame_number);
1594
+ conn.connection.send_unreliable(this.#send_buffer.raw_bytes, 0, this.#send_buffer.position, -1);
1645
1595
  }
1646
1596
 
1647
1597
  /**
1648
1598
  * Tell a peer which frame this one has just finished simulating. See
1649
1599
  * {@link NetworkPacketType.FRAME_MARKER}. Also serves as the tick's keep-alive: it advances the
1650
- * channel's seq/ack like any packet.
1600
+ * connection's acknowledgements like any message.
1651
1601
  *
1652
1602
  * @param {number} peer_id
1653
1603
  * @param {number} frame_number
@@ -1661,23 +1611,21 @@ export class NetworkPeer {
1661
1611
  this.#send_buffer.position = 0;
1662
1612
  this.#send_buffer.writeUint8(NetworkPacketType.FRAME_MARKER);
1663
1613
  this.#send_buffer.writeUintVar(frame_number);
1664
- this.#send_to_peer(conn, this.#send_buffer.raw_bytes, this.#send_buffer.position);
1614
+ conn.connection.send_unreliable(this.#send_buffer.raw_bytes, 0, this.#send_buffer.position, -1);
1665
1615
  }
1666
1616
 
1667
1617
  /**
1668
- * Send a reliable command to a connected peer. Delivered at-least-once
1669
- * with sender-side retransmit on Channel-detected loss and receiver-
1670
- * side dedup by logical seq. Use for messages where the action-stream
1671
- * model (best-effort, per-frame back-fill) isn't suitable — chat,
1672
- * lobby/room state, level transitions.
1673
- *
1674
- * Returns the logical seq the pipeline assigned (diagnostic only; the
1675
- * receiver dedups internally regardless).
1618
+ * Queue a reliable command for a connected peer, on the connection's USER
1619
+ * lane: it arrives once, whole, and in order with the peer's other
1620
+ * commands, unless the connection ends first. Up to the connection's
1621
+ * `max_message_bytes` (less the type byte). Use for messages where the
1622
+ * action-stream model (best-effort, per-frame back-fill) isn't suitable —
1623
+ * chat, lobby/room state, level transitions.
1676
1624
  *
1677
1625
  * @param {number} peer_id
1678
1626
  * @param {Uint8Array} payload
1679
1627
  * @param {number} length
1680
- * @returns {number} logical seq
1628
+ * @returns {number} delivery mark: see {@link Connection#is_delivered} with {@link Lane.USER}
1681
1629
  */
1682
1630
  send_reliable_command(peer_id, payload, length) {
1683
1631
  assert.isNonNegativeInteger(peer_id, 'peer_id');
@@ -1686,6 +1634,9 @@ export class NetworkPeer {
1686
1634
  if (conn === undefined) {
1687
1635
  throw new Error(`NetworkPeer.send_reliable_command: peer ${peer_id} not connected`);
1688
1636
  }
1689
- return conn.reliable_pipeline.send(payload, length);
1637
+ this.#send_buffer.position = 0;
1638
+ this.#send_buffer.writeUint8(NetworkPacketType.RELIABLE_COMMAND);
1639
+ this.#send_buffer.writeBytes(payload, 0, length);
1640
+ return conn.connection.send_reliable(Lane.USER, this.#send_buffer.raw_bytes, 0, this.#send_buffer.position);
1690
1641
  }
1691
1642
  }