@woosh/meep-engine 3.46.0 → 3.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
  2. package/editor/actions/concrete/TransformModifyAction.js +10 -2
  3. package/editor/ecs/component/FieldDescriptor.js +15 -0
  4. package/editor/ecs/component/createObjectEditor.js +41 -12
  5. package/editor/ecs/component/editors/ecs/Transform64Editor.d.ts.map +1 -1
  6. package/editor/ecs/component/editors/ecs/Transform64Editor.js +77 -70
  7. package/editor/tools/TransformToolV2.d.ts.map +1 -1
  8. package/editor/tools/TransformToolV2.js +4 -0
  9. package/editor/tools/v2/TransformControls.d.ts.map +1 -1
  10. package/editor/tools/v2/TransformControls.js +7 -0
  11. package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
  12. package/editor/view/ecs/EntityEditor.js +4 -2
  13. package/package.json +1 -1
  14. package/src/core/collection/array/array_insert_at.d.ts +16 -0
  15. package/src/core/collection/array/array_insert_at.d.ts.map +1 -0
  16. package/src/core/collection/array/array_insert_at.js +32 -0
  17. package/src/core/collection/array/array_remove_at.d.ts +15 -0
  18. package/src/core/collection/array/array_remove_at.d.ts.map +1 -0
  19. package/src/core/collection/array/array_remove_at.js +30 -0
  20. package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts +37 -0
  21. package/src/core/geom/3d/quaternion/quat3_look_rotation.d.ts.map +1 -0
  22. package/src/core/geom/3d/quaternion/quat3_look_rotation.js +172 -0
  23. package/src/core/geom/Quaternion.d.ts +1 -0
  24. package/src/core/geom/Quaternion.d.ts.map +1 -1
  25. package/src/core/geom/Quaternion.js +12 -48
  26. package/src/engine/Engine.d.ts.map +1 -1
  27. package/src/engine/Engine.js +9 -4
  28. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  29. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +10 -0
  30. package/src/engine/ecs/transform/t64_look_rotation.d.ts +4 -4
  31. package/src/engine/ecs/transform/t64_look_rotation.d.ts.map +1 -1
  32. package/src/engine/ecs/transform/t64_look_rotation.js +7 -18
  33. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  34. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +30 -1
  35. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +6 -0
  36. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  37. package/src/engine/graphics3/GPUParticleEmitterSystem.js +72 -29
  38. package/src/engine/input/devices/KeyboardDevice.d.ts +1 -0
  39. package/src/engine/input/devices/KeyboardDevice.d.ts.map +1 -1
  40. package/src/engine/input/devices/KeyboardDevice.js +7 -6
  41. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
  42. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +15 -2
  43. package/src/engine/network/CONGESTION_CONTROL.md +9 -9
  44. package/src/engine/network/MESSAGE_TRANSPORT.md +364 -0
  45. package/src/engine/network/NetworkSession.d.ts +31 -11
  46. package/src/engine/network/NetworkSession.d.ts.map +1 -1
  47. package/src/engine/network/NetworkSession.js +453 -286
  48. package/src/engine/network/README.md +77 -27
  49. package/src/engine/network/SMOOTHNESS_DESIGN.md +13 -5
  50. package/src/engine/network/diagnostics/BandwidthMeter.d.ts +1 -1
  51. package/src/engine/network/diagnostics/BandwidthMeter.js +1 -1
  52. package/src/engine/network/orchestrator/HostJoin.d.ts +111 -0
  53. package/src/engine/network/orchestrator/HostJoin.d.ts.map +1 -0
  54. package/src/engine/network/orchestrator/HostJoin.js +271 -0
  55. package/src/engine/network/orchestrator/NetworkPeer.d.ts +151 -112
  56. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  57. package/src/engine/network/orchestrator/NetworkPeer.js +450 -499
  58. package/src/engine/network/orchestrator/PendingSlices.d.ts +30 -0
  59. package/src/engine/network/orchestrator/PendingSlices.d.ts.map +1 -0
  60. package/src/engine/network/orchestrator/PendingSlices.js +83 -0
  61. package/src/engine/network/orchestrator/ReliableFrames.d.ts +34 -0
  62. package/src/engine/network/orchestrator/ReliableFrames.d.ts.map +1 -0
  63. package/src/engine/network/orchestrator/ReliableFrames.js +96 -0
  64. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts +77 -10
  65. package/src/engine/network/orchestrator/ServerAuthoritativeClient.d.ts.map +1 -1
  66. package/src/engine/network/orchestrator/ServerAuthoritativeClient.js +236 -61
  67. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts +20 -6
  68. package/src/engine/network/orchestrator/ServerAuthoritativeServer.d.ts.map +1 -1
  69. package/src/engine/network/orchestrator/ServerAuthoritativeServer.js +33 -8
  70. package/src/engine/network/replication/Replicator.d.ts.map +1 -1
  71. package/src/engine/network/replication/Replicator.js +7 -1
  72. package/src/engine/network/sim/ActionLog.d.ts +32 -5
  73. package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
  74. package/src/engine/network/sim/ActionLog.js +47 -5
  75. package/src/engine/network/sim/SimActionExecutor.d.ts +42 -2
  76. package/src/engine/network/sim/SimActionExecutor.d.ts.map +1 -1
  77. package/src/engine/network/sim/SimActionExecutor.js +100 -4
  78. package/src/engine/network/sim/Snapshotter.d.ts +12 -0
  79. package/src/engine/network/sim/Snapshotter.d.ts.map +1 -1
  80. package/src/engine/network/sim/Snapshotter.js +43 -0
  81. package/src/engine/network/state/Baseline.d.ts +1 -1
  82. package/src/engine/network/state/Baseline.js +1 -1
  83. package/src/engine/network/state/FrameBufferRing.d.ts +25 -6
  84. package/src/engine/network/state/FrameBufferRing.d.ts.map +1 -1
  85. package/src/engine/network/state/FrameBufferRing.js +51 -6
  86. package/src/engine/network/state/InputRing.d.ts +4 -0
  87. package/src/engine/network/state/InputRing.d.ts.map +1 -1
  88. package/src/engine/network/state/InputRing.js +7 -0
  89. package/src/engine/network/transport/DeferredTransport.d.ts.map +1 -1
  90. package/src/engine/network/transport/DeferredTransport.js +1 -0
  91. package/src/engine/network/transport/LoopbackTransport.d.ts.map +1 -1
  92. package/src/engine/network/transport/LoopbackTransport.js +4 -2
  93. package/src/engine/network/transport/Transport.d.ts +7 -0
  94. package/src/engine/network/transport/Transport.d.ts.map +1 -1
  95. package/src/engine/network/transport/Transport.js +8 -0
  96. package/src/engine/network/transport/adapters/NodeUDPTransport.d.ts.map +1 -1
  97. package/src/engine/network/transport/adapters/NodeUDPTransport.js +20 -5
  98. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.d.ts.map +1 -1
  99. package/src/engine/network/transport/adapters/WebRTCDataChannelTransport.js +8 -0
  100. package/src/engine/network/transport/adapters/WebTransportTransport.d.ts.map +1 -1
  101. package/src/engine/network/transport/adapters/WebTransportTransport.js +6 -0
  102. package/src/engine/network/transport/connection/Connection.d.ts +191 -0
  103. package/src/engine/network/transport/connection/Connection.d.ts.map +1 -0
  104. package/src/engine/network/transport/connection/Connection.js +1262 -0
  105. package/src/engine/network/transport/connection/RangeSet.d.ts +94 -0
  106. package/src/engine/network/transport/connection/RangeSet.d.ts.map +1 -0
  107. package/src/engine/network/transport/connection/RangeSet.js +260 -0
  108. package/src/engine/network/transport/connection/RxStream.d.ts +48 -0
  109. package/src/engine/network/transport/connection/RxStream.d.ts.map +1 -0
  110. package/src/engine/network/transport/connection/RxStream.js +211 -0
  111. package/src/engine/network/transport/connection/TxStream.d.ts +90 -0
  112. package/src/engine/network/transport/connection/TxStream.d.ts.map +1 -0
  113. package/src/engine/network/transport/connection/TxStream.js +243 -0
  114. package/src/engine/network/transport/connection/bytes_copy.d.ts +17 -0
  115. package/src/engine/network/transport/connection/bytes_copy.d.ts.map +1 -0
  116. package/src/engine/network/transport/connection/bytes_copy.js +34 -0
  117. package/src/engine/network/transport/connection/varint.d.ts +46 -0
  118. package/src/engine/network/transport/connection/varint.d.ts.map +1 -0
  119. package/src/engine/network/transport/connection/varint.js +107 -0
  120. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  121. package/src/engine/sound/ecs/audio/AudioEmitter.d.ts +38 -7
  122. package/src/engine/sound/ecs/audio/AudioEmitter.d.ts.map +1 -1
  123. package/src/engine/sound/ecs/audio/AudioEmitter.js +35 -7
  124. package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.d.ts.map +1 -1
  125. package/src/engine/sound/ecs/audio/AudioEmitterSerializationAdapter.js +5 -1
  126. package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts +18 -0
  127. package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.d.ts.map +1 -0
  128. package/src/engine/sound/ecs/audio/AudioEmitterSerializationUpgrader_0_1.js +48 -0
  129. package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts +12 -2
  130. package/src/engine/sound/ecs/audio/AudioEmitterSystem.d.ts.map +1 -1
  131. package/src/engine/sound/ecs/audio/AudioEmitterSystem.js +173 -9
  132. package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts +9 -3
  133. package/src/engine/sound/ecs/audio/LiveEmitterSet.d.ts.map +1 -1
  134. package/src/engine/sound/ecs/audio/LiveEmitterSet.js +28 -8
  135. package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts +8 -0
  136. package/src/engine/sound/ecs/trigger/AudioEventTrigger.d.ts.map +1 -1
  137. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.d.ts.map +1 -1
  138. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationAdapter.js +5 -1
  139. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts +18 -0
  140. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.d.ts.map +1 -0
  141. package/src/engine/sound/ecs/trigger/AudioEventTriggerSerializationUpgrader_0_1.js +51 -0
  142. package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.d.ts.map +1 -1
  143. package/src/engine/sound/ecs/trigger/AudioEventTriggerSystem.js +15 -1
  144. package/src/engine/sound/sopra/README.md +768 -644
  145. package/src/engine/sound/sopra/SopraEngine.d.ts +104 -8
  146. package/src/engine/sound/sopra/SopraEngine.d.ts.map +1 -1
  147. package/src/engine/sound/sopra/SopraEngine.js +327 -12
  148. package/src/engine/sound/sopra/definition/EventDescription.d.ts +28 -2
  149. package/src/engine/sound/sopra/definition/EventDescription.d.ts.map +1 -1
  150. package/src/engine/sound/sopra/definition/EventDescription.js +55 -5
  151. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts +10 -0
  152. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.d.ts.map +1 -1
  153. package/src/engine/sound/sopra/definition/EventDescriptionSerializationAdapter.js +77 -55
  154. package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts +31 -0
  155. package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.d.ts.map +1 -0
  156. package/src/engine/sound/sopra/definition/EventDescriptionSerializationUpgrader_0_1.js +75 -0
  157. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts +38 -9
  158. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.d.ts.map +1 -1
  159. package/src/engine/sound/sopra/definition/effect/AbstractAudioEffect.js +34 -5
  160. package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts +7 -2
  161. package/src/engine/sound/sopra/definition/effect/CompressorEffect.d.ts.map +1 -1
  162. package/src/engine/sound/sopra/definition/effect/CompressorEffect.js +137 -122
  163. package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts +3 -1
  164. package/src/engine/sound/sopra/definition/effect/EqEffect.d.ts.map +1 -1
  165. package/src/engine/sound/sopra/definition/effect/EqEffect.js +146 -131
  166. package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts +1 -0
  167. package/src/engine/sound/sopra/definition/effect/ReverbEffect.d.ts.map +1 -1
  168. package/src/engine/sound/sopra/definition/effect/ReverbEffect.js +6 -1
  169. package/src/engine/sound/sopra/runtime/BusGraph.d.ts +44 -2
  170. package/src/engine/sound/sopra/runtime/BusGraph.d.ts.map +1 -1
  171. package/src/engine/sound/sopra/runtime/BusGraph.js +83 -43
  172. package/src/engine/sound/sopra/runtime/EffectChain.d.ts +96 -0
  173. package/src/engine/sound/sopra/runtime/EffectChain.d.ts.map +1 -0
  174. package/src/engine/sound/sopra/runtime/EffectChain.js +286 -0
  175. package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts +123 -0
  176. package/src/engine/sound/sopra/runtime/EffectParamOverrides.d.ts.map +1 -0
  177. package/src/engine/sound/sopra/runtime/EffectParamOverrides.js +320 -0
  178. package/src/engine/sound/sopra/runtime/EventInstance.d.ts +55 -5
  179. package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
  180. package/src/engine/sound/sopra/runtime/EventInstance.js +201 -26
  181. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts +2 -1
  182. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.d.ts.map +1 -1
  183. package/src/engine/sound/sopra/serialization/populateSopraSerializationRegistry.js +6 -1
  184. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts +15 -0
  185. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.d.ts.map +1 -1
  186. package/src/engine/sound/sopra/serialization/sopraSerializationHarness.js +32 -0
  187. package/src/engine/sound/sopra/util/MockAudioContext.d.ts.map +1 -1
  188. package/src/engine/sound/sopra/util/MockAudioContext.js +3 -0
  189. package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts +13 -3
  190. package/src/engine/sound/sopra/util/fadeOutAndStop.d.ts.map +1 -1
  191. package/src/engine/sound/sopra/util/fadeOutAndStop.js +25 -13
  192. package/src/shade/RENDERER_CONTRACT.md +6 -3
  193. package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts +24 -0
  194. package/src/shade/device/SHADE_DEVICE_FLOOR.d.ts.map +1 -0
  195. package/src/shade/device/SHADE_DEVICE_FLOOR.js +34 -0
  196. package/src/shade/device/ShadeDeviceFailure.d.ts +12 -5
  197. package/src/shade/device/ShadeDeviceFailure.d.ts.map +1 -1
  198. package/src/shade/device/ShadeDeviceFailure.js +17 -8
  199. package/src/shade/device/ShadeDeviceFailureReason.d.ts +1 -1
  200. package/src/shade/device/ShadeDeviceFailureReason.js +2 -2
  201. package/src/shade/device/shade_device_floor_unmet.d.ts +13 -0
  202. package/src/shade/device/shade_device_floor_unmet.d.ts.map +1 -0
  203. package/src/shade/device/shade_device_floor_unmet.js +32 -0
  204. package/src/shade/renderer/Renderer.d.ts +2 -0
  205. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  206. package/src/shade/renderer/Renderer.js +29 -55
  207. package/src/shade/renderer/buffer/GPUBufferAllocator.d.ts.map +1 -1
  208. package/src/shade/renderer/buffer/GPUBufferAllocator.js +9 -0
  209. package/src/shade/renderer/buffer/buffer_pool_ops.d.ts +21 -12
  210. package/src/shade/renderer/buffer/buffer_pool_ops.d.ts.map +1 -1
  211. package/src/shade/renderer/buffer/buffer_pool_ops.js +18 -19
  212. package/src/shade/renderer/gpu_primitive/test/gpu_test.d.ts.map +1 -1
  213. package/src/shade/renderer/gpu_primitive/test/gpu_test.js +8 -11
  214. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  215. package/src/shade/renderer/pool/TransientResourcePool.d.ts +24 -0
  216. package/src/shade/renderer/pool/TransientResourcePool.d.ts.map +1 -1
  217. package/src/shade/renderer/pool/TransientResourcePool.js +41 -10
  218. package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
  219. package/src/shade/renderer/shader/ComputeShader.js +18 -2
  220. package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
  221. package/src/shade/renderer/shader/ImageShader.js +230 -218
  222. package/src/view/graphics/GraphicsContextFailureView.d.ts +1 -2
  223. package/src/view/graphics/GraphicsContextFailureView.d.ts.map +1 -1
  224. package/src/view/graphics/GraphicsContextFailureView.js +1 -2
  225. package/src/engine/network/transport/Channel.js +0 -336
  226. package/src/engine/network/transport/ReliableCommandPipeline.js +0 -335
  227. package/src/engine/network/transport/fragments/FragmentAssembler.js +0 -350
  228. package/src/engine/network/transport/fragments/FragmentRetention.js +0 -191
  229. package/src/engine/network/transport/fragments/fragment_send.js +0 -157
  230. package/src/engine/network/transport/fragments/packet_size.js +0 -107
