murow 0.0.60 → 0.0.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (425) hide show
  1. package/README.md +52 -37
  2. package/dist/cjs/core/binary-codec/binary-codec.js +1 -0
  3. package/dist/cjs/core/binary-codec/index.js +1 -0
  4. package/dist/cjs/core/driver/driver.js +1 -0
  5. package/dist/cjs/core/driver/drivers/immediate.js +1 -0
  6. package/dist/cjs/core/driver/drivers/index.js +1 -0
  7. package/dist/cjs/core/driver/drivers/raf.js +1 -0
  8. package/dist/cjs/core/driver/drivers/timeout.js +1 -0
  9. package/dist/cjs/core/driver/index.js +1 -0
  10. package/dist/cjs/core/events/event-system.js +1 -0
  11. package/dist/cjs/core/events/index.js +1 -0
  12. package/dist/cjs/core/fixed-ticker/fixed-ticker.js +1 -0
  13. package/dist/cjs/core/fixed-ticker/index.js +1 -0
  14. package/dist/cjs/core/free-list/free-list.js +1 -0
  15. package/dist/cjs/core/free-list/index.js +1 -0
  16. package/dist/cjs/core/generate-id/generate-id.js +1 -0
  17. package/dist/cjs/core/generate-id/index.js +1 -0
  18. package/dist/cjs/core/index.js +1 -0
  19. package/dist/cjs/core/input/index.js +1 -0
  20. package/dist/cjs/core/input/manager.js +1 -0
  21. package/dist/cjs/core/input/sources/browser.js +1 -0
  22. package/dist/cjs/core/input/sources/index.js +1 -0
  23. package/dist/cjs/core/input/types.js +1 -0
  24. package/dist/cjs/core/lerp/index.js +1 -0
  25. package/dist/cjs/core/lerp/lerp.js +1 -0
  26. package/dist/cjs/core/navmesh/index.js +1 -0
  27. package/dist/cjs/core/navmesh/navmesh-worker-pool.js +1 -0
  28. package/dist/cjs/core/navmesh/navmesh.js +1 -0
  29. package/dist/cjs/core/navmesh/navmesh.worker.js +1 -0
  30. package/dist/cjs/core/pooled-codec/index.js +1 -0
  31. package/dist/cjs/core/pooled-codec/pooled-codec.js +1 -0
  32. package/dist/cjs/core/prediction/index.js +1 -0
  33. package/dist/cjs/core/prediction/prediction.js +1 -0
  34. package/dist/cjs/core/ray/index.js +1 -0
  35. package/dist/cjs/core/ray/ray-2d.js +1 -0
  36. package/dist/cjs/core/ray/ray-3d.js +1 -0
  37. package/dist/cjs/core/simple-rng/index.js +1 -0
  38. package/dist/cjs/core/simple-rng/simple-rng.js +1 -0
  39. package/dist/cjs/core/sparse-batcher/index.js +1 -0
  40. package/dist/cjs/core/sparse-batcher/sparse-batcher.js +1 -0
  41. package/dist/cjs/ecs/component-store.js +1 -0
  42. package/dist/cjs/ecs/component.js +1 -0
  43. package/dist/cjs/ecs/entity-handle.js +1 -0
  44. package/dist/cjs/ecs/index.js +1 -0
  45. package/dist/cjs/ecs/system-builder.js +1 -0
  46. package/dist/cjs/ecs/world-systems.js +1 -0
  47. package/dist/cjs/ecs/world.js +1 -0
  48. package/dist/cjs/game/index.js +1 -0
  49. package/dist/cjs/game/loop/index.js +1 -0
  50. package/dist/cjs/game/loop/loop.js +1 -0
  51. package/dist/cjs/index.js +1 -0
  52. package/dist/cjs/net/adapters/browser-websocket.js +1 -0
  53. package/dist/cjs/net/adapters/bun-websocket.js +1 -0
  54. package/dist/cjs/net/buffer-pool.js +1 -0
  55. package/dist/cjs/net/client.js +1 -0
  56. package/dist/cjs/net/index.js +1 -0
  57. package/dist/cjs/net/server.js +1 -0
  58. package/dist/cjs/net/types.js +1 -0
  59. package/dist/cjs/net/validators.js +1 -0
  60. package/dist/cjs/protocol/index.js +1 -0
  61. package/dist/cjs/protocol/intent/define-intent.js +1 -0
  62. package/dist/cjs/protocol/intent/index.js +1 -0
  63. package/dist/cjs/protocol/intent/intent-registry.js +1 -0
  64. package/dist/cjs/protocol/intent/intent.js +1 -0
  65. package/dist/cjs/protocol/rpc/define-rpc.js +1 -0
  66. package/dist/cjs/protocol/rpc/index.js +1 -0
  67. package/dist/cjs/protocol/rpc/rpc-registry.js +1 -0
  68. package/dist/cjs/protocol/rpc/rpc.js +1 -0
  69. package/dist/cjs/protocol/snapshot/index.js +1 -0
  70. package/dist/cjs/protocol/snapshot/snapshot-codec.js +1 -0
  71. package/dist/cjs/protocol/snapshot/snapshot-registry.js +1 -0
  72. package/dist/cjs/protocol/snapshot/snapshot.js +1 -0
  73. package/dist/cjs/renderer/base-2d-renderer.js +1 -0
  74. package/dist/cjs/renderer/base-3d-renderer.js +1 -0
  75. package/dist/cjs/renderer/base-renderer.js +1 -0
  76. package/dist/cjs/renderer/index.js +1 -0
  77. package/dist/cjs/renderer/types.js +1 -0
  78. package/dist/esm/core/binary-codec/binary-codec.js +1 -0
  79. package/dist/esm/core/binary-codec/index.js +1 -0
  80. package/dist/esm/core/driver/driver.js +1 -0
  81. package/dist/esm/core/driver/drivers/immediate.js +1 -0
  82. package/dist/esm/core/driver/drivers/index.js +1 -0
  83. package/dist/esm/core/driver/drivers/raf.js +1 -0
  84. package/dist/esm/core/driver/drivers/timeout.js +1 -0
  85. package/dist/esm/core/driver/index.js +1 -0
  86. package/dist/esm/core/events/event-system.js +1 -0
  87. package/dist/esm/core/events/index.js +1 -0
  88. package/dist/esm/core/fixed-ticker/fixed-ticker.js +1 -0
  89. package/dist/esm/core/fixed-ticker/index.js +1 -0
  90. package/dist/esm/core/free-list/free-list.js +1 -0
  91. package/dist/esm/core/free-list/index.js +1 -0
  92. package/dist/esm/core/generate-id/generate-id.js +1 -0
  93. package/dist/esm/core/generate-id/index.js +1 -0
  94. package/dist/esm/core/index.js +1 -0
  95. package/dist/esm/core/input/index.js +1 -0
  96. package/dist/esm/core/input/manager.js +1 -0
  97. package/dist/esm/core/input/sources/browser.js +1 -0
  98. package/dist/esm/core/input/sources/index.js +1 -0
  99. package/dist/esm/core/input/types.js +0 -0
  100. package/dist/esm/core/lerp/index.js +1 -0
  101. package/dist/esm/core/lerp/lerp.js +1 -0
  102. package/dist/esm/core/navmesh/index.js +1 -0
  103. package/dist/esm/core/navmesh/navmesh-worker-pool.js +1 -0
  104. package/dist/esm/core/navmesh/navmesh.js +1 -0
  105. package/dist/esm/core/navmesh/navmesh.worker.js +1 -0
  106. package/dist/esm/core/pooled-codec/index.js +1 -0
  107. package/dist/esm/core/pooled-codec/pooled-codec.js +1 -0
  108. package/dist/esm/core/prediction/index.js +1 -0
  109. package/dist/esm/core/prediction/prediction.js +1 -0
  110. package/dist/esm/core/ray/index.js +1 -0
  111. package/dist/esm/core/ray/ray-2d.js +1 -0
  112. package/dist/esm/core/ray/ray-3d.js +1 -0
  113. package/dist/esm/core/simple-rng/index.js +1 -0
  114. package/dist/esm/core/simple-rng/simple-rng.js +1 -0
  115. package/dist/esm/core/sparse-batcher/index.js +1 -0
  116. package/dist/esm/core/sparse-batcher/sparse-batcher.js +1 -0
  117. package/dist/esm/ecs/component-store.js +1 -0
  118. package/dist/esm/ecs/component.js +1 -0
  119. package/dist/esm/ecs/entity-handle.js +1 -0
  120. package/dist/esm/ecs/index.js +1 -0
  121. package/dist/esm/ecs/system-builder.js +1 -0
  122. package/dist/esm/ecs/world-systems.js +1 -0
  123. package/dist/esm/ecs/world.js +1 -0
  124. package/dist/esm/game/index.js +1 -0
  125. package/dist/esm/game/loop/index.js +1 -0
  126. package/dist/esm/game/loop/loop.js +1 -0
  127. package/dist/esm/index.js +1 -0
  128. package/dist/esm/net/adapters/browser-websocket.js +1 -0
  129. package/dist/esm/net/adapters/bun-websocket.js +1 -0
  130. package/dist/esm/net/buffer-pool.js +1 -0
  131. package/dist/esm/net/client.js +1 -0
  132. package/dist/esm/net/index.js +1 -0
  133. package/dist/esm/net/server.js +1 -0
  134. package/dist/esm/net/types.js +1 -0
  135. package/dist/esm/net/validators.js +1 -0
  136. package/dist/esm/protocol/index.js +1 -0
  137. package/dist/esm/protocol/intent/define-intent.js +1 -0
  138. package/dist/esm/protocol/intent/index.js +1 -0
  139. package/dist/esm/protocol/intent/intent-registry.js +1 -0
  140. package/dist/esm/protocol/intent/intent.js +0 -0
  141. package/dist/esm/protocol/rpc/define-rpc.js +1 -0
  142. package/dist/esm/protocol/rpc/index.js +1 -0
  143. package/dist/esm/protocol/rpc/rpc-registry.js +1 -0
  144. package/dist/esm/protocol/rpc/rpc.js +0 -0
  145. package/dist/esm/protocol/snapshot/index.js +1 -0
  146. package/dist/esm/protocol/snapshot/snapshot-codec.js +1 -0
  147. package/dist/esm/protocol/snapshot/snapshot-registry.js +1 -0
  148. package/dist/esm/protocol/snapshot/snapshot.js +1 -0
  149. package/dist/esm/renderer/base-2d-renderer.js +1 -0
  150. package/dist/esm/renderer/base-3d-renderer.js +1 -0
  151. package/dist/esm/renderer/base-renderer.js +1 -0
  152. package/dist/esm/renderer/index.js +1 -0
  153. package/dist/esm/renderer/types.js +0 -0
  154. package/dist/{core → types/core}/binary-codec/binary-codec.d.ts +4 -0
  155. package/dist/{core/loop → types/core/driver}/drivers/immediate.d.ts +1 -1
  156. package/dist/{core/loop → types/core/driver}/drivers/raf.d.ts +1 -1
  157. package/dist/{core/loop → types/core/driver}/drivers/timeout.d.ts +1 -1
  158. package/dist/{core/loop → types/core/driver}/index.d.ts +1 -1
  159. package/dist/{core → types/core}/events/event-system.d.ts +14 -33
  160. package/dist/{core → types/core}/fixed-ticker/fixed-ticker.d.ts +1 -1
  161. package/dist/types/core/free-list/free-list.d.ts +31 -0
  162. package/dist/types/core/free-list/index.d.ts +1 -0
  163. package/dist/{core → types/core}/index.d.ts +7 -1
  164. package/dist/types/core/input/index.d.ts +3 -0
  165. package/dist/types/core/input/manager.d.ts +56 -0
  166. package/dist/types/core/input/sources/browser.d.ts +9 -0
  167. package/dist/types/core/input/sources/index.d.ts +1 -0
  168. package/dist/types/core/input/types.d.ts +36 -0
  169. package/dist/{core → types/core}/navmesh/navmesh.d.ts +1 -21
  170. package/dist/types/core/ray/index.d.ts +2 -0
  171. package/dist/types/core/ray/ray-2d.d.ts +37 -0
  172. package/dist/types/core/ray/ray-3d.d.ts +42 -0
  173. package/dist/types/core/simple-rng/index.d.ts +1 -0
  174. package/dist/types/core/simple-rng/simple-rng.d.ts +36 -0
  175. package/dist/types/core/sparse-batcher/index.d.ts +1 -0
  176. package/dist/types/core/sparse-batcher/sparse-batcher.d.ts +55 -0
  177. package/dist/{ecs → types/ecs}/system-builder.d.ts +20 -9
  178. package/dist/{ecs → types/ecs}/world.d.ts +11 -0
  179. package/dist/types/game/index.d.ts +1 -0
  180. package/dist/types/game/loop/index.d.ts +1 -0
  181. package/dist/types/game/loop/loop.d.ts +175 -0
  182. package/dist/{index.d.ts → types/index.d.ts} +2 -0
  183. package/dist/{net → types/net}/index.d.ts +2 -2
  184. package/dist/{net → types/net}/server.d.ts +39 -19
  185. package/dist/{protocol → types/protocol}/intent/define-intent.d.ts +15 -0
  186. package/dist/{protocol → types/protocol}/intent/index.d.ts +1 -1
  187. package/dist/types/renderer/base-2d-renderer.d.ts +13 -0
  188. package/dist/types/renderer/base-3d-renderer.d.ts +10 -0
  189. package/dist/types/renderer/base-renderer.d.ts +21 -0
  190. package/dist/types/renderer/index.d.ts +4 -0
  191. package/dist/types/renderer/types.d.ts +79 -0
  192. package/dist/webgpu/cjs/index.js +6004 -0
  193. package/dist/webgpu/esm/index.js +5972 -0
  194. package/dist/webgpu/types/2d/animation.d.ts +97 -0
  195. package/dist/webgpu/types/2d/renderer.d.ts +55 -0
  196. package/dist/webgpu/types/2d/shader.d.ts +61 -0
  197. package/dist/webgpu/types/2d/sprite-accessor.d.ts +47 -0
  198. package/dist/webgpu/types/2d/sprite-accessor.test.d.ts +1 -0
  199. package/dist/webgpu/types/3d/gltf-skin-parser.d.ts +101 -0
  200. package/dist/webgpu/types/3d/morph-animation.d.ts +69 -0
  201. package/dist/webgpu/types/3d/morph-animation.test.d.ts +1 -0
  202. package/dist/webgpu/types/3d/renderer.d.ts +216 -0
  203. package/dist/webgpu/types/3d/shader.d.ts +136 -0
  204. package/dist/webgpu/types/3d/skeletal-animation-compute/index.d.ts +2 -0
  205. package/dist/webgpu/types/3d/skeletal-animation-compute/kernel.d.ts +8 -0
  206. package/dist/webgpu/types/3d/skeletal-animation-compute/packer.d.ts +32 -0
  207. package/dist/webgpu/types/3d/skeletal-animation.d.ts +90 -0
  208. package/dist/webgpu/types/camera/camera-2d.d.ts +53 -0
  209. package/dist/webgpu/types/camera/camera-2d.test.d.ts +1 -0
  210. package/dist/webgpu/types/camera/camera-3d.d.ts +81 -0
  211. package/dist/webgpu/types/camera/camera-3d.test.d.ts +1 -0
  212. package/dist/webgpu/types/camera/index.d.ts +2 -0
  213. package/dist/webgpu/types/compute/compute-builder.d.ts +123 -0
  214. package/dist/webgpu/types/compute/compute-builder.test.d.ts +1 -0
  215. package/dist/webgpu/types/core/constants.d.ts +59 -0
  216. package/dist/webgpu/types/core/constants.test.d.ts +1 -0
  217. package/dist/webgpu/types/core/index.d.ts +2 -0
  218. package/dist/webgpu/types/core/math.d.ts +37 -0
  219. package/dist/webgpu/types/core/types.d.ts +125 -0
  220. package/dist/webgpu/types/core/types.test.d.ts +1 -0
  221. package/dist/webgpu/types/geometry/built-in.d.ts +58 -0
  222. package/dist/webgpu/types/geometry/built-in.test.d.ts +1 -0
  223. package/dist/webgpu/types/geometry/geometry-builder.d.ts +281 -0
  224. package/dist/webgpu/types/geometry/geometry-builder.test.d.ts +1 -0
  225. package/dist/webgpu/types/geometry/index.d.ts +2 -0
  226. package/dist/webgpu/types/index.d.ts +32 -0
  227. package/dist/webgpu/types/particle/emitter.d.ts +36 -0
  228. package/dist/webgpu/types/shaders/index.d.ts +2 -0
  229. package/dist/webgpu/types/shaders/runtime-transpile.d.ts +18 -0
  230. package/dist/webgpu/types/shaders/sprite-2d.wgsl.d.ts +10 -0
  231. package/dist/webgpu/types/shaders/typegpu.d.ts +9 -0
  232. package/dist/webgpu/types/shaders/utils.d.ts +28 -0
  233. package/dist/webgpu/types/shaders/utils.test.d.ts +1 -0
  234. package/dist/webgpu/types/spritesheet/index.d.ts +1 -0
  235. package/dist/webgpu/types/spritesheet/spritesheet.d.ts +57 -0
  236. package/dist/webgpu/types/spritesheet/spritesheet.test.d.ts +1 -0
  237. package/package.json +96 -26
  238. package/dist/core/binary-codec/binary-codec.js +0 -354
  239. package/dist/core/binary-codec/index.js +0 -1
  240. package/dist/core/events/event-system.js +0 -88
  241. package/dist/core/events/index.js +0 -1
  242. package/dist/core/fixed-ticker/fixed-ticker.js +0 -101
  243. package/dist/core/fixed-ticker/index.js +0 -1
  244. package/dist/core/generate-id/generate-id.js +0 -25
  245. package/dist/core/generate-id/index.js +0 -1
  246. package/dist/core/index.js +0 -9
  247. package/dist/core/lerp/index.js +0 -1
  248. package/dist/core/lerp/lerp.js +0 -42
  249. package/dist/core/loop/drivers/immediate.js +0 -61
  250. package/dist/core/loop/drivers/index.js +0 -3
  251. package/dist/core/loop/drivers/raf.js +0 -62
  252. package/dist/core/loop/drivers/timeout.js +0 -71
  253. package/dist/core/loop/index.js +0 -2
  254. package/dist/core/loop/loop.js +0 -47
  255. package/dist/core/navmesh/index.js +0 -1
  256. package/dist/core/navmesh/navmesh-worker-pool.js +0 -180
  257. package/dist/core/navmesh/navmesh.js +0 -799
  258. package/dist/core/navmesh/navmesh.worker.js +0 -79
  259. package/dist/core/pooled-codec/index.js +0 -1
  260. package/dist/core/pooled-codec/pooled-codec.js +0 -410
  261. package/dist/core/prediction/index.js +0 -1
  262. package/dist/core/prediction/prediction.js +0 -99
  263. package/dist/core.esm.js +0 -1
  264. package/dist/core.js +0 -1
  265. package/dist/ecs/component-store.js +0 -175
  266. package/dist/ecs/component.js +0 -43
  267. package/dist/ecs/entity-handle.js +0 -515
  268. package/dist/ecs/example.js +0 -125
  269. package/dist/ecs/index.js +0 -4
  270. package/dist/ecs/system-builder.js +0 -180
  271. package/dist/ecs/system.d.ts +0 -63
  272. package/dist/ecs/system.js +0 -92
  273. package/dist/ecs/world-systems.js +0 -79
  274. package/dist/ecs/world.js +0 -684
  275. package/dist/index.js +0 -24
  276. package/dist/net/adapters/browser-websocket.js +0 -74
  277. package/dist/net/adapters/bun-websocket.js +0 -245
  278. package/dist/net/buffer-pool.js +0 -89
  279. package/dist/net/client.js +0 -586
  280. package/dist/net/index.js +0 -58
  281. package/dist/net/server.js +0 -938
  282. package/dist/net/types.js +0 -31
  283. package/dist/net/validators.js +0 -88
  284. package/dist/protocol/index.js +0 -92
  285. package/dist/protocol/intent/define-intent.js +0 -125
  286. package/dist/protocol/intent/index.js +0 -91
  287. package/dist/protocol/intent/intent-registry.js +0 -91
  288. package/dist/protocol/rpc/define-rpc.js +0 -84
  289. package/dist/protocol/rpc/index.js +0 -3
  290. package/dist/protocol/rpc/rpc-registry.js +0 -159
  291. package/dist/protocol/rpc/rpc.js +0 -12
  292. package/dist/protocol/snapshot/index.js +0 -43
  293. package/dist/protocol/snapshot/snapshot-codec.js +0 -67
  294. package/dist/protocol/snapshot/snapshot-registry.js +0 -168
  295. package/dist/protocol/snapshot/snapshot.js +0 -30
  296. package/src/core/binary-codec/README.md +0 -60
  297. package/src/core/binary-codec/binary-codec.test.ts +0 -300
  298. package/src/core/binary-codec/binary-codec.ts +0 -448
  299. package/src/core/binary-codec/index.ts +0 -1
  300. package/src/core/events/README.md +0 -47
  301. package/src/core/events/event-system.test.ts +0 -243
  302. package/src/core/events/event-system.ts +0 -140
  303. package/src/core/events/index.ts +0 -1
  304. package/src/core/fixed-ticker/README.md +0 -77
  305. package/src/core/fixed-ticker/fixed-ticker.test.ts +0 -151
  306. package/src/core/fixed-ticker/fixed-ticker.ts +0 -169
  307. package/src/core/fixed-ticker/index.ts +0 -1
  308. package/src/core/generate-id/README.md +0 -18
  309. package/src/core/generate-id/generate-id.test.ts +0 -79
  310. package/src/core/generate-id/generate-id.ts +0 -37
  311. package/src/core/generate-id/index.ts +0 -1
  312. package/src/core/index.ts +0 -9
  313. package/src/core/lerp/README.md +0 -79
  314. package/src/core/lerp/index.ts +0 -1
  315. package/src/core/lerp/lerp.test.ts +0 -90
  316. package/src/core/lerp/lerp.ts +0 -42
  317. package/src/core/loop/README.md +0 -97
  318. package/src/core/loop/drivers/immediate.ts +0 -66
  319. package/src/core/loop/drivers/index.ts +0 -3
  320. package/src/core/loop/drivers/raf.ts +0 -67
  321. package/src/core/loop/drivers/timeout.ts +0 -77
  322. package/src/core/loop/index.ts +0 -2
  323. package/src/core/loop/loop.test.ts +0 -414
  324. package/src/core/loop/loop.ts +0 -71
  325. package/src/core/navmesh/README.md +0 -164
  326. package/src/core/navmesh/index.ts +0 -1
  327. package/src/core/navmesh/navmesh-worker-pool.ts +0 -236
  328. package/src/core/navmesh/navmesh-workers.test.ts +0 -356
  329. package/src/core/navmesh/navmesh.test.ts +0 -344
  330. package/src/core/navmesh/navmesh.ts +0 -1047
  331. package/src/core/navmesh/navmesh.worker.ts +0 -147
  332. package/src/core/pooled-codec/README.md +0 -70
  333. package/src/core/pooled-codec/index.ts +0 -1
  334. package/src/core/pooled-codec/pooled-codec.test.ts +0 -862
  335. package/src/core/pooled-codec/pooled-codec.ts +0 -504
  336. package/src/core/prediction/README.md +0 -64
  337. package/src/core/prediction/index.ts +0 -1
  338. package/src/core/prediction/prediction.test.ts +0 -423
  339. package/src/core/prediction/prediction.ts +0 -112
  340. package/src/ecs/README.md +0 -427
  341. package/src/ecs/benchmark.test.ts +0 -1645
  342. package/src/ecs/component-store.ts +0 -198
  343. package/src/ecs/component.ts +0 -90
  344. package/src/ecs/entity-handle.test.ts +0 -393
  345. package/src/ecs/entity-handle.ts +0 -563
  346. package/src/ecs/example.ts +0 -152
  347. package/src/ecs/index.ts +0 -4
  348. package/src/ecs/system-builder.ts +0 -309
  349. package/src/ecs/system.ts +0 -111
  350. package/src/ecs/world-systems.ts +0 -83
  351. package/src/ecs/world.test.ts +0 -310
  352. package/src/ecs/world.ts +0 -828
  353. package/src/index.ts +0 -28
  354. package/src/net/README.md +0 -474
  355. package/src/net/adapters/browser-websocket.ts +0 -86
  356. package/src/net/adapters/bun-websocket.ts +0 -292
  357. package/src/net/buffer-pool.ts +0 -106
  358. package/src/net/client.test.ts +0 -807
  359. package/src/net/client.ts +0 -695
  360. package/src/net/index.ts +0 -60
  361. package/src/net/server.test.ts +0 -799
  362. package/src/net/server.ts +0 -1116
  363. package/src/net/types.ts +0 -228
  364. package/src/net/validators.ts +0 -104
  365. package/src/protocol/README.md +0 -469
  366. package/src/protocol/index.ts +0 -93
  367. package/src/protocol/intent/define-intent.test.ts +0 -397
  368. package/src/protocol/intent/define-intent.ts +0 -182
  369. package/src/protocol/intent/index.ts +0 -94
  370. package/src/protocol/intent/intent-registry.test.ts +0 -198
  371. package/src/protocol/intent/intent-registry.ts +0 -112
  372. package/src/protocol/intent/intent.ts +0 -12
  373. package/src/protocol/rpc/define-rpc.test.ts +0 -141
  374. package/src/protocol/rpc/define-rpc.ts +0 -113
  375. package/src/protocol/rpc/index.ts +0 -3
  376. package/src/protocol/rpc/rpc-registry.test.ts +0 -168
  377. package/src/protocol/rpc/rpc-registry.ts +0 -176
  378. package/src/protocol/rpc/rpc.ts +0 -37
  379. package/src/protocol/snapshot/index.ts +0 -45
  380. package/src/protocol/snapshot/snapshot-codec.test.ts +0 -138
  381. package/src/protocol/snapshot/snapshot-codec.ts +0 -87
  382. package/src/protocol/snapshot/snapshot-registry.test.ts +0 -310
  383. package/src/protocol/snapshot/snapshot-registry.ts +0 -201
  384. package/src/protocol/snapshot/snapshot.test.ts +0 -76
  385. package/src/protocol/snapshot/snapshot.ts +0 -41
  386. /package/dist/{core → types/core}/binary-codec/index.d.ts +0 -0
  387. /package/dist/{core/loop/loop.d.ts → types/core/driver/driver.d.ts} +0 -0
  388. /package/dist/{core/loop → types/core/driver}/drivers/index.d.ts +0 -0
  389. /package/dist/{core → types/core}/events/index.d.ts +0 -0
  390. /package/dist/{core → types/core}/fixed-ticker/index.d.ts +0 -0
  391. /package/dist/{core → types/core}/generate-id/generate-id.d.ts +0 -0
  392. /package/dist/{core → types/core}/generate-id/index.d.ts +0 -0
  393. /package/dist/{core → types/core}/lerp/index.d.ts +0 -0
  394. /package/dist/{core → types/core}/lerp/lerp.d.ts +0 -0
  395. /package/dist/{core → types/core}/navmesh/index.d.ts +0 -0
  396. /package/dist/{core → types/core}/navmesh/navmesh-worker-pool.d.ts +0 -0
  397. /package/dist/{core → types/core}/navmesh/navmesh.worker.d.ts +0 -0
  398. /package/dist/{core → types/core}/pooled-codec/index.d.ts +0 -0
  399. /package/dist/{core → types/core}/pooled-codec/pooled-codec.d.ts +0 -0
  400. /package/dist/{core → types/core}/prediction/index.d.ts +0 -0
  401. /package/dist/{core → types/core}/prediction/prediction.d.ts +0 -0
  402. /package/dist/{ecs → types/ecs}/component-store.d.ts +0 -0
  403. /package/dist/{ecs → types/ecs}/component.d.ts +0 -0
  404. /package/dist/{ecs → types/ecs}/entity-handle.d.ts +0 -0
  405. /package/dist/{ecs → types/ecs}/example.d.ts +0 -0
  406. /package/dist/{ecs → types/ecs}/index.d.ts +0 -0
  407. /package/dist/{ecs → types/ecs}/world-systems.d.ts +0 -0
  408. /package/dist/{net → types/net}/adapters/browser-websocket.d.ts +0 -0
  409. /package/dist/{net → types/net}/adapters/bun-websocket.d.ts +0 -0
  410. /package/dist/{net → types/net}/buffer-pool.d.ts +0 -0
  411. /package/dist/{net → types/net}/client.d.ts +0 -0
  412. /package/dist/{net → types/net}/types.d.ts +0 -0
  413. /package/dist/{net → types/net}/validators.d.ts +0 -0
  414. /package/dist/{protocol → types/protocol}/index.d.ts +0 -0
  415. /package/dist/{protocol → types/protocol}/intent/intent-registry.d.ts +0 -0
  416. /package/dist/{protocol → types/protocol}/intent/intent.d.ts +0 -0
  417. /package/dist/{protocol → types/protocol}/rpc/define-rpc.d.ts +0 -0
  418. /package/dist/{protocol → types/protocol}/rpc/index.d.ts +0 -0
  419. /package/dist/{protocol → types/protocol}/rpc/rpc-registry.d.ts +0 -0
  420. /package/dist/{protocol → types/protocol}/rpc/rpc.d.ts +0 -0
  421. /package/dist/{protocol → types/protocol}/snapshot/index.d.ts +0 -0
  422. /package/dist/{protocol → types/protocol}/snapshot/snapshot-codec.d.ts +0 -0
  423. /package/dist/{protocol → types/protocol}/snapshot/snapshot-registry.d.ts +0 -0
  424. /package/dist/{protocol → types/protocol}/snapshot/snapshot.d.ts +0 -0
  425. /package/dist/{protocol/intent/intent.js → webgpu/types/2d/animation.test.d.ts} +0 -0
