murow 0.0.70 → 0.0.72

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