@@ -17,6 +17,7 @@ const codeToKeyNameMap = [];
17
17
  * the key remains pressed and the eventual physical release is picked up at the window level.
18
18
  * NOTE: when the window itself loses focus (e.g. switching to another application), key releases can no longer
19
19
  * be observed at all, so any "pressed" keys are forcibly released with the appropriate signal.
20
+ * A key still held when focus returns is pressed again by its next automatic repetition.
20
21
  *
21
22
  * @author Alex Goldring
22
23
  * @copyright Company Named Limited (c) 2026
@@ -90,14 +91,14 @@ class KeyboardDevice {
90
91
  * @private
91
92
  */
92
93
  #handlerKeyDown = (event) => {
93
- if (event.repeat) {
94
- // ignore automatic repetition
95
- // see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat
96
- return;
94
+ // Automatic repetition is not a new press, so the raw signal skips it,
95
+ // see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/repeat
96
+ // It is still proof the key is held: a key released by a window blur, or whose original key-down
97
+ // never reached the element, is pressed again here. Pressing is idempotent, so a key already held is unaffected.
98
+ if (!event.repeat) {
99
+ this.on.down.send1(event);
97
100
  }
98
101
 
99
- this.on.down.send1(event);
100
-
101
102
  let should_prevent_default = false;
102
103
 
103
104
  //hook up dispatch handler for individual keys
@@ -1 +1 @@
1
- {"version":3,"file":"PathFollowingSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/navigation/ecs/path_following/PathFollowingSystem.js"],"names":[],"mappings":";AAiJA;;IACI,sCAA8B;IAC9B,mKAIE;IAEF;;;;OAIG;IACH,qBAAiB;IAEjB;;;;;;OAMG;IACH,6BALW,YAAY,aACZ,WAAW,QACX,IAAI,UACJ,MAAM,QAuBhB;CAgBJ;uBAvMsB,wBAAwB;yBAGtB,mBAAmB;4BAFhB,uCAAuC;4CAFvB,uDAAuD;iBAGlF,uBAAuB"}
1
+ {"version":3,"file":"PathFollowingSystem.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/navigation/ecs/path_following/PathFollowingSystem.js"],"names":[],"mappings":";AAyJA;;IACI,sCAA8B;IAC9B,mKAIE;IAEF;;;;OAIG;IACH,qBAAiB;IAEjB;;;;;;OAMG;IACH,6BALW,YAAY,aACZ,WAAW,QACX,IAAI,UACJ,MAAM,QA4BhB;CAgBJ;uBApNsB,wBAAwB;yBAItB,mBAAmB;4BAFhB,uCAAuC;4CAHvB,uDAAuD;iBAIlF,uBAAuB"}
@@ -4,6 +4,7 @@ import { min2 } from "../../../../core/math/min2.js";
4
4
  import { ResourceAccessKind } from "../../../../core/model/ResourceAccessKind.js";