@@ -1,938 +0,0 @@
1
- import { MessageType, MessagePriority } from "./types";
2
- import { MessageWrapperPool } from "./buffer-pool";
3
- /**
4
- * Generic game server that manages multiple peer connections
5
- * Each peer gets its own snapshot registry for per-peer state tracking (fog of war, interest management)
6
- *
7
- * @template TPeer The transport adapter type for peer connections
8
- * @template TSnapshots Union type of all possible snapshot update types
9
- *
10
- * @remarks
11
- * **Client-Side Prediction Support:**
12
- * - Automatically tracks the last processed client tick for each peer
13
- * - All intents include a 'tick' field (added by defineIntent())
14
- * - Use `getConfirmedClientTick(peerId)` to get the confirmed tick for snapshots
15
- * - Use `onAnyIntent()` to track which peers need snapshot responses
16
- *
17
- * @example
18
- * ```ts
19
- * type GameSnapshots = PlayerUpdate | ScoreUpdate | ProjectileUpdate;
20
- *
21
- * const server = new ServerNetwork<WebSocketPeer, GameSnapshots>({
22
- * transport: wsServerTransport,
23
- * intentRegistry,
24
- * createPeerSnapshotRegistry: () => {
25
- * const registry = new SnapshotRegistry<GameSnapshots>();
26
- * registry.register('players', playerCodec);
27
- * registry.register('score', scoreCodec);
28
- * return registry;
29
- * },
30
- * });
31
- *
32
- * // Track which peers need responses (fires for ALL intents)
33
- * const pendingResponses = new Set<string>();
34
- * server.onAnyIntent((peerId) => {
35
- * pendingResponses.add(peerId);
36
- * });
37
- *
38
- * // Type-safe intent handlers
39
- * server.onIntent<MoveIntent>(IntentKind.Move, (peerId, intent) => {
40
- * intent.tick // ✅ Automatically included in all intents
41
- * intent.dx // ✅ Correctly typed
42
- * });
43
- *
44
- * // Send snapshot with confirmed client tick (for client-side prediction)
45
- * const confirmedTick = server.getConfirmedClientTick(peerId);
46
- * server.sendSnapshotToPeer(peerId, 'players', {
47
- * tick: confirmedTick, // Client can reconcile based on this
48
- * updates: { players: [...] }
49
- * });
50
- * ```
51
- */
52
- export class ServerNetwork {
53
- constructor(config) {
54
- /** Per-peer state tracking */
55
- this.peers = new Map();
56
- /** Per-peer snapshot registries - this is the key feature! */
57
- this.peerSnapshotRegistries = new Map();
58
- /** Track last processed client tick per peer (for client-side prediction) */
59
- this.lastProcessedClientTick = new Map();
60
- /** Track last sent snapshot hashes per peer per type (for delta detection) */
61
- this.lastSnapshotHashes = new Map();
62
- /** Intent handlers: kind -> handler[] (supports multiple handlers) */
63
- this.intentHandlers = new Map();
64
- /** Global intent handler called for ALL intents before specific handlers */
65
- this.anyIntentHandlers = [];
66
- /** RPC method handlers: method -> handler[] (supports multiple handlers) */
67
- this.rpcHandlers = new Map();
68
- /** Connection lifecycle handlers */
69
- this.connectionHandlers = [];
70
- this.disconnectionHandlers = [];
71
- /** Message wrapper pool for zero-allocation message wrapping */
72
- this.messagePool = null;
73
- /** Heartbeat interval timer */
74
- this.heartbeatTimer = null;
75
- this.transport = config.transport;
76
- this.intentRegistry = config.intentRegistry;
77
- this.createPeerSnapshotRegistry = config.createPeerSnapshotRegistry;
78
- this.rpcRegistry = config.rpcRegistry;
79
- this.config = {
80
- maxMessageSize: config.config?.maxMessageSize ?? 65536,
81
- debug: config.config?.debug ?? false,
82
- maxMessagesPerSecond: config.config?.maxMessagesPerSecond ?? 100,
83
- maxSendQueueSize: config.config?.maxSendQueueSize ?? 100,
84
- enableBufferPooling: config.config?.enableBufferPooling ?? true,
85
- heartbeatInterval: config.config?.heartbeatInterval ?? 30000,
86
- heartbeatTimeout: config.config?.heartbeatTimeout ?? 60000,
87
- lagSimulation: config.config?.lagSimulation ?? 0,
88
- };
89
- // Initialize message pool if buffer pooling is enabled
90
- if (this.config.enableBufferPooling) {
91
- this.messagePool = new MessageWrapperPool();
92
- }
93
- this.setupTransportHandlers();
94
- this.setupHeartbeat();
95
- }
96
- /**
97
- * Get the snapshot registry for a specific peer
98
- */
99
- getPeerSnapshotRegistry(peerId) {
100
- return this.peerSnapshotRegistries.get(peerId);
101
- }
102
- /**
103
- * Register a handler for a specific intent kind (type-safe)
104
- * Supports multiple handlers per intent type
105
- * @template T The intent type for this handler
106
- * @param validator Optional validation function - if it returns false, intent is rejected
107
- * @returns Unsubscribe function to remove this handler
108
- */
109
- onIntent(intent, handler, validator) {
110
- let handlers = this.intentHandlers.get(intent.kind);
111
- if (!handlers) {
112
- handlers = [];
113
- this.intentHandlers.set(intent.kind, handlers);
114
- }
115
- // Wrap handler with validator if provided
116
- const wrappedHandler = (peerId, intent) => {
117
- if (validator) {
118
- if (!validator(peerId, intent)) {
119
- this.log(`Intent validation failed for peer ${peerId}, kind ${intent.kind}`);
120
- return;
121
- }
122
- }
123
- handler(peerId, intent);
124
- };
125
- handlers.push(wrappedHandler);
126
- // Return unsubscribe function
127
- return () => {
128
- const handlers = this.intentHandlers.get(intent.kind);
129
- if (handlers) {
130
- const index = handlers.indexOf(wrappedHandler);
131
- if (index > -1) {
132
- handlers.splice(index, 1);
133
- }
134
- }
135
- };
136
- }
137
- /**
138
- * Register a handler that fires for ALL intents, regardless of kind.
139
- * Useful for tracking which peers sent intents this tick.
140
- *
141
- * @param handler Callback invoked for every intent before specific handlers
142
- * @returns Unsubscribe function
143
- *
144
- * @remarks
145
- * This handler is called BEFORE the specific intent handlers registered via onIntent().
146
- * Common use case: Track which peers need snapshot responses this tick.
147
- *
148
- * @example
149
- * ```ts
150
- * const pendingResponses = new Set<string>();
151
- *
152
- * server.onAnyIntent((peerId, intent) => {
153
- * // Mark this peer as needing a response on next tick
154
- * pendingResponses.add(peerId);
155
- * });
156
- * ```
157
- */
158
- onAnyIntent(handler) {
159
- this.anyIntentHandlers.push(handler);
160
- // Return unsubscribe function
161
- return () => {
162
- const index = this.anyIntentHandlers.indexOf(handler);
163
- if (index > -1) {
164
- this.anyIntentHandlers.splice(index, 1);
165
- }
166
- };
167
- }
168
- /**
169
- * Register a handler for new connections
170
- */
171
- onConnection(handler) {
172
- this.connectionHandlers.push(handler);
173
- }
174
- /**
175
- * Register a handler for disconnections
176
- */
177
- onDisconnection(handler) {
178
- this.disconnectionHandlers.push(handler);
179
- }
180
- /**
181
- * Send an RPC to a specific peer (type-safe)
182
- *
183
- * @template TSchema The RPC data type
184
- * @param peerId The peer to send to
185
- * @param rpc The RPC definition created by defineRPC()
186
- * @param data The RPC data to send
187
- * @param priority Message priority (default: NORMAL)
188
- *
189
- * @example
190
- * ```ts
191
- * const MatchCountdown = defineRPC({
192
- * method: 'matchCountdown',
193
- * schema: { secondsRemaining: BinaryCodec.u8 }
194
- * });
195
- *
196
- * server.sendRpc(peerId, MatchCountdown, { secondsRemaining: 10 });
197
- * ```
198
- */
199
- sendRPC(peerId, rpc, data, priority = MessagePriority.NORMAL) {
200
- if (!this.rpcRegistry) {
201
- throw new Error('RpcRegistry not configured. Pass rpcRegistry to ServerNetworkConfig.');
202
- }
203
- const peer = this.peers.get(peerId);
204
- if (!peer) {
205
- this.log(`Cannot send RPC to unknown peer: ${peerId}`);
206
- return;
207
- }
208
- try {
209
- // Encode RPC
210
- const rpcData = this.rpcRegistry.encode(rpc, data);
211
- // Wrap with message type header (use pool if enabled)
212
- let message;
213
- if (this.messagePool) {
214
- message = this.messagePool.wrap(MessageType.CUSTOM, rpcData);
215
- }
216
- else {
217
- message = new Uint8Array(1 + rpcData.byteLength);
218
- message[0] = MessageType.CUSTOM;
219
- message.set(rpcData, 1);
220
- }
221
- // Check backpressure and queue if necessary
222
- if (peer.isBackpressured || peer.sendQueue.length > 0) {
223
- // Peer is experiencing backpressure, queue the message with priority
224
- this.queueMessage(peer, message, priority);
225
- // Release pooled buffer since we copied it in queueMessage
226
- if (this.messagePool) {
227
- this.messagePool.release(message);
228
- }
229
- return;
230
- }
231
- // Try to send immediately
232
- this.sendMessageToPeer(peer, message);
233
- // Release pooled buffer after send
234
- if (this.messagePool) {
235
- this.messagePool.release(message);
236
- }
237
- this.log(`Sent RPC (method: ${rpc.method}) to peer: ${peerId}`);
238
- }
239
- catch (error) {
240
- this.log(`Failed to send RPC to peer ${peerId}: ${error}`);
241
- }
242
- }
243
- /**
244
- * Send an RPC to all connected peers (broadcast)
245
- *
246
- * @template TSchema The RPC data type
247
- * @param rpc The RPC definition created by defineRPC()
248
- * @param data The RPC data to send
249
- * @param priority Message priority (default: NORMAL)
250
- *
251
- * @example
252
- * ```ts
253
- * server.sendRpcBroadcast(MatchCountdown, { secondsRemaining: 3 });
254
- * ```
255
- */
256
- sendRpcBroadcast(rpc, data, priority = MessagePriority.NORMAL) {
257
- for (const peerId of this.getPeerIds()) {
258
- this.sendRPC(peerId, rpc, data, priority);
259
- }
260
- }
261
- /**
262
- * Register a handler for incoming RPCs from clients (type-safe)
263
- * Supports multiple handlers per RPC method
264
- *
265
- * @template TSchema The RPC data type
266
- * @param rpc The RPC definition created by defineRPC()
267
- * @param handler Callback function to handle the RPC
268
- * @returns Unsubscribe function to remove this handler
269
- *
270
- * @example
271
- * ```ts
272
- * const BuyItem = defineRPC({
273
- * method: 'buyItem',
274
- * schema: { itemId: BinaryCodec.string(32) }
275
- * });
276
- *
277
- * server.onRpc(BuyItem, (peerId, rpc) => {
278
- * console.log(`${peerId} wants to buy ${rpc.itemId}`);
279
- * });
280
- * ```
281
- */
282
- onRPC(rpc, handler) {
283
- if (!this.rpcRegistry) {
284
- throw new Error('RpcRegistry not configured. Pass rpcRegistry to ServerNetworkConfig.');
285
- }
286
- let handlers = this.rpcHandlers.get(rpc.method);
287
- if (!handlers) {
288
- handlers = [];
289
- this.rpcHandlers.set(rpc.method, handlers);
290
- }
291
- handlers.push(handler);
292
- // Return unsubscribe function
293
- return () => {
294
- const handlers = this.rpcHandlers.get(rpc.method);
295
- if (handlers) {
296
- const index = handlers.indexOf(handler);
297
- if (index > -1) {
298
- handlers.splice(index, 1);
299
- }
300
- }
301
- };
302
- }
303
- /**
304
- * Send a snapshot to a specific peer using their dedicated snapshot registry (type-safe)
305
- * @template T The specific snapshot update type
306
- * @param priority Message priority (default: NORMAL)
307
- */
308
- sendSnapshotToPeer(peerId, type, snapshot, priority = MessagePriority.NORMAL) {
309
- const peer = this.peers.get(peerId);
310
- if (!peer) {
311
- this.log(`Cannot send snapshot to unknown peer: ${peerId}`);
312
- return;
313
- }
314
- const registry = this.peerSnapshotRegistries.get(peerId);
315
- if (!registry) {
316
- throw new Error(`No snapshot registry registered for peer: ${peerId}`);
317
- }
318
- // Encode snapshot using peer-specific registry
319
- const snapshotData = registry.encode(type, snapshot);
320
- // Wrap with message type header (use pool if enabled)
321
- let message;
322
- if (this.messagePool) {
323
- // Zero-copy path: reuse pooled buffer
324
- message = this.messagePool.wrap(MessageType.SNAPSHOT, snapshotData);
325
- }
326
- else {
327
- // Fallback path: allocate new buffer
328
- message = new Uint8Array(1 + snapshotData.byteLength);
329
- message[0] = MessageType.SNAPSHOT;
330
- message.set(snapshotData, 1);
331
- }
332
- // Check backpressure and queue if necessary
333
- if (peer.isBackpressured || peer.sendQueue.length > 0) {
334
- // Peer is experiencing backpressure, queue the message with priority
335
- this.queueMessage(peer, message, priority);
336
- // Release pooled buffer since we copied it in queueMessage
337
- if (this.messagePool) {
338
- this.messagePool.release(message);
339
- }
340
- return;
341
- }
342
- // Try to send immediately
343
- this.sendMessageToPeer(peer, message);
344
- // Update tracking
345
- peer.lastSentTick = snapshot.tick;
346
- this.log(`Sent snapshot (type: ${type}, tick: ${snapshot.tick}) to peer ${peerId}`);
347
- }
348
- /**
349
- * Send a snapshot to a peer only if it has changed since the last send.
350
- * Uses fast binary hash comparison with zero allocations.
351
- *
352
- * @template T The specific snapshot update type
353
- * @param peerId The peer to send to
354
- * @param type The snapshot type identifier
355
- * @param snapshot The snapshot to send
356
- * @param priority Message priority (default: NORMAL)
357
- * @returns true if snapshot was sent, false if skipped (no change)
358
- *
359
- * @remarks
360
- * This method encodes the snapshot and computes a hash of the binary data.
361
- * This is more efficient than JSON.stringify because:
362
- * 1. No string allocations (hash is a number)
363
- * 2. Binary hashing is faster than string hashing
364
- * 3. Reuses the encoding work (binary data needed for sending anyway)
365
- *
366
- * @example
367
- * ```ts
368
- * // In your game tick loop
369
- * for (const peerId of server.getPeerIds()) {
370
- * const wasSent = server.sendSnapshotToPeerIfChanged(peerId, 'gameState', {
371
- * tick: currentTick,
372
- * updates: gameState
373
- * });
374
- * if (wasSent) sentCount++;
375
- * }
376
- * ```
377
- */
378
- sendSnapshotToPeerIfChanged(peerId, type, snapshot, priority = MessagePriority.NORMAL) {
379
- const peer = this.peers.get(peerId);
380
- if (!peer) {
381
- this.log(`Cannot send snapshot to unknown peer: ${peerId}`);
382
- return false;
383
- }
384
- const registry = this.peerSnapshotRegistries.get(peerId);
385
- if (!registry) {
386
- throw new Error(`No snapshot registry registered for peer: ${peerId}`);
387
- }
388
- // Encode snapshot (needed for both hashing and sending)
389
- const snapshotData = registry.encode(type, snapshot);
390
- // Get or create hash map for this peer
391
- let peerHashes = this.lastSnapshotHashes.get(peerId);
392
- if (!peerHashes) {
393
- peerHashes = new Map();
394
- this.lastSnapshotHashes.set(peerId, peerHashes);
395
- }
396
- // Compute hash of only the updates portion (skip typeId + tick)
397
- // Binary format: [typeId(1) + tick(4) + updates(...)]
398
- // We only want to hash the updates, not the tick
399
- const updatesData = snapshotData.subarray(5); // Skip first 5 bytes
400
- const currentHash = this.hashBinary(updatesData);
401
- const lastHash = peerHashes.get(type);
402
- // Only send if changed (or first time)
403
- if (lastHash === undefined || currentHash !== lastHash) {
404
- // Wrap with message type header (use pool if enabled)
405
- let message;
406
- if (this.messagePool) {
407
- // Zero-copy path: reuse pooled buffer
408
- message = this.messagePool.wrap(MessageType.SNAPSHOT, snapshotData);
409
- }
410
- else {
411
- // Fallback path: allocate new buffer
412
- message = new Uint8Array(1 + snapshotData.byteLength);
413
- message[0] = MessageType.SNAPSHOT;
414
- message.set(snapshotData, 1);
415
- }
416
- // Check backpressure and queue if necessary
417
- if (peer.isBackpressured || peer.sendQueue.length > 0) {
418
- this.queueMessage(peer, message, priority);
419
- if (this.messagePool) {
420
- this.messagePool.release(message);
421
- }
422
- }
423
- else {
424
- // Try to send immediately
425
- this.sendMessageToPeer(peer, message);
426
- }
427
- // Update tracking
428
- peer.lastSentTick = snapshot.tick;
429
- peerHashes.set(type, currentHash);
430
- this.log(`Sent snapshot (type: ${type}, tick: ${snapshot.tick}) to peer ${peerId}`);
431
- return true; // Sent
432
- }
433
- this.log(`Skipped snapshot (type: ${type}) to peer ${peerId} - no change detected`);
434
- return false; // Skipped (no change)
435
- }
436
- /**
437
- * Fast binary hash function with zero allocations.
438
- * Uses FNV-1a algorithm optimized for binary data.
439
- *
440
- * @param data The binary data to hash
441
- * @returns A 32-bit hash value
442
- *
443
- * @remarks
444
- * This is significantly faster than JSON.stringify + string hashing because:
445
- * - No string allocations
446
- * - Direct byte-level hashing
447
- * - Operates on data that's already needed for encoding
448
- *
449
- * FNV-1a is chosen for its speed and good distribution properties.
450
- */
451
- hashBinary(data) {
452
- let hash = 2166136261; // FNV-1a 32-bit offset basis
453
- for (let i = 0; i < data.length; i++) {
454
- hash ^= data[i];
455
- hash = Math.imul(hash, 16777619); // FNV-1a 32-bit prime
456
- }
457
- return hash >>> 0; // Ensure unsigned 32-bit integer
458
- }
459
- /**
460
- * Queue a message with priority
461
- */
462
- queueMessage(peer, message, priority) {
463
- const queuedMessage = {
464
- data: new Uint8Array(message), // Copy to avoid pool reuse issues
465
- priority,
466
- timestamp: Date.now(),
467
- };
468
- // If queue is full, drop lowest priority message
469
- if (peer.sendQueue.length >= this.config.maxSendQueueSize) {
470
- // Sort by priority (ascending) to find lowest priority message
471
- peer.sendQueue.sort((a, b) => a.priority - b.priority);
472
- // Drop the lowest priority message (first in sorted array)
473
- const dropped = peer.sendQueue.shift();
474
- this.log(`Send queue full for peer ${peer.peerId}, dropping ${MessagePriority[dropped.priority]} priority message`);
475
- }
476
- // Insert message in priority order (higher priority first)
477
- let insertIndex = peer.sendQueue.length;
478
- for (let i = 0; i < peer.sendQueue.length; i++) {
479
- if (queuedMessage.priority > peer.sendQueue[i].priority) {
480
- insertIndex = i;
481
- break;
482
- }
483
- }
484
- peer.sendQueue.splice(insertIndex, 0, queuedMessage);
485
- }
486
- /**
487
- * Internal: Send a message to a peer, handling pooling and bandwidth tracking
488
- */
489
- sendMessageToPeer(peer, message) {
490
- // Track bandwidth
491
- this.trackBandwidth(peer, message.byteLength);
492
- // Send to peer
493
- const sendResult = peer.transport.send(message);
494
- // Handle both sync and async transports for pool cleanup
495
- if (this.messagePool) {
496
- if (sendResult instanceof Promise) {
497
- // Async transport: wait for send to complete before releasing
498
- sendResult.then(() => {
499
- this.messagePool.release(message);
500
- // Try to flush queue if there are pending messages
501
- this.flushSendQueue(peer.peerId);
502
- }).catch(() => {
503
- // Mark peer as backpressured on send failure
504
- peer.isBackpressured = true;
505
- this.messagePool.release(message);
506
- this.log(`Send failed for peer ${peer.peerId}, marking as backpressured`);
507
- });
508
- }
509
- else {
510
- // Sync transport: release immediately
511
- this.messagePool.release(message);
512
- }
513
- }
514
- else if (sendResult instanceof Promise) {
515
- // No pool but async transport - still handle failures
516
- sendResult.catch(() => {
517
- peer.isBackpressured = true;
518
- this.log(`Send failed for peer ${peer.peerId}, marking as backpressured`);
519
- });
520
- }
521
- }
522
- /**
523
- * Flush send queue for a peer (called after successful sends)
524
- * Sends messages in priority order (highest priority first)
525
- */
526
- flushSendQueue(peerId) {
527
- const peer = this.peers.get(peerId);
528
- if (!peer || peer.sendQueue.length === 0) {
529
- return;
530
- }
531
- // Mark as no longer backpressured
532
- peer.isBackpressured = false;
533
- // Send queued messages (up to a limit per flush to avoid blocking)
534
- // Queue is already sorted by priority (highest first)
535
- const maxMessagesPerFlush = 10;
536
- let sent = 0;
537
- while (peer.sendQueue.length > 0 && sent < maxMessagesPerFlush) {
538
- const queuedMessage = peer.sendQueue.shift();
539
- this.sendMessageToPeer(peer, queuedMessage.data);
540
- sent++;
541
- // If we hit backpressure again, stop flushing
542
- if (peer.isBackpressured) {
543
- break;
544
- }
545
- }
546
- if (peer.sendQueue.length > 0) {
547
- this.log(`Peer ${peerId} still has ${peer.sendQueue.length} queued messages`);
548
- }
549
- }
550
- /**
551
- * Track bandwidth usage for a peer
552
- */
553
- trackBandwidth(peer, bytes) {
554
- const now = Date.now();
555
- const windowStart = Math.floor(now / 1000) * 1000;
556
- // Reset counter if we're in a new time window
557
- if (peer.bandwidthWindow !== windowStart) {
558
- peer.bandwidthWindow = windowStart;
559
- peer.bytesSent = 0;
560
- }
561
- peer.bytesSent += bytes;
562
- }
563
- /**
564
- * Broadcast a snapshot to all connected peers (type-safe)
565
- * Each peer receives the snapshot encoded with their own snapshot registry
566
- * @template T The specific snapshot update type
567
- * @param priority Message priority (default: NORMAL)
568
- */
569
- broadcastSnapshot(type, snapshot, filter, priority = MessagePriority.NORMAL) {
570
- for (const peerId of this.peers.keys()) {
571
- if (filter && !filter(peerId)) {
572
- continue;
573
- }
574
- this.sendSnapshotToPeer(peerId, type, snapshot, priority);
575
- }
576
- }
577
- /**
578
- * Advanced: Broadcast with per-peer snapshot customization (type-safe)
579
- * Allows you to modify the snapshot for each peer (e.g., fog of war, interest management)
580
- * @template T The specific snapshot update type
581
- * @param priority Message priority (default: NORMAL)
582
- */
583
- broadcastSnapshotWithCustomization(type, baseSnapshot, customize, priority = MessagePriority.NORMAL) {
584
- for (const peerId of this.peers.keys()) {
585
- const customSnapshot = customize(peerId, baseSnapshot);
586
- this.sendSnapshotToPeer(peerId, type, customSnapshot, priority);
587
- }
588
- }
589
- /**
590
- * Get all connected peer IDs
591
- */
592
- getPeerIds() {
593
- return Array.from(this.peers.keys());
594
- }
595
- /**
596
- * Get peer state for a specific peer
597
- */
598
- getPeerState(peerId) {
599
- return this.peers.get(peerId);
600
- }
601
- /**
602
- * Update peer metadata
603
- */
604
- setPeerMetadata(peerId, key, value) {
605
- const peer = this.peers.get(peerId);
606
- if (peer) {
607
- peer.metadata[key] = value;
608
- }
609
- }
610
- /**
611
- * Get bandwidth usage for a peer (bytes per second in current window)
612
- */
613
- getPeerBandwidth(peerId) {
614
- const peer = this.peers.get(peerId);
615
- return peer ? peer.bytesSent : 0;
616
- }
617
- /**
618
- * Get total bandwidth usage across all peers
619
- */
620
- getTotalBandwidth() {
621
- let total = 0;
622
- for (const peer of this.peers.values()) {
623
- total += peer.bytesSent;
624
- }
625
- return total;
626
- }
627
- /**
628
- * Check if a peer is experiencing backpressure
629
- */
630
- isPeerBackpressured(peerId) {
631
- const peer = this.peers.get(peerId);
632
- return peer ? peer.isBackpressured || peer.sendQueue.length > 0 : false;
633
- }
634
- /**
635
- * Get the last confirmed client tick for a peer.
636
- * Used for client-side prediction reconciliation.
637
- *
638
- * @param peerId The peer ID to query
639
- * @returns The last processed client tick number, or 0 if peer not found
640
- *
641
- * @remarks
642
- * This is automatically tracked when intents arrive, as all intents
643
- * include a 'tick' field (added by defineIntent).
644
- */
645
- getConfirmedClientTick(peerId) {
646
- return this.lastProcessedClientTick.get(peerId) ?? 0;
647
- }
648
- /**
649
- * Set the confirmed client tick for a peer.
650
- * Rarely needed as this is automatically tracked by handleIntent.
651
- *
652
- * @param peerId The peer ID
653
- * @param tick The client tick number to set
654
- *
655
- * @remarks
656
- * All intents automatically include a 'tick' field via defineIntent(),
657
- * so this is tracked automatically when intents are received.
658
- */
659
- setConfirmedClientTick(peerId, tick) {
660
- this.lastProcessedClientTick.set(peerId, tick);
661
- }
662
- /**
663
- * Close the server and all connections
664
- */
665
- close() {
666
- this.log("Closing server...");
667
- // Stop heartbeat timer
668
- if (this.heartbeatTimer) {
669
- clearInterval(this.heartbeatTimer);
670
- this.heartbeatTimer = null;
671
- }
672
- return this.transport.close();
673
- }
674
- /**
675
- * Setup transport event handlers
676
- */
677
- setupTransportHandlers() {
678
- this.transport.onConnection((peer, peerId) => {
679
- this.handleConnection(peer, peerId);
680
- });
681
- this.transport.onDisconnection((peerId) => {
682
- this.handleDisconnection(peerId);
683
- });
684
- }
685
- /**
686
- * Setup heartbeat mechanism
687
- */
688
- setupHeartbeat() {
689
- if (this.config.heartbeatInterval === 0) {
690
- return; // Heartbeats disabled
691
- }
692
- this.heartbeatTimer = setInterval(() => {
693
- this.checkHeartbeats();
694
- }, this.config.heartbeatInterval);
695
- }
696
- /**
697
- * Check all peers for heartbeat timeout and send heartbeats
698
- */
699
- checkHeartbeats() {
700
- const now = Date.now();
701
- const heartbeatMessage = new Uint8Array([MessageType.HEARTBEAT]);
702
- for (const [peerId, peer] of this.peers.entries()) {
703
- // Check if peer has timed out
704
- const timeSinceLastMessage = now - peer.lastMessageReceivedAt;
705
- if (timeSinceLastMessage > this.config.heartbeatTimeout) {
706
- this.log(`Peer ${peerId} timed out (no message for ${timeSinceLastMessage}ms)`);
707
- // Close the connection - this will trigger handleDisconnection
708
- peer.transport.close();
709
- continue;
710
- }
711
- // Send heartbeat to peer
712
- try {
713
- peer.transport.send(heartbeatMessage);
714
- this.trackBandwidth(peer, heartbeatMessage.byteLength);
715
- }
716
- catch (error) {
717
- this.log(`Failed to send heartbeat to peer ${peerId}: ${error}`);
718
- }
719
- }
720
- }
721
- /**
722
- * Handle new peer connection
723
- */
724
- handleConnection(peer, peerId) {
725
- this.log(`Peer connected: ${peerId}`);
726
- // Create peer state
727
- const now = Date.now();
728
- const peerState = {
729
- peerId,
730
- transport: peer,
731
- lastSentTick: 0,
732
- connectedAt: now,
733
- metadata: {},
734
- messageCount: 0,
735
- messageCountWindow: now,
736
- sendQueue: [],
737
- bytesSent: 0,
738
- bandwidthWindow: now,
739
- isBackpressured: false,
740
- lastMessageReceivedAt: now,
741
- };
742
- this.peers.set(peerId, peerState);
743
- // Create per-peer snapshot registry
744
- const snapshotRegistry = this.createPeerSnapshotRegistry();
745
- this.peerSnapshotRegistries.set(peerId, snapshotRegistry);
746
- // Initialize client tick tracking (for client-side prediction)
747
- this.lastProcessedClientTick.set(peerId, 0);
748
- // Setup message handler for this peer
749
- peer.onMessage((data) => {
750
- this.handlePeerMessage(peerId, data);
751
- });
752
- // Notify handlers
753
- for (const handler of this.connectionHandlers) {
754
- try {
755
- handler(peerId);
756
- }
757
- catch (error) {
758
- // Don't call log here as it might throw, use console.error directly
759
- if (this.config.debug) {
760
- console.error(`[ServerNetwork] Error in connection handler: ${error}`);
761
- }
762
- }
763
- }
764
- }
765
- /**
766
- * Handle peer disconnection
767
- */
768
- handleDisconnection(peerId) {
769
- this.log(`Peer disconnected: ${peerId}`);
770
- this.peers.delete(peerId);
771
- this.peerSnapshotRegistries.delete(peerId);
772
- this.lastProcessedClientTick.delete(peerId);
773
- this.lastSnapshotHashes.delete(peerId);
774
- // Notify handlers
775
- for (const handler of this.disconnectionHandlers) {
776
- try {
777
- handler(peerId);
778
- }
779
- catch (error) {
780
- // Don't call log here as it might throw, use console.error directly
781
- if (this.config.debug) {
782
- console.error(`[ServerNetwork] Error in disconnection handler: ${error}`);
783
- }
784
- }
785
- }
786
- }
787
- /**
788
- * Handle incoming message from a peer
789
- */
790
- handlePeerMessage(peerId, data) {
791
- // Update last message received timestamp
792
- const peer = this.peers.get(peerId);
793
- if (peer) {
794
- peer.lastMessageReceivedAt = Date.now();
795
- }
796
- if (data.byteLength === 0) {
797
- this.log(`Received empty message from peer ${peerId}`);
798
- return;
799
- }
800
- if (data.byteLength > this.config.maxMessageSize) {
801
- this.log(`Message from peer ${peerId} exceeds max size: ${data.byteLength} > ${this.config.maxMessageSize}`);
802
- return;
803
- }
804
- const messageType = data[0];
805
- const payload = data.subarray(1);
806
- switch (messageType) {
807
- case MessageType.INTENT:
808
- this.handleIntent(peerId, payload);
809
- break;
810
- case MessageType.HEARTBEAT:
811
- // Heartbeat received - already updated lastMessageReceivedAt above
812
- this.log(`Received heartbeat from peer ${peerId}`);
813
- break;
814
- case MessageType.CUSTOM:
815
- this.handleRPC(peerId, payload);
816
- break;
817
- default:
818
- this.log(`Unknown message type ${messageType} from peer ${peerId}`);
819
- }
820
- }
821
- /**
822
- * Decode and handle an intent from a peer
823
- */
824
- handleIntent(peerId, data) {
825
- // Rate limiting check
826
- if (!this.checkRateLimit(peerId)) {
827
- this.log(`Rate limit exceeded for peer ${peerId}, dropping intent`);
828
- return;
829
- }
830
- try {
831
- // Decode using intent registry (extracts kind from first byte internally)
832
- const intent = this.intentRegistry.decode(data);
833
- this.log(`Received intent (kind: ${intent.kind}) from peer ${peerId}`);
834
- // Automatically track client tick for client-side prediction
835
- // All intents have a 'tick' field added by defineIntent()
836
- this.lastProcessedClientTick.set(peerId, intent.tick);
837
- // Call global intent handlers first (e.g., for tracking pending responses)
838
- for (const handler of this.anyIntentHandlers) {
839
- try {
840
- handler(peerId, intent);
841
- }
842
- catch (error) {
843
- this.log(`Error in global intent handler: ${error}`);
844
- }
845
- }
846
- const handlers = this.intentHandlers.get(intent.kind);
847
- if (handlers && handlers.length > 0) {
848
- // Call all registered handlers
849
- for (const handler of handlers) {
850
- try {
851
- handler(peerId, intent);
852
- }
853
- catch (error) {
854
- this.log(`Error in intent handler: ${error}`);
855
- }
856
- }
857
- }
858
- else {
859
- this.log(`No handler registered for intent kind: ${intent.kind}`);
860
- }
861
- }
862
- catch (error) {
863
- this.log(`Failed to decode intent from peer ${peerId}: ${error}`);
864
- }
865
- }
866
- /**
867
- * Handle incoming RPC message from a peer
868
- */
869
- handleRPC(peerId, data) {
870
- if (!this.rpcRegistry) {
871
- this.log("Received RPC but RpcRegistry not configured");
872
- return;
873
- }
874
- // Rate limiting check
875
- if (!this.checkRateLimit(peerId)) {
876
- this.log(`Rate limit exceeded for peer ${peerId}, dropping RPC`);
877
- return;
878
- }
879
- try {
880
- // Decode using RPC registry (returns { method, data })
881
- const decoded = this.rpcRegistry.decode(data);
882
- this.log(`Received RPC (method: ${decoded.method}) from peer ${peerId}`);
883
- // Call all method-specific handlers if registered
884
- const handlers = this.rpcHandlers.get(decoded.method);
885
- if (handlers && handlers.length > 0) {
886
- for (const handler of handlers) {
887
- try {
888
- handler(peerId, decoded.data);
889
- }
890
- catch (error) {
891
- this.log(`Error in RPC handler: ${error}`);
892
- }
893
- }
894
- }
895
- else {
896
- this.log(`No handler registered for RPC method: ${decoded.method}`);
897
- }
898
- }
899
- catch (error) {
900
- this.log(`Failed to decode RPC from peer ${peerId}: ${error}`);
901
- }
902
- }
903
- /**
904
- * Check rate limit for a peer
905
- * Returns true if message should be processed, false if rate limit exceeded
906
- */
907
- checkRateLimit(peerId) {
908
- if (this.config.maxMessagesPerSecond === 0) {
909
- return true; // Rate limiting disabled
910
- }
911
- const peer = this.peers.get(peerId);
912
- if (!peer) {
913
- return false;
914
- }
915
- const now = Date.now();
916
- const windowStart = Math.floor(now / 1000) * 1000; // Start of current second
917
- // Reset counter if we're in a new time window
918
- if (peer.messageCountWindow !== windowStart) {
919
- peer.messageCountWindow = windowStart;
920
- peer.messageCount = 0;
921
- }
922
- // Check if limit would be exceeded BEFORE incrementing
923
- if (peer.messageCount >= this.config.maxMessagesPerSecond) {
924
- return false;
925
- }
926
- // Only increment if we're allowing this message
927
- peer.messageCount++;
928
- return true;
929
- }
930
- /**
931
- * Debug logging
932
- */
933
- log(message) {
934
- if (this.config.debug) {
935
- console.log(`[ServerNetwork] ${message}`);
936
- }
937
- }
938
- }