5
5
  import { ResourceAccessSpecification } from "../../../../core/model/ResourceAccessSpecification.js";
6
6
  import { System } from '../../../ecs/System.js';
7
+ import { t64_announce_change } from "../../../ecs/transform/t64_announce_change.js";
7
8
  import { Transform64 } from '../../../ecs/transform/Transform64.js';
8
9
  import Path from '../components/Path.js';
9
10
  import PathFollower from './PathFollower.js';
@@ -25,11 +26,14 @@ const scratch_quaternion = new Quaternion();
25
26
  * @param {Path} path
26
27
  * @param {Transform64} transform
27
28
  * @param {number} timeDelta
29
+ * @returns {boolean} whether the transform was written, which its writer then has to announce
28
30
  */
29
31
  function performStep(pathFollower, path, transform, timeDelta) {
30
32
  let remainingDistance = pathFollower.speed.getValue() * timeDelta;
31
33
  const path_length = path.length;
32
34
 
35
+ let moved = false;
36
+
33
37
  while (remainingDistance > 0) {
34
38
 
35
39
  let stepLimit = pathFollower.maxMoveDistance;
@@ -138,9 +142,13 @@ function performStep(pathFollower, path, transform, timeDelta) {
138
142
  position[0] = nextPosition[0];
139
143
  position[1] = nextPosition[1];
140
144
  position[2] = nextPosition[2];
145
+
146
+ moved = true;
141
147
  }
142
148
 
143
149
  }
150
+
151
+ return moved;
144
152
  }
145
153
 
146
154
  class PathFollowingSystem extends System {
@@ -177,13 +185,18 @@ class PathFollowingSystem extends System {
177
185
  return;
178
186
  }
179
187
 
180
- performStep(pathFollower, path, transform, this.__time_delta);
188
+ const dataset = this.entityManager.dataset;
189
+
190
+ if (performStep(pathFollower, path, transform, this.__time_delta)) {
191
+ // a Transform64 cannot say it was written, so the writer does: this is what moves the
192
+ // entity's rendered row, its sound, and whatever else follows it by its transform
193
+ t64_announce_change(dataset, entity);
194
+ }
181
195
 
182
196
  if (pathFollower.getFlag(PathFollowerFlags.Finished)) {
183
197
  //deactivate
184
198
  pathFollower.clearFlag(PathFollowerFlags.Active);
185
199
 
186
- const dataset = this.entityManager.dataset
187
200
  dataset.sendEvent(entity, PathFollowerEventType.EndReached);
188
201
  }
189
202
  }
@@ -6,14 +6,14 @@ against Valve GameNetworkingSockets (GNS), 2026-06.
6
6
 
7
7
  ## The gap
8
8
 
9
- There is no congestion control or send pacing anywhere on the meep-owned send
10
- path. `NetworkPeer.flush_outbound` packs `[last_acked+1 .. current]` for every
11
- peer every tick and hands it straight to `Channel.send` / `send_fragmented` with
12
- no byte budget, no rate ceiling, and no loss→rate feedback. `onPacketLost` only
13
- drops the stale `seq→frame_end` mapping; it never reduces a send rate. So under a
14
- constrained link, output *rises* exactly when the path sheds load (the repacked
15
- unacked frames plus reliable/fragment retransmits stack on top) — classic
16
- self-inflicted bufferbloat with no AIMD backoff.
9
+ There is no congestion control on the meep-owned send path. Each peer's `Connection`
10
+ (`MESSAGE_TRANSPORT.md`) paces its reliable USER and BULK lanes with a token bucket at a fixed
11
+ rate — a ceiling, but not one that learns: loss never reduces it. The action stream, AUTH_STATE and
12
+ the CONTROL lane are not paced at all. `NetworkPeer.flush_outbound` packs
13
+ `[last_acked+1 .. current]` for every peer every tick, so under a constrained link output *rises*
14
+ exactly when the path sheds load (the repacked unacked frames plus reliable re-sends stack on top) —
15
+ classic self-inflicted bufferbloat with no AIMD backoff. The token bucket is where a governor would
16
+ set the rate.
17
17
 
18
18
  GNS, by contrast, paces every packet through `SNP_TokenBucket_Accumulate` at a
19
19
  `m_nCurrentSendRateEstimate` that is RFC3390-seeded, shrinks on loss, grows on
@@ -80,7 +80,7 @@ not the obstacle.
80
80
  which bytes go out this tick and defer the rest (`BandwidthMeter` can verify
81
81
  the achieved rate). These primitives already exist and are correct — they are
82
82
  intentionally unwired opt-in surface, **not** dead code.
83
- - Do NOT add a cipher/sliding-window or any of this to `Channel`; congestion
83
+ - Do NOT add a cipher/sliding-window or any of this to `Connection`'s packet layer; congestion
84
84
  control belongs above the seq/ack layer, on the transport-facing send path.
85
85
 
86
86
  @author Alex Goldring
@@ -0,0 +1,364 @@
1
+ # Message transport
2
+
3
+ Status: LANDED, design record. Planned 2026-10-15, revised after two adversarial reviews of the plan (§9),
4
+ implemented, then reviewed adversarially again (§10). Replaced `transport/Channel.js`, `transport/fragments/*`
5
+ and `transport/ReliableCommandPipeline.js` with one connection layer that carries messages of any size, for
6
+ every message type the engine sends and for user messages.
7
+
8
+ ## 1. Why
9
+
10
+ All engine traffic leaves `NetworkPeer` through one function, `#send_to_peer`. A message was one datagram, or
11
+ it was cut into fragments by `fragment_send.js`. That path had five independent ceilings, all measured
12
+ (2026-10-14, probes `zz_probe_join` / `zz_probe_wire`):
13
+
14
+ | Ceiling | Effect |
15
+ |---|---|
16
+ | `FragmentAssembler.max_message_size` 65,536 B | larger messages dropped silently: a world of 2,990 one-component entities could never be joined |
17
+ | uint8 chunk count, 255 × 1,186 B ≈ 302 KB | send throws; for INITIAL_SYNC on every tick forever, for a 20k-entity spawn frame every tick in `closeFixedStep` |
18
+ | `Channel` acks: latest + 32-bit bitfield, only on packets the receiver sends anyway | a message of 34+ fragments sent in one tick is never fully acked; its fragments age out as lost though they arrived (one spawn frame re-sent 32×) |
19
+ | NACK / retention timers on `performance.now()` | repair ignores the simulation clock; a spec ticking faster than wall time gets no repair |
20
+ | no pacing | a 447 KB snapshot leaves in one tick |
21
+
22
+ Steady-state wire cost was also poor: host → client sent `2 + owned entities` datagrams per tick (each
23
+ AUTH_STATE, TIME_DILATION and FRAME_MARKER its own datagram with a 9 B channel header). 16 owned entities: 17
24
+ datagrams, 346 B payload, 822 B on the wire with UDP/IPv4 headers.
25
+
26
+ **Measured after** (same probe, 16 owned entities): host → client 1 datagram, 237 B payload, 265 B on the wire
27
+ per tick (3.1× less); client → host 2 datagrams → 1. A 5,000-entity world joins over Loopback; 20,000 joins
28
+ paced, including while it grows by 1,000 entities a tick (`initial_sync_large_world.spec.js`).
29
+
30
+ ## 2. Requirements
31
+
32
+ 1. Any message type, engine or user, up to a configured size, survives loss and arrives intact.
33
+ 2. Receiver memory per connection is bounded by configuration, never by what a peer claims; so is the
34
+ sender's (queues are capped; a join's pages are built only as the window frees). So is receiver time (§3.9).
35
+ 3. What a peer can make the sender do is bounded by what the sender chose to send (README, "Connecting").
36
+ 4. One datagram per peer per tick in steady state; per-message overhead of a few bytes.
37
+ 5. No per-packet or per-message allocation in steady state (buffers grow to a high-watermark and stay; §4).
38
+ 6. Every timer runs on a clock the caller supplies; the network stack never reads wall time itself.
39
+ 7. Large transfers never delay latency-critical traffic.
40
+ 8. The action stream keeps its contract: confirm a slice only when every packet carrying it is acked, abandon
41
+ it when any is lost (never credit what was not delivered).
42
+
43
+ ## 3. Design
44
+
45
+ One class per peer, `Connection` (`transport/connection/`), used by `NetworkPeer` in place of `Channel` +
46
+ fragments + `ReliableCommandPipeline`. A message is still `[NetworkPacketType byte][payload]`; `NetworkPeer`'s
47
+ dispatch is unchanged except that FRAGMENT and NACK_FRAGMENT are gone, RELIABLE_COMMAND lost its logical seq,
48
+ INITIAL_SYNC is a page, and TIME_DILATION carries the host's frame.
49
+
50
+ ### 3.1 Packet layer (after QUIC, RFC 9000 §12–13, §17.1, §19.3; RFC 9002)
51
+
52
+ ```
53
+ u8 flags bit0 ACK present, bit1 4-byte packet number, bits 4–7 protocol version (1)
54
+ u16|u32 packet number truncated; the full number increases monotonically and is never reused
55
+ [ACK] varint largest acknowledged (full number)
56
+ varint ack delay (8 µs units)
57
+ varint additional range count (≤ 63)
58
+ varint first range
59
+ count × (varint gap, varint range length)
60
+ frames to the end of the datagram
61
+ ```
62
+
63
+ - **Packet numbers are never reused**; a retransmission is new frames in a new packet. Encoded in 2 bytes while
64
+ `number − largest acknowledged < 16,384`, else 4 (RFC 9000 §17.1), expanded by Appendix A.3.
65
+ - **Acks are ranges**: one ACK covers any burst. The receiver tracks at most `max_ack_ranges` (32, at most 64);
66
+ everything below its floor is dropped as a duplicate or too old. The floor rises when an ACK frame of its own
67
+ is acknowledged (§13.2.4) or when the oldest range is pushed out by the cap. A side that sends only ACKs adds
68
+ a PING once per RTT so that it ever hears which ACKs arrived. The ACK takes at most half a datagram; ranges
69
+ that do not fit wait for a later ACK.
70
+ - **Loss** (RFC 9002 §6.1): packet threshold 3, time threshold `9/8 × max(srtt, latest_rtt)`, floored at the
71
+ timer granularity. **Lost means not confirmed**, as in QUIC: a packet that arrived but whose acknowledgement
72
+ never got through — every ACK carrying it lost, or crowded out by newer ranges past `max_ack_ranges` holes —
73
+ is reported lost. The action stream's contract (requirement 8) is exactly this: never credit the unconfirmed.
74
+ - **Probe timeout** (§6.2) doubles per expiry. On expiry the oldest outstanding packet whose reliable bytes are
75
+ still unacknowledged has them queued again (never declared lost); a packet whose bytes were all acknowledged
76
+ by another — the one it re-sent, an earlier probe — is passed over; with none, a PING. A probe may pass an
77
+ empty token bucket once.
78
+ - **Notices are deferred.** `onMessageAcked` / `onMessageLost` are told after the walk that settled them —
79
+ the end of a flush, or of an ACK's processing — never in the middle of one. A handler may send, flush
80
+ (which tells the rest, in order) or dispose (after which nothing more is told).
81
+ - **RTT** from ack delay, clamped to `max_ack_delay`; Karn-free since numbers are never reused.
82
+ - **Timer granularity and `max_ack_delay` are one tick.** All sends and receives happen at fixed-step
83
+ boundaries, so nothing finer is real.
84
+ - **Validation before action.** A datagram is parsed whole before any of it is acted on; a malformed one is
85
+ dropped unacknowledged (`onMalformedDatagram`). An ACK naming a packet never sent is a protocol error (§3.9).
86
+ - **Transports that are already reliable** (`transport.reliable`: WebSocket) never declare loss and never
87
+ probe: on TCP a re-send queues behind the original and makes a stall worse. Loopback is *not* reliable: its
88
+ `drop_next` loses packets, and the specs need the loss path.
89
+ - **Datagram size per transport**: `Transport.max_datagram_bytes`, in [64, 16,383]. 1,200 by default; WebRTC
90
+ 1,150 so a full datagram fits one SCTP packet (§8); WebTransport reads `datagrams.maxDatagramSize` once.
91
+
92
+ ### 3.2 Messages
93
+
94
+ ```
95
+ 0x01 MESSAGE varint length, bytes unreliable, at most one datagram
96
+ 0x02 PING asks for an ACK
97
+ 0x10|lane varint offset, varint length, bytes STREAM, reliable lane 0..3
98
+ ```
99
+
100
+ **Unreliable messages fit one datagram** (1,192 B at the default size). There is no segmented unreliable tier:
101
+ a segmented message evicted from reassembly after its packets were acked would be credited by the action
102
+ stream without being applied — exactly the old fragment-eviction defect. Larger is refused (throws); a caller
103
+ with more goes reliable. Each carries an optional token, and the connection reports `acked(token)` or
104
+ `lost(token)` with its packet, exactly once. When the ACK opening a flush leaves too little room for the
105
+ largest message, the ACK goes alone.
106
+
107
+ **Reliable lanes**, after QUIC streams: each lane is an ordered byte stream of `[varint length][bytes]`.
108
+ - Sender: the message is copied once into the lane's buffer; segments are ranges of it; each packet records the
109
+ ranges it carried; a lost range is queued again minus anything acknowledged since. Bytes are released when
110
+ room is needed, not per ack (a large message's tail is not slid down once per packet).
111
+ - **Flow control without frames**: a sender never sends a byte at or past `acked_prefix + receive_window`. The
112
+ receiver delivers each message the moment its bytes are contiguous, so its contiguous point is never behind
113
+ the sender's acked prefix, and what it holds is at most `max_message_bytes + receive_window`. This holds only
114
+ if both ends use the same `max_message_bytes` and `receive_window_bytes`; a segment past the receiver's bound,
115
+ a message declaring more than its maximum, or more holes than an honest sender makes (§3.9) is a **protocol
116
+ error that ends the connection** (QUIC's FLOW_CONTROL_ERROR), never a silent drop of acknowledged data.
117
+ - **Queue cap**: `max_queued_bytes` per lane; `send_reliable` throws past it.
118
+ - **Delivery mark**: `send_reliable` returns the offset past the message; `is_delivered(lane, mark)` asks
119
+ whether the acked prefix has reached it.
120
+
121
+ Lanes, in scheduling order:
122
+
123
+ | Lane | Carries | Paced |
124
+ |---|---|---|
125
+ | CONTROL | RESUME_HELLO/ACCEPT/REJECT, DISCONNECT, RECOVERY_REQUEST — engine-only, one datagram each | no |
126
+ | (unreliable) | ACTION_STREAM slices, AUTH_STATE, TIME_DILATION, FRAME_MARKER | no |
127
+ | FRAMES | action-stream frames larger than a datagram | no — the stream waits on them |
128
+ | USER | `send_reliable_command`, up to `max_message_bytes` | yes |
129
+ | BULK | INITIAL_SYNC pages, STATE_BURST | yes |
130
+
131
+ ### 3.3 Scheduler
132
+
133
+ Every message for a peer is queued; `flush` sends them packed into as few datagrams as fit, in the order above,
134
+ the ACK in the first datagram. A token bucket per connection at `send_rate_bytes_per_second` (default 256 KiB/s)
135
+ paces USER and BULK; unpaced traffic is counted against it and may take it at most one burst below zero. The
136
+ burst is two ticks of rate (at least two datagrams). Paced lanes always get at least one datagram a flush, so a
137
+ peer that owns many entities cannot starve a join. Congestion control, when built (`CONGESTION_CONTROL.md`),
138
+ sets the rate; transport backpressure (`bufferedAmount`) is a follow-up.
139
+
140
+ **When things go out.** `NetworkPeer.send_*` only queue. `NetworkPeer.send_queued()` flushes every connection.
141
+ `NetworkPeer.end_tick`, `ServerAuthoritativeServer.end_tick` (after `onTickComplete`) and
142
+ `ServerAuthoritativeClient.end_tick` call it last, so one tick's traffic — the action stream, AUTH_STATE,
143
+ TIME_DILATION, join pages; the client's FRAME_MARKER, queued before `end_tick` — leaves in one flush. A send
144
+ outside a tick is flushed where it is made: RESUME_HELLO by `connect()`, a kick's last word by the kick.
145
+ `flush_outbound` only queues.
146
+
147
+ **Closing.** `disconnect_peer` never sends. An explicit close (`drop_peer`, `disconnect`, a kick) calls
148
+ `NetworkPeer.flush_peer(id)` after queueing DISCONNECT and tears down in `finally`: one best-effort datagram.
149
+ A transport that has already died is never flushed. Teardown from inside a connection's own dispatch is safe:
150
+ the receive loop stops at the first message after disposal.
151
+
152
+ ### 3.4 Clock
153
+
154
+ `NetworkPeer` takes a required `clock: () => number` (ms) and `tick_period_ms`. `NetworkSession` passes its own
155
+ `#clock_ms`, the render-paced clock that already drives timeouts, grace and reconnects: one clock, and the fixed
156
+ steps of one update all see the same time, so a burst of catch-up steps cannot age packets whose acks could not
157
+ have arrived.
158
+
159
+ ### 3.5 What moved where
160
+
161
+ | Message | Before | After |
162
+ |---|---|---|
163
+ | ACTION_STREAM slice ≤ datagram | own datagram + notice | unreliable message + notice, coalesced |
164
+ | ACTION_STREAM frame > datagram | fragments, ≤ 64 KiB, 33-window | FRAMES, reliable, once (§3.6) |
165
+ | AUTH_STATE, TIME_DILATION, FRAME_MARKER | own datagram each | unreliable, coalesced; AUTH_STATE > datagram throws |
166
+ | RECOVERY_REQUEST, RESUME_*, DISCONNECT | unreliable datagram | CONTROL |
167
+ | STATE_BURST | fragments | BULK |
168
+ | INITIAL_SYNC | one message ≤ 64 KiB, sent twice | lazily built pages on BULK, catch-up rounds, then FINAL (§3.7) |
169
+ | RELIABLE_COMMAND | `ReliableCommandPipeline`, ≤ 1,178 B | USER, ≤ `max_message_bytes` |
170
+
171
+ ### 3.6 Oversized action-stream frames
172
+
173
+ A frame whose group does not fit one datagram goes on FRAMES, unpaced, once, keeping the head flag it was
174
+ packed with. While it is in flight the pack skips it and sends what follows as further slices, which the
175
+ receiver holds until it lands; it does not stall the stream. It is credited when delivered, like any slice,
176
+ through `#credit_confirmed`. Unpaced because the action stream cannot outlast the 32-frame ring: a frame that
177
+ takes longer to deliver than the ring holds loses the frames after it.
178
+
179
+ A frame larger than `max_message_bytes` can never be delivered. Once it is the pack start it is given up and
180
+ said so (`NetworkPeer.onFrameTooLarge(peer_id, frame_start, frame_end, bytes)`), credited, and the stream goes
181
+ on behind it — never a throw, which would stop the tick for every peer.
182
+
183
+ ### 3.7 Joining a world of any size
184
+
185
+ Host, per joining peer (`orchestrator/HostJoin.js`):
186
+ 1. **Hold** the peer's action stream (`hold_outbound`); skip its AUTH_STATE.
187
+ 2. **First round.** At the first tick, collect the networked entities. If the whole world fits
188
+ `JOIN_FINAL_ROUND_BYTES` (256 KiB) and the lane's room, it goes in that tick and the round is the last:
189
+ every entity is at the same frame. Otherwise pages of whole entities (`JOIN_PAGE_BYTES`, 16 KiB; a bigger
190
+ entity is a page by itself) are built only as BULK's window (128 KiB for a join) frees, each at the current
191
+ frame, noting each entity's frame sent. Host memory per joiner: the window plus 12 bytes per entity (its
192
+ frame sent, and its place in the list).
193
+ 3. **Catch-up rounds.** When every page is delivered, compare each networked entity's last-changed frame
194
+ (`NetworkPeer.last_changed_frame`, stamped for everything its executor executes or records — a
195
+ direct write reported with `net_mutate_component` included; a write nobody reports is not a
196
+ change the join can know of) with the frame its state
197
+ was sent; entities changed since, or created since, go out as a round at the current frame. A catch-up
198
+ round always tries to finish in its tick, with what BULK may still hold (`max_queued_bytes − unacked −
199
+ max_message_bytes`) as its budget: what changed while a world went out is a fraction of it, and a round
200
+ that may only page would never shrink for a world that changes faster than a tick's budget.
201
+ 4. **Release.** A round that finishes in its tick leaves the client with the whole world at that frame D: every
202
+ entity it carries is at D, every entity it leaves out unchanged since its state went out. The FINAL page
203
+ (frame D) goes after it, and the peer's stream is released from D + 1 (still in the ring — the next frame).
204
+ The join is over once FINAL is *delivered*: until then a peer that drops holds part of a world and is synced
205
+ afresh rather than resumed.
206
+ 5. **Bounded.** At most `JOIN_MAX_ROUNDS` (8) rounds; a join that has not converged is dropped
207
+ (`join_did_not_converge`). Pages are only built as the window frees, so a peer that never acknowledges costs
208
+ the window and nothing else.
209
+
210
+ Client:
211
+ - From `connect()` until its binding's answer is whole, the client **holds inbound** action stream and
212
+ AUTH_STATE (`hold_inbound`): a stream that overtakes the pages is about a world the client does not hold yet.
213
+ Bounded by `max_queued_bytes`; past it the connection is broken (§3.9) and the client joins again.
214
+ - The FIRST page issues the token, seeks the frame counter, clears prediction state and forgets the timeline,
215
+ and puts the binding in **Syncing**, which counts as the binding's answer: a later RESUME_ACCEPT/REJECT is
216
+ stale, the reconnect ladder stops.
217
+ - Each page is applied as it lands (creating entities as INITIAL_SYNC did).
218
+ - FINAL seeks again, repeats the clears and `forget_timeline` (prediction ran on a partial world meanwhile),
219
+ sets the world start frame, releases outbound, and makes the binding **Synced**. The held inbound is applied
220
+ once the step's inbound is done (not inside the page's dispatch, which shares the receive buffer), then
221
+ `NetworkSession.onSynced` fires. RESUME_ACCEPT releases the hold the same way.
222
+ - **Seeking.** A page's frame can be old when it lands — a late FINAL — so the client seeks to
223
+ `max(page frame, host frame seen) + 1 + depth`, where the host's frame comes with every TIME_DILATION.
224
+
225
+ ### 3.8 Deleted
226
+
227
+ `Channel.js`, `ReliableCommandPipeline.js`, `fragments/*`, FRAGMENT / NACK_FRAGMENT, `NetworkPeer.channel_for`
228
+ (now `connection_for`), `#pending_initial_sync`'s resend count. Hard break, no shims.
229
+
230
+ ### 3.9 Trust, and a connection that breaks
231
+
232
+ The connection does not authenticate datagrams; its transport delivers only the peer's. WebRTC (DTLS) and
233
+ WebTransport / WebSocket (TLS) do; `NodeUDPTransport` drops datagrams from any source but its remote (by port
234
+ alone when the remote is a host name); Loopback and Simulated are in-process. Before this was stated, one stray
235
+ datagram numbered ahead of the peer's wedged a connection for good: the receiver acknowledged it, and the
236
+ sender dropped every datagram carrying that ACK, silently (measured: 0 of 300 messages through, for a stray
237
+ 1,000 ahead or 2^30 ahead).
238
+
239
+ `Connection.onProtocolError` says the connection is no longer whole:
240
+ - a reliable lane broke its limits (past the window, a message over `max_message_bytes`, a bad length);
241
+ - more than `max(1,024, window / 256)` holes held out of order on a lane: an honest sender's holes are packets
242
+ it lost and has not re-sent, a few hundred at most in a 256 KiB window, while a peer sending one-byte
243
+ segments two bytes apart had the receiver spend 46 ms a datagram at 512k ranges (measured; now refused after
244
+ 6 datagrams, the worst taking 1.5 ms; ranges are added by binary search);
245
+ - the peer acknowledged a packet this side never sent: it took in a datagram that was not ours.
246
+
247
+ `NetworkPeer.onProtocolError` adds the client's inbound hold overflowing (§3.7). `NetworkSession` handles
248
+ every one as a dropped transport and closes the transport so the other end hears of it at once: on the host
249
+ the peer goes into the grace window; on the client the reconnect ladder resumes on a new connection. A stray
250
+ datagram on either side now costs a resume (the specs allow 30 ticks), where it cost the connection.
251
+
252
+ ## 4. Allocation and copies
253
+
254
+ - Outbound: unreliable bytes are copied once into a staging buffer, reliable bytes once into the lane buffer, and
255
+ from either straight into the datagram. The transport's own copy is the last.
256
+ - Inbound: a datagram is parsed in place; an unreliable message is dispatched as a view into it; stream bytes are
257
+ copied once into the lane's receive buffer and dispatched as a view.
258
+ - Per-packet state is struct-of-arrays (packet table, record ring, notice queue) grown to a high-watermark.
259
+ - `NetworkPeer`'s per-slice `PendingAck` objects are a typed-array pool keyed by token (`PendingSlices`).
260
+ - Copies go through `bytes_copy`: a loop up to 64 bytes, `set` over a `subarray` view past that. Measured (Node
261
+ 22): the loop costs ~1.2 ns a byte, the view 50–70 ns flat, so a loop for all sizes made 1 KB copies 20×
262
+ slower. Two connections exchanging ten tokened messages a tick plus ACKs, 200k ticks: with `subarray` on every
263
+ copy, 236 scavenges at 100 B messages; now 13 at 20 B, 212 at 100 B and 226 at 1,000 B, where each copy is one
264
+ short-lived view.
265
+
266
+ ## 5. Phases
267
+
268
+ All landed: **P1** Connection; **P2** NetworkPeer on Connection, old layers deleted; **P3** oversized frames,
269
+ the join, resume/disconnect paths, forest specs; **P4** README "Connecting", the packet table, this record.
270
+
271
+ ## 6. Defaults
272
+
273
+ | Option | Default | Why |
274
+ |---|---|---|
275
+ | `send_rate_bytes_per_second` | 262,144 | GNS's default clamp; a 447 KB join in ~1.8 s |
276
+ | `max_message_bytes` | 1 MiB | largest USER/BULK/FRAMES message; pages are far smaller |
277
+ | `receive_window_bytes` | 256 KiB | ≥ rate × 1 s RTT, so the window never limits the paced rate |
278
+ | `max_queued_bytes` | 4 MiB per lane | a lane this far behind is a peer that is gone |
279
+ | `max_ack_ranges` | 32 (≤ 64) | QUIC implementations keep a few dozen |
280
+ | `JOIN_PAGE_BYTES` | 16 KiB | small enough that a page is one tick of rate |
281
+ | `JOIN_FINAL_ROUND_BYTES` | 256 KiB | a first round this size finishes in one tick |
282
+ | `JOIN_MAX_ROUNDS` | 8 | a join that needs more is a world changing faster than a peer can be sent it |
283
+
284
+ ## 7. Not in scope
285
+
286
+ Congestion control and transport backpressure; encryption, handshakes and anti-spoofing tokens (§3.9 states
287
+ what is assumed of transports); level-baseline identity; the open action-stream issues in
288
+ `netcode-open-defects` (silent watermark skip at `max_packets_per_tick = 1`, silent loss past the ring floor).
289
+
290
+ ## 8. Open
291
+
292
+ Stated, not measured:
293
+ - A client that spawns networked entities locally *during* a join can take a network id a later page carries.
294
+ Clients pre-build pools before connecting (GAP-038); spawning mid-join is not guarded.
295
+ - The WebRTC datagram size (1,150) is from Chrome's source as remembered, not a device measurement.
296
+ - A stray from the remote's own address and port — a previous connection's packet on a reused socket pair — is
297
+ not filtered by `NodeUDPTransport`; it costs a resume (§3.9), and a lower-numbered one can mark a lost packet
298
+ acknowledged. A connection id would close it.
299
+
300
+ From the implementation review, kept open (§10):
301
+ - **I5** An oversized non-head frame behind a frame lost past the ring floor is never applied while the stream
302
+ stays quiet. The same class as the ring-floor loss in §7.
303
+ - **I7** A tokenless reconnect after an incomplete join is refused (NoSessionToken). Tokenless claims are
304
+ refused on purpose: they are what an id-squatter looks like.
305
+
306
+ Measured in passing: 1,500 entities created a tick outrun the action stream itself (the forest spec runs 1,000);
307
+ not a join defect.
308
+
309
+ ## 9. Plan review dispositions
310
+
311
+ Protocol review (R1) and integration review (R2) of the plan, 2026-10-15.
312
+
313
+ | # | Finding | Disposition |
314
+ |---|---|---|
315
+ | R1.1, R2.8 | segmented unreliable message evicted after its packets were acked is credited | **removed the tier**: unreliable ≤ one datagram |
316
+ | R1.2 | 16-bit packet numbers wrap under a silent peer; ring overwrites live entries | 2/4-byte encoding by distance; packet table grows, never wraps; ACK of an unsent number refused (now a protocol error, §3.9) |
317
+ | R1.3, R2.13 | steps × period clock: catch-up steps fake losses; RTT quantised | session `#clock_ms`; granularity and `max_ack_delay` one tick |
318
+ | R1.4 | probe timeout without backoff, behind the bucket | doubles per expiry; one probe passes an empty bucket; never declares loss |
319
+ | R1.5 | frameless flow control assumes matching config | violation is a connection-ending protocol error, documented; no silent drop |
320
+ | R1.6, R2.10 | unbounded queues; unpaced traffic starves BULK; user messages unpaced | per-lane queue cap; bucket floor; paced lanes get ≥ 1 datagram a flush; USER lane paced |
321
+ | R1.7, R2.3 | frozen snapshot = world-sized memory per joiner; ledger missing, retention-bound, empty-burst deadlock | **lazy pages + per-entity change stamps**; no ledger; catch-up as pages; FINAL page even when nothing changed |
322
+ | R1.8 | one code path misbehaves on TCP | reliable transports never declare loss or probe |
323
+ | R1.9 | 1,200 B datagram = two SCTP packets on WebRTC | WebRTC declares 1,150 (§8) |
324
+ | R1.10, R2.2 | oversized frame behind BULK; stall past ring; re-pack re-sends it | FRAMES lane ahead of paced lanes, unpaced; skipped while in flight |
325
+ | R1.11 | ack-only side can never prune ranges | PING once per RTT in ack-only packets; floor rule stated |
326
+ | R1.12 | ACK frames unvalidated | range count ≤ 63, numbers < next sent, delay clamped |
327
+ | R1.13 | no version | 4-bit version in every datagram; anti-spoofing out of scope (§7) |
328
+ | R1.14 | burst of 22 datagrams | burst = two ticks of rate |
329
+ | R1.15 | exactly one outcome per packet | first wins; a late ACK of a lost packet is ignored |
330
+ | R2.1 | paged join is not the binding's answer; ladder restarts it; stale reject acted on | **Syncing** binding state on page 0 |
331
+ | R2.4 | page-by-page apply breaks clear-on-sync | clears and `forget_timeline` repeated at FINAL; host holds AUTH_STATE until release |
332
+ | R2.5 | id collision from client spawns mid-join | stated (§8) |
333
+ | R2.6 | flush placement; spec breaks | single flush at the end of each `end_tick`; FRAME_MARKER queued before it |
334
+ | R2.7 | flushing in `disconnect_peer` sends on dead transports | `disconnect_peer` never sends; explicit `flush_peer` on explicit close only |
335
+ | R2.9 | peer withholds BULK acks to make the host re-serialize forever | at most 8 rounds; pages only as the window frees |
336
+ | R2.11 | resume accepted over a half-applied world | incomplete join → StaleFrame, fresh sync |
337
+ | R2.12 | release checked outside the flush that applies it | release from D + 1, the next frame, by construction |
338
+ | R2.14 | forest spec budget too small for the default rate | spec budget states the rate |
339
+ | R2.15 | README contradictions | P4 |
340
+
341
+ ## 10. Implementation review dispositions
342
+
343
+ Connection review (C) and peer/session integration review (I) of the implementation, 2026-10-15. Each fix was
344
+ pinned by a spec that failed first.
345
+
346
+ | # | Finding | Disposition |
347
+ |---|---|---|
348
+ | C1 | one stray datagram wedges the connection for good | ACK of an unsent packet is a protocol error; session resumes on a new connection; NodeUDP filters by source (§3.9) |
349
+ | C2 | `flush` throws `RangeError` for a full-size unreliable message when an ACK is owed | the ACK goes alone |
350
+ | C3 | a handler flushing from an ACK notice made acked messages report lost (23 of 200) | notices deferred to the end of the walk |
351
+ | C4 | `dispose()` in `onMessageLost` did not stop the walk; later notices still told | deferred notices stop at dispose |
352
+ | C5 | a probe whose bytes were acked elsewhere sent nothing; a tail loss then never recovered (> 1 h) | probes pass over such packets; PING when none has bytes |
353
+ | C6 | one-byte segments two bytes apart: 46 ms a datagram at 512k ranges | hole cap, protocol error; binary-search insert |
354
+ | C7 | delivered messages reported lost past `max_ack_ranges` holes | lost = not confirmed, documented (§3.1); the action stream's contract |
355
+ | C8 | `max_ack_ranges` above 64 made the peer reject every ACK | asserted ≤ 64 |
356
+ | I1 | actions arriving before the FIRST page applied to the old world | inbound held from `connect()` |
357
+ | I2 | a frame over `max_message_bytes` threw every tick, for every peer | given up, `onFrameTooLarge` |
358
+ | I3 | join counted complete when FINAL was queued, not delivered | FINAL_SENT until delivered |
359
+ | I4 | a world changing more than 256 KiB a round could never be joined | catch-up budget is the lane's room |
360
+ | I5 | non-head oversized frame behind a frame lost past the ring floor | open (§8) |
361
+ | I6 | inbound-hold overflow dropped acknowledged frames silently | protocol error; session wiring (§3.9) |
362
+ | I7 | tokenless reconnect after an incomplete join refused | open, by design (§8) |
363
+ | I8 | `release_inbound` ran inside the INITIAL_SYNC dispatch | released after the step's inbound (with I1) |
364
+ | — | `subarray` views were 2.3 of 2.5 KB a tick of the layer's allocation | `bytes_copy` (§4) |
@@ -71,8 +71,17 @@ export const NetworkSessionReconcile: Readonly<{
71
71
  *
72
72
  * Replication is component-driven: attaching a {@link NetworkIdentity}
73
73
  * to an entity registers it with the session via an `EntityObserver`.
74
- * Mutation is event-driven: callers fire
75
- * `dataset.sendEvent(entity, "net_mutate_component", payload)`.
74
+ *
75
+ * **What travels is what goes through the action executor**, and nothing
76
+ * else: actions (`send`, a client's input), and direct writes their writer
77
+ * reports — `dataset.sendEvent(entity, "net_mutate_component", payload)`.
78
+ * The session never looks for changes by itself. That is what keeps a tick's
79
+ * cost in what changed and what is owned, never in the size of the world;
80
+ * the stack is built for worlds of millions of networked objects. A
81
+ * replicated component written directly and not reported stays where it was
82
+ * written. Ownership is set when an identity attaches, or by the join, and
83
+ * `owner_peer_id` / `network_id` are not written afterwards. The README's
84
+ * "What the session assumes" has the whole contract and its measured costs.
76
85
  *
77
86
  * **The session runs on the engine's clock.** The `EntityManager`'s fixed
78
87
  * step is the network tick: each step opens a network frame before any
@@ -155,12 +164,6 @@ export class NetworkSession {
155
164
  * - `reconnect` (client role): policy for the reconnect ladder. See
156
165
  * {@link DEFAULT_RECONNECT_POLICY}. Set `enabled: false` to opt
157
166
  * out entirely.
158
- * - `publish_changes` (host role, default true): at the end of every
159
- * step, send each replicated component the simulation changed — physics,
160
- * controllers, anything that wrote it directly. Components an action
161
- * wrote are already on the wire and are not sent twice. Publishing never
162
- * writes back into the host's own state. Set false to publish by hand
163
- * (`net_mutate_component`).
164
167
  * - `reconcile` (client role): see {@link NetworkSessionReconcile}. Default
165
168
  * `'correct'`.
166
169
  * - `late_input` (host role): what to do with client input that arrives
@@ -168,7 +171,7 @@ export class NetworkSession {
168
171
  * `'apply'`: fold it into the current frame. `'rollback'` rewinds and
169
172
  * replays, which re-executes actions only, never the engine's systems.
170
173
  */
171
- constructor({ entity_manager, transport, transport_factory, role, local_peer_id, simulation_delay_ticks, tick_rate_hz, frame_capacity, binary_registry, scope_filter, time_dilation, adaptive_render_delay, server_resume_grace_ms, connection_timeout_ms, reconnect, publish_changes, late_input, reconcile, }?: {
174
+ constructor({ entity_manager, transport, transport_factory, role, local_peer_id, simulation_delay_ticks, tick_rate_hz, frame_capacity, binary_registry, scope_filter, time_dilation, adaptive_render_delay, server_resume_grace_ms, connection_timeout_ms, reconnect, late_input, reconcile, }?: {
172
175
  entity_manager: EntityManager;
173
176
  transport?: object;
174
177
  transport_factory?: (() => object);
@@ -260,6 +263,13 @@ export class NetworkSession {
260
263
  * @type {Signal}
261
264
  */
262
265
  onReconnected: Signal;
266
+ /**
267
+ * Client: a join has completed — the host's FINAL page has landed, every networked entity's
268
+ * state is here, and from now on this client's input is sent. No arguments. A world is sent
269
+ * as pages over as many ticks as its size takes; until this fires it is still arriving.
270
+ * @type {Signal}
271
+ */
272
+ onSynced: Signal;
263
273
  /**
264
274
  * Client: the reconnect ladder is exhausted, or the host explicitly
265
275
  * kicked us with a DISCONNECT packet, or `reconnect.enabled` is
@@ -400,7 +410,7 @@ export class NetworkSession {
400
410
  /**
401
411
  * Wire the session to the engine. After this, the EntityObserver
402
412
  * is active, the right orchestrator is constructed, and a
403
- * {@link NetworkSystem} is attached. Subsequent `replicate_*`
413
+ * {@link NetworkSystem} is attached until {@link stop}. Subsequent `replicate_*`
404
414
  * / {@link defineAction} / {@link defineInputSampler} calls throw.
405
415
  *
406
416
  * Pre-condition: every replicated component class must already have a
@@ -464,8 +474,18 @@ export class NetworkSession {
464
474
  disconnect(reason?: string): void;
465
475
  /**
466
476
  * Tear down. Idempotent — safe to call from cleanup paths.
477
+ *
478
+ * Takes back from the engine what {@link start} put in it: the {@link NetworkSystem} is out of
479
+ * the engine's loop when this returns, so another session can start on the same engine; this one
480
+ * does not start again. The
481
+ * networked entities stay in the world, and their `network_id`s are released — an id means
482
+ * nothing without the session that gave it. The {@link InterpolationSystem} stays too, whoever
483
+ * added it: it is the engine's, and blends whatever else is interpolated; the session only
484
+ * withdraws its own source from it.
485
+ *
486
+ * @returns {Promise} resolves once the engine has shut the {@link NetworkSystem} down
467
487
  */
468
- stop(): void;
488
+ stop(): Promise<any>;
469
489
  /**
470
490
  * **Deprecated; does nothing.** The session used to run its own fixed-step clock here, next to
471
491
  * the engine's: two clocks, so network frame N and the engine's step N were unrelated, and
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkSession.d.ts","sourceRoot":"","sources":["../../../../src/engine/network/NetworkSession.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;iCA6CoB,MAAM;AAhB1B;;;;;;;;;;;;;;;;;GAiBG;AACH;;;GAGG;;;;sCAKiB,MAAM;AAH1B;;;;GAIG;AACH;IACI;;;;;;;OAOG;;IAEH;;;;OAIG;;GAEJ;AAgLH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH;IAwaI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkEG;IACH,kTAlEW;QACV,cAAkB,gBAAgB;QAClC,SAAa,CAAC,EAAE,MAAM,CAAC;QACvB,iBAAqB,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,CAAC;QACvC,IAAQ,CAAC,EAAE,QAAQ,GAAC,MAAM,CAAC;QAC3B,aAAiB,CAAC,EAAE,MAAM,CAAC;QAC3B,sBAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,YAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,eAAmB,CAAC,EAAE,2BAA2B,CAAC;QAClD,YAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,aAAiB,CAAC,EAAE,YAAY,CAAC;QACjC,qBAAyB,CAAC,EAAE,mBAAmB,CAAC;QAChD,sBAA0B,CAAC,EAAE,MAAM,CAAC;QACpC,qBAAyB,CAAC,EAAE,MAAM,CAAC;QACnC,SAAa,CAAC,EAAE;YAChB,OAAa,CAAC,EAAE,OAAO,CAAC;YACxB,YAAkB,CAAC,EAAE,MAAM,CAAC;YAC5B,aAAmB,CAAC,EAAE,MAAM,CAAC;YAC7B,YAAkB,CAAC,EAAE,MAAM,CAAC;YAC5B,kBAAwB,CAAC,EAAE,MAAM,CAAC;YAClC,gBAAsB,CAAC,EAAE,MAAM,CAAC;YAChC,mBAAyB,CAAC,EAAE,OAAO,CAAC;SAC/B,CAAC;KACH,EAkLH;IAriBD;;;;OAIG;IACH,cAFU,OAAO,CAEI;IAgHrB;;;OAGG;IACH,kBAFU,MAAM,CAEK;IAErB;;;;OAIG;IACH,0BAFU,MAAM,CAEa;IAE7B;;;;;;;;OAQG;IACH,cAFU,MAAM,CAEY;IA6E5B;;;;;;OAMG;IACH,eAFU,IAAI,GAAC,IAAI,CAEE;IAiDrB;;;;OAIG;IACH,YAFU,MAAM,CAEU;IAE1B;;;;;OAKG;IACH,0BAFU,MAAM,CAEwB;IAExC;;;;OAIG;IACH,kBAFU,MAAM,CAEgB;IAEhC;;;;OAIG;IACH,oBAFU,MAAM,CAEkB;IAElC;;;;;;OAMG;IACH,eAFU,MAAM,CAEa;IAE7B;;;;;OAKG;IACH,6BAFU,MAAM,CAE2B;IAkIvC;;;OAGG;IACH,uCAAoC;IAEpC;;;OAGG;IACH,uCAAmC;IAEnC;;;OAGG;IACH,eAFU,kBAAkB,GAAC,MAAM,CAEnB;IAQhB;;;;OAIG;IACH,yBAFU,MAAM,CAE+C;IAE/D;;;OAGG;IACH,iCAFU,MAAM,CAIT;IAEP;;;;;;OAMG;IACH,yBAFU,MAAM,CAEoB;IAEpC;;;OAGG;IACH,wBAFU,MAAM,CAIgC;IAEhD;;;OAGG;IACH,0BAFU,2BAA2B,CAEsC;IAiD/E;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,8DAHW,WAAW,gCAkBrB;IAED;;;;;;;;;;;;;;OAcG;IACH,2EAMC;IAED;;;;;;;;;;;;OAYG;IACH,mDAFW,0BAA0B,GAAC,IAAI,QAsBzC;IA0BD;;;;;;;;OAQG;IACH,6CAIC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,+BAFW,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,SAAS,CAAC,GAAC,IAAI,QAWlD;IAMD;;;;;;;;;OASG;IACH,uBAsSC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,wBAHW,MAAM,aACN,MAAM,QAsFhB;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,mBAHW,MAAM,WACN,MAAM,QA8BhB;IAED;;;;;;OAMG;IACH,oBAFW,MAAM,QAkChB;IAED;;OAEG;IACH,aA0CC;IAMD;;;;;;;;;;;OAWG;IACH,kBAFW,MAAM,QAOhB;IA2OD;;;;;;;;;OASG;IACH,2BAKC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,sBAFW,SAAS,QAYnB;IAMD;;OAEG;IACH,wCAEC;IAED;;OAEG;IACH,wCAEC;IAED;;OAEG;IACH,wBAEC;IAED;;OAEG;IACH,0CAEC;IAED;;OAEG;IACH,iDAEC;IAED;;OAEG;IACH,kCAEC;IAED;;;;;;;;;;OAUG;IACH,kCAEC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,wBAHW,MAAM,GACJ;QAAC,iBAAiB,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAC,CAIlE;IAED;;OAEG;IACH,4BAGC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,qBAFW,MAAM,QAahB;;CA43CJ;mBAn6GkB,oCAAoC;qBAElC,qBAAqB;4CACG,sDAAsD;4BAWvE,iCAAiC;2CADE,gDAAgD;0BAKrF,oBAAoB;0CAPO,6CAA6C;0CADxD,6CAA6C;4BADtC,+BAA+B;iCAO/C,sCAAsC;oCAKnC,+BAA+B;6BAEtC,wBAAwB"}
1
+ {"version":3,"file":"NetworkSession.d.ts","sourceRoot":"","sources":["../../../../src/engine/network/NetworkSession.js"],"names":[],"mappings":"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"}