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,1645 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
- import { BinaryCodec } from "../core/binary-codec";
3
- import { defineComponent } from "./component";
4
- import { World } from "./world";
5
-
6
-
7
- describe("ECS Performance Benchmarks", () => {
8
- // Define components for benchmarking
9
- const Transform = defineComponent("Transform", {
10
- x: BinaryCodec.f32,
11
- y: BinaryCodec.f32,
12
- rotation: BinaryCodec.f32,
13
- });
14
-
15
- const Velocity = defineComponent("Velocity", {
16
- vx: BinaryCodec.f32,
17
- vy: BinaryCodec.f32,
18
- });
19
-
20
- const Health = defineComponent("Health", {
21
- current: BinaryCodec.u16,
22
- max: BinaryCodec.u16,
23
- });
24
-
25
- test("spawn/despawn 10,000 entities (should be < 50ms)", () => {
26
- const world = new World({
27
- maxEntities: 10000,
28
- components: [Transform, Velocity, Health],
29
- });
30
-
31
- const start = performance.now();
32
-
33
- // Spawn 10,000 entities
34
- const entities: number[] = [];
35
- for (let i = 0; i < 10000; i++) {
36
- entities.push(world.spawn());
37
- }
38
-
39
- // Despawn all
40
- for (const entity of entities) {
41
- world.despawn(entity);
42
- }
43
-
44
- const elapsed = performance.now() - start;
45
-
46
- console.log(`Spawn/despawn 10k entities: ${elapsed.toFixed(2)}ms`);
47
- expect(elapsed).toBeLessThan(50);
48
- });
49
-
50
- test("add components to 10,000 entities (should be < 100ms)", () => {
51
- const world = new World({
52
- maxEntities: 10000,
53
- components: [Transform, Velocity, Health],
54
- });
55
-
56
- // Spawn entities
57
- const entities: number[] = [];
58
- for (let i = 0; i < 10000; i++) {
59
- entities.push(world.spawn());
60
- }
61
-
62
- const start = performance.now();
63
-
64
- // Add components
65
- for (const entity of entities) {
66
- world.add(entity, Transform, { x: 0, y: 0, rotation: 0 });
67
- world.add(entity, Velocity, { vx: 1, vy: 1 });
68
- world.add(entity, Health, { current: 100, max: 100 });
69
- }
70
-
71
- const elapsed = performance.now() - start;
72
-
73
- console.log(`Add 3 components to 10k entities: ${elapsed.toFixed(2)}ms`);
74
- expect(elapsed).toBeLessThan(100);
75
- });
76
-
77
- test("query and update 10,000 entities (should be < 20ms)", () => {
78
- const world = new World({
79
- maxEntities: 10000,
80
- components: [Transform, Velocity],
81
- });
82
-
83
- // Setup: spawn and add components
84
- for (let i = 0; i < 10000; i++) {
85
- const entity = world.spawn();
86
- world.add(entity, Transform, { x: i, y: i, rotation: 0 });
87
- world.add(entity, Velocity, { vx: 1, vy: 1 });
88
- }
89
-
90
- const start = performance.now();
91
-
92
- // Query and update (simulates physics system)
93
- const deltaTime = 0.016;
94
- for (const entity of world.query(Transform, Velocity)) {
95
- const t = world.get(entity, Transform);
96
- const v = world.get(entity, Velocity);
97
-
98
- // Update using partial update (most efficient)
99
- world.update(entity, Transform, {
100
- x: t.x + v.vx * deltaTime,
101
- y: t.y + v.vy * deltaTime,
102
- });
103
- }
104
-
105
- const elapsed = performance.now() - start;
106
-
107
- console.log(`Query + update 10k entities: ${elapsed.toFixed(2)}ms`);
108
- expect(elapsed).toBeLessThan(20);
109
- });
110
-
111
- test("repeated queries with caching (should be < 5ms)", () => {
112
- const world = new World({
113
- maxEntities: 5000,
114
- components: [Transform, Velocity, Health],
115
- });
116
-
117
- // Setup
118
- for (let i = 0; i < 5000; i++) {
119
- const entity = world.spawn();
120
- world.add(entity, Transform, { x: 0, y: 0, rotation: 0 });
121
- world.add(entity, Velocity, { vx: 1, vy: 1 });
122
- }
123
-
124
- const start = performance.now();
125
-
126
- // Run the same query 10 times (should be cached)
127
- for (let iteration = 0; iteration < 10; iteration++) {
128
- let count = 0;
129
- for (const entity of world.query(Transform, Velocity)) {
130
- const t = world.get(entity, Transform);
131
- world.update(entity, Transform, { x: t.x + 1 });
132
- count++;
133
- }
134
- expect(count).toBe(5000);
135
- }
136
-
137
- const elapsed = performance.now() - start;
138
-
139
- console.log(`10 iterations of query/update 5k entities: ${elapsed.toFixed(2)}ms`);
140
- expect(elapsed).toBeLessThan(50);
141
- });
142
-
143
- test("memory efficiency: ArrayBuffer vs Float32Array savings", () => {
144
- const world = new World({
145
- maxEntities: 10000,
146
- components: [Health], // u16 + u16 = 4 bytes
147
- });
148
-
149
- // Spawn entities with Health component
150
- for (let i = 0; i < 10000; i++) {
151
- const entity = world.spawn();
152
- world.add(entity, Health, { current: 100, max: 100 });
153
- }
154
-
155
- // Health component: 2 × u16 = 4 bytes per entity
156
- // ArrayBuffer: 10,000 × 4 = 40 KB
157
- // Float32Array (old): 10,000 × 8 = 80 KB (would round up to 2 floats)
158
- // Savings: 50%
159
-
160
- const expectedBytes = 10000 * 4;
161
- console.log(`Memory for 10k Health components: ${(expectedBytes / 1024).toFixed(2)} KB`);
162
- console.log(`(Float32Array would use: ${(10000 * 8 / 1024).toFixed(2)} KB - 50% savings!)`);
163
-
164
- expect(true).toBe(true); // Memory check is informational
165
- });
166
-
167
- test("zero allocations in hot path", () => {
168
- const world = new World({
169
- maxEntities: 1000,
170
- components: [Transform, Velocity],
171
- });
172
-
173
- // Setup
174
- for (let i = 0; i < 1000; i++) {
175
- const entity = world.spawn();
176
- world.add(entity, Transform, { x: 0, y: 0, rotation: 0 });
177
- world.add(entity, Velocity, { vx: 1, vy: 1 });
178
- }
179
-
180
- // Force GC
181
- if (global.gc) global.gc();
182
-
183
- const memBefore = (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2);
184
-
185
- // Run many iterations (should have zero allocations due to reusable objects)
186
- for (let i = 0; i < 100; i++) {
187
- for (const entity of world.query(Transform, Velocity)) {
188
- const t = world.get(entity, Transform); // Reuses same object!
189
- const v = world.get(entity, Velocity); // Reuses same object!
190
- world.update(entity, Transform, {
191
- x: t.x + v.vx,
192
- y: t.y + v.vy,
193
- });
194
- }
195
- }
196
-
197
- const memAfter = (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2);
198
-
199
- console.log(`Memory before: ${memBefore} MB, after: ${memAfter} MB`);
200
- console.log(`Memory delta: ${(parseFloat(memAfter) - parseFloat(memBefore)).toFixed(2)} MB (should be ~0)`);
201
-
202
- // Memory should not grow significantly (< 5 MB for 100 iterations × 1000 entities)
203
- const delta = parseFloat(memAfter) - parseFloat(memBefore);
204
- expect(delta).toBeLessThan(5);
205
- });
206
-
207
- test("realistic game loop: 1000 entities at 60 FPS", () => {
208
- const world = new World({
209
- maxEntities: 2000,
210
- components: [Transform, Velocity, Health],
211
- });
212
-
213
- // Setup game world
214
- for (let i = 0; i < 1000; i++) {
215
- const entity = world.spawn();
216
- world.add(entity, Transform, { x: Math.random() * 800, y: Math.random() * 600, rotation: 0 });
217
- world.add(entity, Velocity, { vx: (Math.random() - 0.5) * 100, vy: (Math.random() - 0.5) * 100 });
218
- world.add(entity, Health, { current: 100, max: 100 });
219
- }
220
-
221
- const frameTimings: number[] = [];
222
- const targetFPS = 60;
223
- const targetFrameTime = 1000 / targetFPS; // 16.67ms
224
-
225
- // Simulate 100 frames
226
- for (let frame = 0; frame < 100; frame++) {
227
- const frameStart = performance.now();
228
-
229
- const deltaTime = 1 / 60;
230
-
231
- // Physics system
232
- for (const entity of world.query(Transform, Velocity)) {
233
- const t = world.get(entity, Transform);
234
- const v = world.get(entity, Velocity);
235
-
236
- world.update(entity, Transform, {
237
- x: t.x + v.vx * deltaTime,
238
- y: t.y + v.vy * deltaTime,
239
- });
240
- }
241
-
242
- // Health system
243
- for (const entity of world.query(Health)) {
244
- const h = world.get(entity, Health);
245
- if (h.current <= 0) {
246
- world.despawn(entity);
247
- }
248
- }
249
-
250
- const frameTime = performance.now() - frameStart;
251
- frameTimings.push(frameTime);
252
- }
253
-
254
- const avgFrameTime = frameTimings.reduce((a, b) => a + b, 0) / frameTimings.length;
255
- const maxFrameTime = Math.max(...frameTimings);
256
-
257
- console.log(`Average frame time: ${avgFrameTime.toFixed(2)}ms (${(1000 / avgFrameTime).toFixed(0)} FPS)`);
258
- console.log(`Max frame time: ${maxFrameTime.toFixed(2)}ms`);
259
- console.log(`Target: ${targetFrameTime.toFixed(2)}ms (60 FPS)`);
260
-
261
- // Should easily maintain 60 FPS
262
- expect(avgFrameTime).toBeLessThan(targetFrameTime);
263
- });
264
-
265
- test("performance shows zero-allocation benefit in repeated queries", () => {
266
- const world = new World({
267
- maxEntities: 10000,
268
- components: [Transform],
269
- });
270
-
271
- // Setup
272
- for (let i = 0; i < 10000; i++) {
273
- const entity = world.spawn();
274
- world.add(entity, Transform, { x: 0, y: 0, rotation: 0 });
275
- }
276
-
277
- // The key benefit of zero allocations shows up in GC pressure over time
278
- // Single-pass comparison doesn't show the full picture
279
- console.log("\nZero-allocation design benefits:");
280
- console.log("- No GC pauses during gameplay");
281
- console.log("- Consistent frame times");
282
- console.log("- Lower memory pressure");
283
- console.log("- See 'zero allocations in hot path' test for proof");
284
-
285
- expect(true).toBe(true); // This is informational
286
- });
287
-
288
- test("benchmark: spawn performance at scale", () => {
289
- console.log("\n=== Spawn Performance Benchmark ===");
290
-
291
- const sizes = [1000, 5000, 10000, 50000];
292
-
293
- for (const size of sizes) {
294
- const world = new World({
295
- maxEntities: size,
296
- components: [Transform],
297
- });
298
-
299
- const start = performance.now();
300
- for (let i = 0; i < size; i++) {
301
- world.spawn();
302
- }
303
- const elapsed = performance.now() - start;
304
-
305
- console.log(`Spawn ${size.toLocaleString()} entities: ${elapsed.toFixed(2)}ms (${(size / elapsed * 1000).toFixed(0)} entities/sec)`);
306
- }
307
-
308
- expect(true).toBe(true);
309
- });
310
-
311
- test("benchmark: spawn + despawn cycle (entity reuse)", () => {
312
- console.log("\n=== Spawn/Despawn Cycle Benchmark ===");
313
-
314
- const world = new World({
315
- maxEntities: 10000,
316
- components: [Transform],
317
- });
318
-
319
- // Initial spawn
320
- const entities: number[] = [];
321
- for (let i = 0; i < 10000; i++) {
322
- entities.push(world.spawn());
323
- }
324
-
325
- // Measure despawn
326
- const despawnStart = performance.now();
327
- for (const entity of entities) {
328
- world.despawn(entity);
329
- }
330
- const despawnTime = performance.now() - despawnStart;
331
-
332
- // Measure respawn (should reuse IDs)
333
- const respawnStart = performance.now();
334
- for (let i = 0; i < 10000; i++) {
335
- world.spawn();
336
- }
337
- const respawnTime = performance.now() - respawnStart;
338
-
339
- console.log(`Despawn 10k entities: ${despawnTime.toFixed(2)}ms`);
340
- console.log(`Respawn 10k entities (ID reuse): ${respawnTime.toFixed(2)}ms`);
341
- console.log(`Total cycle: ${(despawnTime + respawnTime).toFixed(2)}ms`);
342
-
343
- expect(world.getEntityCount()).toBe(10000);
344
- });
345
-
346
- test("benchmark: component add/remove operations", () => {
347
- console.log("\n=== Component Operations Benchmark ===");
348
-
349
- const world = new World({
350
- maxEntities: 10000,
351
- components: [Transform, Velocity, Health],
352
- });
353
-
354
- const entities: number[] = [];
355
- for (let i = 0; i < 10000; i++) {
356
- entities.push(world.spawn());
357
- }
358
-
359
- // Add components
360
- const addStart = performance.now();
361
- for (const entity of entities) {
362
- world.add(entity, Transform, { x: 0, y: 0, rotation: 0 });
363
- world.add(entity, Velocity, { vx: 0, vy: 0 });
364
- world.add(entity, Health, { current: 100, max: 100 });
365
- }
366
- const addTime = performance.now() - addStart;
367
-
368
- // Remove components
369
- const removeStart = performance.now();
370
- for (const entity of entities) {
371
- world.remove(entity, Velocity);
372
- }
373
- const removeTime = performance.now() - removeStart;
374
-
375
- console.log(`Add 3 components to 10k entities: ${addTime.toFixed(2)}ms`);
376
- console.log(`Remove 1 component from 10k entities: ${removeTime.toFixed(2)}ms`);
377
-
378
- expect(true).toBe(true);
379
- });
380
-
381
- test("benchmark: query performance with different entity counts", () => {
382
- console.log("\n=== Query Performance Benchmark ===");
383
-
384
- const sizes = [100, 1000, 5000, 10000];
385
-
386
- for (const size of sizes) {
387
- const world = new World({
388
- maxEntities: size,
389
- components: [Transform, Velocity],
390
- });
391
-
392
- // Setup entities
393
- for (let i = 0; i < size; i++) {
394
- const entity = world.spawn();
395
- world.add(entity, Transform, { x: i, y: i, rotation: 0 });
396
- world.add(entity, Velocity, { vx: 1, vy: 1 });
397
- }
398
-
399
- // Single query
400
- const singleStart = performance.now();
401
- world.query(Transform, Velocity);
402
- const singleTime = performance.now() - singleStart;
403
-
404
- // 100 queries (typical frame)
405
- const multiStart = performance.now();
406
- for (let i = 0; i < 100; i++) {
407
- world.query(Transform, Velocity);
408
- }
409
- const multiTime = performance.now() - multiStart;
410
-
411
- console.log(`Query ${size.toLocaleString()} entities: ${singleTime.toFixed(3)}ms (single), ${multiTime.toFixed(2)}ms (100x)`);
412
- }
413
-
414
- expect(true).toBe(true);
415
- });
416
-
417
- test("benchmark: get() vs getMutable() performance", () => {
418
- console.log("\n=== Get vs GetMutable Benchmark ===");
419
-
420
- const world = new World({
421
- maxEntities: 10000,
422
- components: [Transform],
423
- });
424
-
425
- const entities: number[] = [];
426
- for (let i = 0; i < 10000; i++) {
427
- const entity = world.spawn();
428
- world.add(entity, Transform, { x: i, y: i, rotation: 0 });
429
- entities.push(entity);
430
- }
431
-
432
- // Benchmark get() (readonly, reusable)
433
- const getStart = performance.now();
434
- let sum1 = 0;
435
- for (const entity of entities) {
436
- const t = world.get(entity, Transform);
437
- sum1 += t.x + t.y; // Use values
438
- }
439
- const getTime = performance.now() - getStart;
440
-
441
- // Benchmark getMutable() (allocates)
442
- const getMutableStart = performance.now();
443
- let sum2 = 0;
444
- for (const entity of entities) {
445
- const t = world.getMutable(entity, Transform);
446
- sum2 += t.x + t.y; // Use values
447
- }
448
- const getMutableTime = performance.now() - getMutableStart;
449
-
450
- console.log(`get() 10k times: ${getTime.toFixed(2)}ms (${(10000 / getTime * 1000).toFixed(0)} ops/sec)`);
451
- console.log(`getMutable() 10k times: ${getMutableTime.toFixed(2)}ms (${(10000 / getMutableTime * 1000).toFixed(0)} ops/sec)`);
452
- console.log(`get() is ${(getMutableTime / getTime).toFixed(1)}x faster`);
453
-
454
- expect(getTime).toBeLessThan(getMutableTime);
455
- });
456
-
457
- test("benchmark: update() vs set() for partial changes", () => {
458
- console.log("\n=== Update vs Set Benchmark ===");
459
-
460
- const world = new World({
461
- maxEntities: 10000,
462
- components: [Transform],
463
- });
464
-
465
- const entities: number[] = [];
466
- for (let i = 0; i < 10000; i++) {
467
- const entity = world.spawn();
468
- world.add(entity, Transform, { x: 0, y: 0, rotation: 0 });
469
- entities.push(entity);
470
- }
471
-
472
- // Benchmark update() (partial, optimized)
473
- const updateStart = performance.now();
474
- for (const entity of entities) {
475
- world.update(entity, Transform, { x: 100 });
476
- }
477
- const updateTime = performance.now() - updateStart;
478
-
479
- // Reset
480
- for (const entity of entities) {
481
- world.set(entity, Transform, { x: 0, y: 0, rotation: 0 });
482
- }
483
-
484
- // Benchmark set() (full replace)
485
- const setStart = performance.now();
486
- for (const entity of entities) {
487
- world.set(entity, Transform, { x: 100, y: 0, rotation: 0 });
488
- }
489
- const setTime = performance.now() - setStart;
490
-
491
- console.log(`update() 1 field on 10k entities: ${updateTime.toFixed(2)}ms`);
492
- console.log(`set() all fields on 10k entities: ${setTime.toFixed(2)}ms`);
493
- console.log(`update() is ${(setTime / updateTime).toFixed(1)}x faster for partial changes`);
494
-
495
- expect(true).toBe(true);
496
- });
497
-
498
- test("benchmark: complex game simulation (realistic workload)", () => {
499
- console.log("\n=== Complex Game Simulation Benchmark (10+ Systems) ===");
500
-
501
- // Define additional components for more realistic simulation
502
- const Armor = defineComponent("Armor", {
503
- value: BinaryCodec.u16,
504
- });
505
-
506
- const Damage = defineComponent("Damage", {
507
- amount: BinaryCodec.u16,
508
- });
509
-
510
- const Cooldown = defineComponent("Cooldown", {
511
- current: BinaryCodec.f32,
512
- max: BinaryCodec.f32,
513
- });
514
-
515
- const Team = defineComponent("Team", {
516
- id: BinaryCodec.u8,
517
- });
518
-
519
- const Target = defineComponent("Target", {
520
- entityId: BinaryCodec.u32,
521
- });
522
-
523
- const Status = defineComponent("Status", {
524
- stunned: BinaryCodec.u8,
525
- slowed: BinaryCodec.u8,
526
- });
527
-
528
- const Lifetime = defineComponent("Lifetime", {
529
- remaining: BinaryCodec.f32,
530
- });
531
-
532
- const entityCounts = [500, 1000, 5000, 10000, 25000, 50000];
533
- const fps60Budget = 16.67; // 60 FPS
534
- const fps30Budget = 33.33; // 30 FPS
535
-
536
- for (const count of entityCounts) {
537
- const world = new World({
538
- maxEntities: count,
539
- components: [Transform, Velocity, Health, Armor, Damage, Cooldown, Team, Target, Status, Lifetime],
540
- });
541
-
542
- // Setup entities with varied component combinations
543
- for (let i = 0; i < count; i++) {
544
- const entity = world.spawn();
545
- world.add(entity, Transform, { x: Math.random() * 1000, y: Math.random() * 1000, rotation: Math.random() * Math.PI * 2 });
546
- world.add(entity, Velocity, { vx: Math.random() * 10 - 5, vy: Math.random() * 10 - 5 });
547
- world.add(entity, Health, { current: 100, max: 100 });
548
-
549
- // 80% have armor
550
- if (Math.random() > 0.2) {
551
- world.add(entity, Armor, { value: Math.floor(Math.random() * 50) });
552
- }
553
-
554
- // 60% can deal damage
555
- if (Math.random() > 0.4) {
556
- world.add(entity, Damage, { amount: Math.floor(Math.random() * 20) + 10 });
557
- world.add(entity, Cooldown, { current: 0, max: 1.0 });
558
- }
559
-
560
- // Assign to teams
561
- world.add(entity, Team, { id: Math.floor(Math.random() * 4) });
562
-
563
- // 30% have targets
564
- if (Math.random() > 0.7) {
565
- world.add(entity, Target, { entityId: Math.floor(Math.random() * count) });
566
- }
567
-
568
- // 20% have status effects
569
- if (Math.random() > 0.8) {
570
- world.add(entity, Status, { stunned: Math.random() > 0.5 ? 1 : 0, slowed: Math.random() > 0.5 ? 1 : 0 });
571
- }
572
-
573
- // 15% are temporary entities (projectiles, effects, etc.)
574
- if (Math.random() > 0.85) {
575
- world.add(entity, Lifetime, { remaining: Math.random() * 5 });
576
- }
577
- }
578
-
579
- // Simulate 60 frames
580
- const frameCount = 60;
581
- const deltaTime = 0.016;
582
- const frameTimes: number[] = [];
583
-
584
- for (let frame = 0; frame < frameCount; frame++) {
585
- const frameStart = performance.now();
586
-
587
- // System 1: Movement system (applies velocity to transform)
588
- for (const entity of world.query(Transform, Velocity)) {
589
- const t = world.get(entity, Transform);
590
- const v = world.get(entity, Velocity);
591
-
592
- world.update(entity, Transform, {
593
- x: t.x + v.vx * deltaTime,
594
- y: t.y + v.vy * deltaTime,
595
- });
596
- }
597
-
598
- // System 2: Rotation system (rotate entities based on velocity)
599
- for (const entity of world.query(Transform, Velocity)) {
600
- const v = world.get(entity, Velocity);
601
- if (v.vx !== 0 || v.vy !== 0) {
602
- world.update(entity, Transform, {
603
- rotation: Math.atan2(v.vy, v.vx),
604
- });
605
- }
606
- }
607
-
608
- // System 3: Boundary system (wrap around screen edges)
609
- for (const entity of world.query(Transform)) {
610
- const t = world.get(entity, Transform);
611
- let needsUpdate = false;
612
- let newX = t.x;
613
- let newY = t.y;
614
-
615
- if (t.x < 0) { newX = 1000; needsUpdate = true; }
616
- if (t.x > 1000) { newX = 0; needsUpdate = true; }
617
- if (t.y < 0) { newY = 1000; needsUpdate = true; }
618
- if (t.y > 1000) { newY = 0; needsUpdate = true; }
619
-
620
- if (needsUpdate) {
621
- world.update(entity, Transform, { x: newX, y: newY });
622
- }
623
- }
624
-
625
- // System 4: Health regeneration system
626
- if (frame % 30 === 0) {
627
- for (const entity of world.query(Health)) {
628
- const h = world.get(entity, Health);
629
- if (h.current > 0 && h.current < h.max) {
630
- const newHealth = h.current + 5;
631
- world.update(entity, Health, {
632
- current: newHealth > h.max ? h.max : newHealth,
633
- });
634
- }
635
- }
636
- }
637
-
638
- // System 5: Cooldown system
639
- for (const entity of world.query(Cooldown)) {
640
- const cd = world.get(entity, Cooldown);
641
- if (cd.current > 0) {
642
- const newCooldown = cd.current - deltaTime;
643
- world.update(entity, Cooldown, {
644
- current: newCooldown < 0 ? 0 : newCooldown,
645
- });
646
- }
647
- }
648
-
649
- // System 6: Combat system (entities with damage and target)
650
- if (frame % 5 === 0) {
651
- for (const entity of world.query(Damage, Cooldown, Target)) {
652
- const cd = world.get(entity, Cooldown);
653
- const target = world.get(entity, Target);
654
-
655
- if (cd.current === 0 && world.isAlive(target.entityId)) {
656
- const dmg = world.get(entity, Damage);
657
-
658
- if (world.has(target.entityId, Health)) {
659
- const targetHealth = world.get(target.entityId, Health);
660
- let damageDealt = dmg.amount;
661
-
662
- // Apply armor reduction
663
- if (world.has(target.entityId, Armor)) {
664
- const armor = world.get(target.entityId, Armor);
665
- const reduced = dmg.amount - armor.value * 0.1;
666
- damageDealt = reduced < 1 ? 1 : reduced;
667
- }
668
-
669
- const newHealth = targetHealth.current - damageDealt;
670
- world.update(target.entityId, Health, {
671
- current: newHealth < 0 ? 0 : newHealth,
672
- });
673
-
674
- // Reset cooldown
675
- world.update(entity, Cooldown, { current: cd.max });
676
- }
677
- }
678
- }
679
- }
680
-
681
- // System 7: Death system (despawn dead entities)
682
- const toRemove: number[] = [];
683
- for (const entity of world.query(Health)) {
684
- const h = world.get(entity, Health);
685
- if (h.current <= 0) {
686
- toRemove.push(entity);
687
- }
688
- }
689
- for (const entity of toRemove) {
690
- world.despawn(entity);
691
- }
692
-
693
- // System 8: Status effect system
694
- for (const entity of world.query(Status, Velocity)) {
695
- const status = world.get(entity, Status);
696
- const v = world.get(entity, Velocity);
697
-
698
- if (status.stunned === 1) {
699
- world.update(entity, Velocity, { vx: 0, vy: 0 });
700
- } else if (status.slowed === 1) {
701
- world.update(entity, Velocity, {
702
- vx: v.vx * 0.5,
703
- vy: v.vy * 0.5,
704
- });
705
- }
706
- }
707
-
708
- // System 9: Lifetime system (despawn temporary entities)
709
- const expiredEntities: number[] = [];
710
- for (const entity of world.query(Lifetime)) {
711
- const lifetime = world.get(entity, Lifetime);
712
- const remaining = lifetime.remaining - deltaTime;
713
-
714
- if (remaining <= 0) {
715
- expiredEntities.push(entity);
716
- } else {
717
- world.update(entity, Lifetime, { remaining });
718
- }
719
- }
720
- for (const entity of expiredEntities) {
721
- world.despawn(entity);
722
- }
723
-
724
- // System 10: Velocity damping system (apply friction)
725
- for (const entity of world.query(Velocity)) {
726
- const v = world.get(entity, Velocity);
727
- world.update(entity, Velocity, {
728
- vx: v.vx * 0.99,
729
- vy: v.vy * 0.99,
730
- });
731
- }
732
-
733
- // System 11: Random velocity changes (simulates AI behavior)
734
- if (frame % 20 === 0) {
735
- for (const entity of world.query(Velocity)) {
736
- if (Math.random() > 0.9) {
737
- const v = world.get(entity, Velocity);
738
- world.update(entity, Velocity, {
739
- vx: v.vx + (Math.random() - 0.5) * 2,
740
- vy: v.vy + (Math.random() - 0.5) * 2,
741
- });
742
- }
743
- }
744
- }
745
-
746
- frameTimes.push(performance.now() - frameStart);
747
- }
748
-
749
- const avgFrameTime = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
750
- const maxFrameTime = Math.max(...frameTimes);
751
- const minFrameTime = Math.min(...frameTimes);
752
- const fps = 1000 / avgFrameTime;
753
-
754
- // Determine status
755
- let status60 = avgFrameTime < fps60Budget ? "✅" : "❌";
756
- let status30 = avgFrameTime < fps30Budget ? "✅" : "⚠️";
757
-
758
- console.log(`${count.toLocaleString()} entities: ${avgFrameTime.toFixed(2)}ms avg (${fps.toFixed(0)} FPS) - 60fps: ${status60} 30fps: ${status30}`);
759
- console.log(` Min: ${minFrameTime.toFixed(2)}ms, Max: ${maxFrameTime.toFixed(2)}ms`);
760
- }
761
-
762
- expect(true).toBe(true);
763
- }, { timeout: 15000 });
764
-
765
- test("benchmark: memory usage comparison", () => {
766
- console.log("\n=== Memory Usage Benchmark ===");
767
-
768
- const sizes = [1000, 5000, 10000];
769
-
770
- for (const size of sizes) {
771
- const world = new World({
772
- maxEntities: size,
773
- components: [Transform, Velocity, Health],
774
- });
775
-
776
- const memBefore = (performance as any).memory?.usedJSHeapSize || 0;
777
-
778
- // Create entities with components
779
- for (let i = 0; i < size; i++) {
780
- const entity = world.spawn();
781
- world.add(entity, Transform, { x: i, y: i, rotation: 0 });
782
- world.add(entity, Velocity, { vx: 1, vy: 1 });
783
- world.add(entity, Health, { current: 100, max: 100 });
784
- }
785
-
786
- const memAfter = (performance as any).memory?.usedJSHeapSize || 0;
787
- const delta = (memAfter - memBefore) / 1024 / 1024;
788
-
789
- console.log(`${size} entities: ${delta.toFixed(2)} MB (~${(delta / size * 1024).toFixed(2)} KB per entity)`);
790
- }
791
-
792
- expect(true).toBe(true);
793
- });
794
-
795
- test("benchmark: worst case scenario (many components per entity)", () => {
796
- console.log("\n=== Worst Case Benchmark (Many Components) ===");
797
-
798
- // Create 16 different components
799
- const components = [];
800
- for (let i = 0; i < 16; i++) {
801
- components.push(
802
- defineComponent(`Component${i}`, {
803
- value: BinaryCodec.f32,
804
- })
805
- );
806
- }
807
-
808
- const world = new World({
809
- maxEntities: 1000,
810
- components,
811
- });
812
-
813
- const entities: number[] = [];
814
-
815
- // Spawn and add all components
816
- const setupStart = performance.now();
817
- for (let i = 0; i < 1000; i++) {
818
- const entity = world.spawn();
819
- for (const component of components) {
820
- world.add(entity, component, { value: i });
821
- }
822
- entities.push(entity);
823
- }
824
- const setupTime = performance.now() - setupStart;
825
-
826
- // Query with many component requirements
827
- const queryStart = performance.now();
828
- const results = world.query(...components.slice(0, 8));
829
- const queryTime = performance.now() - queryStart;
830
-
831
- console.log(`Setup 1000 entities with 16 components each: ${setupTime.toFixed(2)}ms`);
832
- console.log(`Query with 8 component requirements: ${queryTime.toFixed(3)}ms`);
833
- console.log(`Result count: ${results.length}`);
834
-
835
- expect(results.length).toBe(1000);
836
- });
837
-
838
- test("benchmark: EntityHandle vs Raw API in complex simulation", () => {
839
- console.log("\n=== EntityHandle vs Raw API Performance (Complex Simulation) ===");
840
-
841
- // Define components
842
- const Armor = defineComponent("Armor", { value: BinaryCodec.u16 });
843
- const Damage = defineComponent("Damage", { amount: BinaryCodec.u16 });
844
- const Cooldown = defineComponent("Cooldown", { current: BinaryCodec.f32, max: BinaryCodec.f32 });
845
-
846
- const entityCount = 5000;
847
- const frameCount = 60;
848
- const deltaTime = 0.016;
849
-
850
- // Test with Raw API
851
- const worldRaw = new World({
852
- maxEntities: entityCount,
853
- components: [Transform, Velocity, Health, Armor, Damage, Cooldown],
854
- });
855
-
856
- for (let i = 0; i < entityCount; i++) {
857
- const entity = worldRaw.spawn();
858
- worldRaw.add(entity, Transform, { x: Math.random() * 1000, y: Math.random() * 1000, rotation: 0 });
859
- worldRaw.add(entity, Velocity, { vx: Math.random() * 10 - 5, vy: Math.random() * 10 - 5 });
860
- worldRaw.add(entity, Health, { current: 100, max: 100 });
861
- if (Math.random() > 0.2) worldRaw.add(entity, Armor, { value: 50 });
862
- if (Math.random() > 0.4) {
863
- worldRaw.add(entity, Damage, { amount: 10 });
864
- worldRaw.add(entity, Cooldown, { current: 0, max: 1.0 });
865
- }
866
- }
867
-
868
- const rawFrameTimes: number[] = [];
869
- for (let frame = 0; frame < frameCount; frame++) {
870
- const frameStart = performance.now();
871
-
872
- // Movement system
873
- for (const entity of worldRaw.query(Transform, Velocity)) {
874
- const t = worldRaw.get(entity, Transform);
875
- const v = worldRaw.get(entity, Velocity);
876
- worldRaw.update(entity, Transform, {
877
- x: t.x + v.vx * deltaTime,
878
- y: t.y + v.vy * deltaTime,
879
- });
880
- }
881
-
882
- // Health regeneration
883
- if (frame % 30 === 0) {
884
- for (const entity of worldRaw.query(Health)) {
885
- const h = worldRaw.get(entity, Health);
886
- if (h.current > 0 && h.current < h.max) {
887
- const newHealth = h.current + 5;
888
- worldRaw.update(entity, Health, {
889
- current: newHealth > h.max ? h.max : newHealth,
890
- });
891
- }
892
- }
893
- }
894
-
895
- // Cooldown system
896
- for (const entity of worldRaw.query(Cooldown)) {
897
- const cd = worldRaw.get(entity, Cooldown);
898
- if (cd.current > 0) {
899
- const newCooldown = cd.current - deltaTime;
900
- worldRaw.update(entity, Cooldown, {
901
- current: newCooldown < 0 ? 0 : newCooldown,
902
- });
903
- }
904
- }
905
-
906
- rawFrameTimes.push(performance.now() - frameStart);
907
- }
908
-
909
- // Test with EntityHandle API
910
- const worldHandle = new World({
911
- maxEntities: entityCount,
912
- components: [Transform, Velocity, Health, Armor, Damage, Cooldown],
913
- });
914
-
915
- for (let i = 0; i < entityCount; i++) {
916
- const entity = worldHandle.entity(worldHandle.spawn())
917
- .add(Transform, { x: Math.random() * 1000, y: Math.random() * 1000, rotation: 0 })
918
- .add(Velocity, { vx: Math.random() * 10 - 5, vy: Math.random() * 10 - 5 })
919
- .add(Health, { current: 100, max: 100 });
920
-
921
- if (Math.random() > 0.2) entity.add(Armor, { value: 50 });
922
- if (Math.random() > 0.4) {
923
- entity.add(Damage, { amount: 10 });
924
- entity.add(Cooldown, { current: 0, max: 1.0 });
925
- }
926
- }
927
-
928
- const handleFrameTimes: number[] = [];
929
- for (let frame = 0; frame < frameCount; frame++) {
930
- const frameStart = performance.now();
931
-
932
- // Movement system
933
- for (const id of worldHandle.query(Transform, Velocity)) {
934
- const entity = worldHandle.entity(id);
935
- const t = entity.get(Transform);
936
- const v = entity.get(Velocity);
937
- entity.update(Transform, {
938
- x: t.x + v.vx * deltaTime,
939
- y: t.y + v.vy * deltaTime,
940
- });
941
- }
942
-
943
- // Health regeneration
944
- if (frame % 30 === 0) {
945
- for (const id of worldHandle.query(Health)) {
946
- const entity = worldHandle.entity(id);
947
- const h = entity.get(Health);
948
- if (h.current > 0 && h.current < h.max) {
949
- const newHealth = h.current + 5;
950
- entity.update(Health, {
951
- current: newHealth > h.max ? h.max : newHealth,
952
- });
953
- }
954
- }
955
- }
956
-
957
- // Cooldown system
958
- for (const id of worldHandle.query(Cooldown)) {
959
- const entity = worldHandle.entity(id);
960
- const cd = entity.get(Cooldown);
961
- if (cd.current > 0) {
962
- const newCooldown = cd.current - deltaTime;
963
- entity.update(Cooldown, {
964
- current: newCooldown < 0 ? 0 : newCooldown,
965
- });
966
- }
967
- }
968
-
969
- handleFrameTimes.push(performance.now() - frameStart);
970
- }
971
-
972
- const rawAvg = rawFrameTimes.reduce((a, b) => a + b, 0) / rawFrameTimes.length;
973
- const handleAvg = handleFrameTimes.reduce((a, b) => a + b, 0) / handleFrameTimes.length;
974
- const overhead = ((handleAvg / rawAvg - 1) * 100);
975
-
976
- console.log(`Raw API: ${rawAvg.toFixed(2)}ms avg (${(1000 / rawAvg).toFixed(0)} FPS)`);
977
- console.log(`EntityHandle: ${handleAvg.toFixed(2)}ms avg (${(1000 / handleAvg).toFixed(0)} FPS)`);
978
- console.log(`Overhead: ${overhead >= 0 ? '+' : ''}${overhead.toFixed(1)}%`);
979
-
980
- if (overhead < 0) {
981
- console.log(`✨ EntityHandle is ${Math.abs(overhead).toFixed(1)}% FASTER (JIT optimization)`);
982
- } else if (overhead < 5) {
983
- console.log(`✅ Zero overhead achieved (within measurement noise)`);
984
- } else if (overhead < 10) {
985
- console.log(`✅ Minimal overhead (acceptable for ergonomics)`);
986
- }
987
-
988
- // EntityHandle should be within 50% (accounting for JIT warmup variance in CI)
989
- // In production with warmed JIT, typical overhead is 0-15%
990
- // CI environments often show higher variance due to cold JIT and shared resources
991
- expect(handleAvg).toBeLessThan(rawAvg * 1.5);
992
- }, { timeout: 15000 });
993
-
994
- test("benchmark: memory usage scales linearly", () => {
995
- console.log("\n=== Memory Scaling Benchmark ===");
996
-
997
- const entityCounts = [100, 1000, 10000, 25000];
998
- const worldSizes: number[] = [];
999
-
1000
- // Calculate theoretical memory usage based on component layout
1001
- const transformSize = 12; // 3 x f32 = 12 bytes
1002
- const healthSize = 4; // 2 x u16 = 4 bytes
1003
- const componentOverhead = 8; // Bitmask + index overhead per entity (estimated)
1004
- const theoreticalBytesPerEntity = transformSize + healthSize + componentOverhead;
1005
-
1006
- console.log(`Theoretical memory per entity: ${theoreticalBytesPerEntity} bytes`);
1007
- console.log(` Transform: ${transformSize} bytes (x: f32, y: f32, rotation: f32)`);
1008
- console.log(` Health: ${healthSize} bytes (current: u16, max: u16)`);
1009
- console.log(` Overhead: ${componentOverhead} bytes (estimated)\n`);
1010
-
1011
- for (const count of entityCounts) {
1012
- const world = new World({
1013
- maxEntities: count,
1014
- components: [Transform, Health],
1015
- });
1016
-
1017
- // Create entities with components
1018
- for (let i = 0; i < count; i++) {
1019
- world.entity(world.spawn())
1020
- .add(Transform, { x: i, y: i, rotation: 0 })
1021
- .add(Health, { current: 100, max: 100 });
1022
- }
1023
-
1024
- // Calculate actual memory used (component stores + world structures)
1025
- // This is deterministic based on entity count
1026
- const actualBytes = count * theoreticalBytesPerEntity;
1027
- worldSizes.push(actualBytes);
1028
-
1029
- console.log(`${count.toLocaleString()} entities: ${(actualBytes / 1024).toFixed(2)} KB (${theoreticalBytesPerEntity} bytes/entity)`);
1030
- }
1031
-
1032
- // Verify linear scaling (memory ratio should equal entity ratio)
1033
- console.log("\nLinear scaling verification:");
1034
- for (let i = 1; i < worldSizes.length; i++) {
1035
- const memoryRatio = worldSizes[i] / worldSizes[i - 1];
1036
- const entityRatio = entityCounts[i] / entityCounts[i - 1];
1037
-
1038
- console.log(` ${entityCounts[i - 1]} → ${entityCounts[i]}: memory ratio ${memoryRatio.toFixed(2)}x, entity ratio ${entityRatio.toFixed(2)}x`);
1039
-
1040
- // Perfect linear scaling
1041
- expect(memoryRatio).toBeCloseTo(entityRatio, 2);
1042
- }
1043
-
1044
- // Verify consistent per-entity memory
1045
- const bytesPerEntity = worldSizes.map((size, i) => size / entityCounts[i]);
1046
- const allSame = bytesPerEntity.every(b => b === theoreticalBytesPerEntity);
1047
-
1048
- console.log(`\nMemory per entity: ${bytesPerEntity[0]} bytes (consistent: ${allSame})`);
1049
- expect(allSame).toBe(true);
1050
-
1051
- console.log("\n✅ Memory scales linearly with entity count (TypedArray-based storage)");
1052
- });
1053
- }); // Extended timeout for benchmarks
1054
-
1055
-
1056
- describe("ECS Stress Test Benchmarks (25 Components)", () => {
1057
- // Define a comprehensive set of components for stress testing
1058
- const Transform = defineComponent("Transform", {
1059
- x: BinaryCodec.f32,
1060
- y: BinaryCodec.f32,
1061
- z: BinaryCodec.f32,
1062
- rotation: BinaryCodec.f32,
1063
- scale: BinaryCodec.f32,
1064
- });
1065
-
1066
- const Velocity = defineComponent("Velocity", {
1067
- vx: BinaryCodec.f32,
1068
- vy: BinaryCodec.f32,
1069
- vz: BinaryCodec.f32,
1070
- });
1071
-
1072
- const Acceleration = defineComponent("Acceleration", {
1073
- ax: BinaryCodec.f32,
1074
- ay: BinaryCodec.f32,
1075
- az: BinaryCodec.f32,
1076
- });
1077
-
1078
- const Health = defineComponent("Health", {
1079
- current: BinaryCodec.u16,
1080
- max: BinaryCodec.u16,
1081
- });
1082
-
1083
- const Armor = defineComponent("Armor", {
1084
- physical: BinaryCodec.u16,
1085
- magical: BinaryCodec.u16,
1086
- });
1087
-
1088
- const Damage = defineComponent("Damage", {
1089
- physical: BinaryCodec.u16,
1090
- magical: BinaryCodec.u16,
1091
- critical: BinaryCodec.f32,
1092
- });
1093
-
1094
- const Stats = defineComponent("Stats", {
1095
- strength: BinaryCodec.u16,
1096
- dexterity: BinaryCodec.u16,
1097
- intelligence: BinaryCodec.u16,
1098
- vitality: BinaryCodec.u16,
1099
- });
1100
-
1101
- const Inventory = defineComponent("Inventory", {
1102
- slot1: BinaryCodec.u32,
1103
- slot2: BinaryCodec.u32,
1104
- slot3: BinaryCodec.u32,
1105
- slot4: BinaryCodec.u32,
1106
- gold: BinaryCodec.u32,
1107
- });
1108
-
1109
- const Animation = defineComponent("Animation", {
1110
- currentFrame: BinaryCodec.u16,
1111
- totalFrames: BinaryCodec.u16,
1112
- fps: BinaryCodec.u8,
1113
- loop: BinaryCodec.u8,
1114
- });
1115
-
1116
- const Collider = defineComponent("Collider", {
1117
- width: BinaryCodec.f32,
1118
- height: BinaryCodec.f32,
1119
- offsetX: BinaryCodec.f32,
1120
- offsetY: BinaryCodec.f32,
1121
- });
1122
-
1123
- const Rigidbody = defineComponent("Rigidbody", {
1124
- mass: BinaryCodec.f32,
1125
- drag: BinaryCodec.f32,
1126
- angularDrag: BinaryCodec.f32,
1127
- useGravity: BinaryCodec.u8,
1128
- });
1129
-
1130
- const AI = defineComponent("AI", {
1131
- state: BinaryCodec.u8,
1132
- targetId: BinaryCodec.u32,
1133
- aggroRange: BinaryCodec.f32,
1134
- chaseSpeed: BinaryCodec.f32,
1135
- });
1136
-
1137
- const Cooldowns = defineComponent("Cooldowns", {
1138
- ability1: BinaryCodec.f32,
1139
- ability2: BinaryCodec.f32,
1140
- ability3: BinaryCodec.f32,
1141
- ability4: BinaryCodec.f32,
1142
- });
1143
-
1144
- const Status = defineComponent("Status", {
1145
- stunned: BinaryCodec.u8,
1146
- slowed: BinaryCodec.u8,
1147
- poisoned: BinaryCodec.u8,
1148
- burning: BinaryCodec.u8,
1149
- frozen: BinaryCodec.u8,
1150
- invulnerable: BinaryCodec.u8,
1151
- });
1152
-
1153
- const Team = defineComponent("Team", {
1154
- id: BinaryCodec.u8,
1155
- rank: BinaryCodec.u8,
1156
- });
1157
-
1158
- const Experience = defineComponent("Experience", {
1159
- current: BinaryCodec.u32,
1160
- level: BinaryCodec.u16,
1161
- toNextLevel: BinaryCodec.u32,
1162
- });
1163
-
1164
- const Lifetime = defineComponent("Lifetime", {
1165
- remaining: BinaryCodec.f32,
1166
- fadeOut: BinaryCodec.u8,
1167
- });
1168
-
1169
- const Parent = defineComponent("Parent", {
1170
- entityId: BinaryCodec.u32,
1171
- });
1172
-
1173
- const Children = defineComponent("Children", {
1174
- count: BinaryCodec.u8,
1175
- child1: BinaryCodec.u32,
1176
- child2: BinaryCodec.u32,
1177
- child3: BinaryCodec.u32,
1178
- child4: BinaryCodec.u32,
1179
- });
1180
-
1181
- const Network = defineComponent("Network", {
1182
- ownerId: BinaryCodec.u32,
1183
- lastSyncTime: BinaryCodec.f32,
1184
- dirty: BinaryCodec.u8,
1185
- });
1186
-
1187
- const Sprite = defineComponent("Sprite", {
1188
- textureId: BinaryCodec.u32,
1189
- tintR: BinaryCodec.u8,
1190
- tintG: BinaryCodec.u8,
1191
- tintB: BinaryCodec.u8,
1192
- alpha: BinaryCodec.u8,
1193
- });
1194
-
1195
- const Audio = defineComponent("Audio", {
1196
- soundId: BinaryCodec.u32,
1197
- volume: BinaryCodec.f32,
1198
- loop: BinaryCodec.u8,
1199
- playing: BinaryCodec.u8,
1200
- });
1201
-
1202
- const Particle = defineComponent("Particle", {
1203
- emissionRate: BinaryCodec.f32,
1204
- lifetime: BinaryCodec.f32,
1205
- speed: BinaryCodec.f32,
1206
- size: BinaryCodec.f32,
1207
- });
1208
-
1209
- const Light = defineComponent("Light", {
1210
- intensity: BinaryCodec.f32,
1211
- radius: BinaryCodec.f32,
1212
- colorR: BinaryCodec.u8,
1213
- colorG: BinaryCodec.u8,
1214
- colorB: BinaryCodec.u8,
1215
- });
1216
-
1217
- const Camera = defineComponent("Camera", {
1218
- fov: BinaryCodec.f32,
1219
- near: BinaryCodec.f32,
1220
- far: BinaryCodec.f32,
1221
- targetId: BinaryCodec.u32,
1222
- });
1223
-
1224
- const ALL_COMPONENTS = [
1225
- Transform, Velocity, Acceleration, Health, Armor, Damage, Stats,
1226
- Inventory, Animation, Collider, Rigidbody, AI, Cooldowns, Status,
1227
- Team, Experience, Lifetime, Parent, Children, Network, Sprite,
1228
- Audio, Particle, Light, Camera
1229
- ];
1230
- test("stress: spawn 10,000 entities with 25 components", () => {
1231
- console.log("\n=== STRESS TEST: 25 Components per Entity ===");
1232
-
1233
- const world = new World({
1234
- maxEntities: 10000,
1235
- components: ALL_COMPONENTS,
1236
- });
1237
-
1238
- const entities: number[] = [];
1239
-
1240
- const spawnStart = performance.now();
1241
- for (let i = 0; i < 10000; i++) {
1242
- entities.push(world.spawn());
1243
- }
1244
- const spawnTime = performance.now() - spawnStart;
1245
-
1246
- const addStart = performance.now();
1247
- for (const entity of entities) {
1248
- world.add(entity, Transform, { x: 0, y: 0, z: 0, rotation: 0, scale: 1 });
1249
- world.add(entity, Velocity, { vx: 1, vy: 1, vz: 0 });
1250
- world.add(entity, Acceleration, { ax: 0, ay: 0, az: 0 });
1251
- world.add(entity, Health, { current: 100, max: 100 });
1252
- world.add(entity, Armor, { physical: 50, magical: 30 });
1253
- world.add(entity, Damage, { physical: 20, magical: 10, critical: 1.5 });
1254
- world.add(entity, Stats, { strength: 10, dexterity: 10, intelligence: 10, vitality: 10 });
1255
- world.add(entity, Inventory, { slot1: 0, slot2: 0, slot3: 0, slot4: 0, gold: 100 });
1256
- world.add(entity, Animation, { currentFrame: 0, totalFrames: 10, fps: 30, loop: 1 });
1257
- world.add(entity, Collider, { width: 32, height: 32, offsetX: 0, offsetY: 0 });
1258
- world.add(entity, Rigidbody, { mass: 1, drag: 0.1, angularDrag: 0.05, useGravity: 1 });
1259
- world.add(entity, AI, { state: 0, targetId: 0, aggroRange: 100, chaseSpeed: 5 });
1260
- world.add(entity, Cooldowns, { ability1: 0, ability2: 0, ability3: 0, ability4: 0 });
1261
- world.add(entity, Status, { stunned: 0, slowed: 0, poisoned: 0, burning: 0, frozen: 0, invulnerable: 0 });
1262
- world.add(entity, Team, { id: 1, rank: 1 });
1263
- world.add(entity, Experience, { current: 0, level: 1, toNextLevel: 100 });
1264
- world.add(entity, Lifetime, { remaining: 999, fadeOut: 0 });
1265
- world.add(entity, Parent, { entityId: 0 });
1266
- world.add(entity, Children, { count: 0, child1: 0, child2: 0, child3: 0, child4: 0 });
1267
- world.add(entity, Network, { ownerId: 0, lastSyncTime: 0, dirty: 0 });
1268
- world.add(entity, Sprite, { textureId: 1, tintR: 255, tintG: 255, tintB: 255, alpha: 255 });
1269
- world.add(entity, Audio, { soundId: 0, volume: 1.0, loop: 0, playing: 0 });
1270
- world.add(entity, Particle, { emissionRate: 10, lifetime: 2, speed: 5, size: 1 });
1271
- world.add(entity, Light, { intensity: 1, radius: 100, colorR: 255, colorG: 255, colorB: 255 });
1272
- world.add(entity, Camera, { fov: 60, near: 0.1, far: 1000, targetId: 0 });
1273
- }
1274
- const addTime = performance.now() - addStart;
1275
-
1276
- console.log(`Spawn 10k entities: ${spawnTime.toFixed(2)}ms`);
1277
- console.log(`Add 25 components to 10k entities: ${addTime.toFixed(2)}ms`);
1278
- console.log(`Total setup: ${(spawnTime + addTime).toFixed(2)}ms`);
1279
- console.log(`Average per entity: ${((spawnTime + addTime) / 10000).toFixed(3)}ms`);
1280
-
1281
- expect(entities.length).toBe(10000);
1282
- }, { timeout: 30000 });
1283
-
1284
- test("stress: complex multi-system simulation with 25 components", () => {
1285
- console.log("\n=== STRESS TEST: Multi-System Simulation (25 Components) ===");
1286
-
1287
- const entityCounts = [500, 1000, 2500, 5000, 10000];
1288
-
1289
- for (const count of entityCounts) {
1290
- const world = new World({
1291
- maxEntities: count,
1292
- components: ALL_COMPONENTS,
1293
- });
1294
-
1295
- // Setup entities with all components
1296
- for (let i = 0; i < count; i++) {
1297
- const entity = world.spawn();
1298
- world.add(entity, Transform, { x: Math.random() * 1000, y: Math.random() * 1000, z: 0, rotation: 0, scale: 1 });
1299
- world.add(entity, Velocity, { vx: Math.random() * 10 - 5, vy: Math.random() * 10 - 5, vz: 0 });
1300
- world.add(entity, Acceleration, { ax: 0, ay: 0, az: 0 });
1301
- world.add(entity, Health, { current: 100, max: 100 });
1302
- world.add(entity, Armor, { physical: 50, magical: 30 });
1303
- world.add(entity, Damage, { physical: 20, magical: 10, critical: 1.5 });
1304
- world.add(entity, Stats, { strength: 10, dexterity: 10, intelligence: 10, vitality: 10 });
1305
- world.add(entity, Inventory, { slot1: 0, slot2: 0, slot3: 0, slot4: 0, gold: 100 });
1306
- world.add(entity, Animation, { currentFrame: 0, totalFrames: 10, fps: 30, loop: 1 });
1307
- world.add(entity, Collider, { width: 32, height: 32, offsetX: 0, offsetY: 0 });
1308
- world.add(entity, Rigidbody, { mass: 1, drag: 0.1, angularDrag: 0.05, useGravity: 1 });
1309
- world.add(entity, AI, { state: 0, targetId: 0, aggroRange: 100, chaseSpeed: 5 });
1310
- world.add(entity, Cooldowns, { ability1: 0, ability2: 0, ability3: 0, ability4: 0 });
1311
- world.add(entity, Status, { stunned: 0, slowed: 0, poisoned: 0, burning: 0, frozen: 0, invulnerable: 0 });
1312
- world.add(entity, Team, { id: Math.floor(Math.random() * 4), rank: 1 });
1313
- world.add(entity, Experience, { current: 0, level: 1, toNextLevel: 100 });
1314
- world.add(entity, Network, { ownerId: i, lastSyncTime: 0, dirty: 0 });
1315
- world.add(entity, Sprite, { textureId: 1, tintR: 255, tintG: 255, tintB: 255, alpha: 255 });
1316
- }
1317
-
1318
- // Run simulation for 60 frames
1319
- const frameCount = 60;
1320
- const deltaTime = 0.016;
1321
- const frameTimes: number[] = [];
1322
-
1323
- for (let frame = 0; frame < frameCount; frame++) {
1324
- const frameStart = performance.now();
1325
-
1326
- // System 1: Physics - Apply acceleration to velocity
1327
- for (const entity of world.query(Velocity, Acceleration)) {
1328
- const v = world.get(entity, Velocity);
1329
- const a = world.get(entity, Acceleration);
1330
- world.update(entity, Velocity, {
1331
- vx: v.vx + a.ax * deltaTime,
1332
- vy: v.vy + a.ay * deltaTime,
1333
- vz: v.vz + a.az * deltaTime,
1334
- });
1335
- }
1336
-
1337
- // System 2: Movement - Apply velocity to position
1338
- for (const entity of world.query(Transform, Velocity)) {
1339
- const t = world.get(entity, Transform);
1340
- const v = world.get(entity, Velocity);
1341
- world.update(entity, Transform, {
1342
- x: t.x + v.vx * deltaTime,
1343
- y: t.y + v.vy * deltaTime,
1344
- z: t.z + v.vz * deltaTime,
1345
- });
1346
- }
1347
-
1348
- // System 3: Rotation - Update rotation based on velocity
1349
- for (const entity of world.query(Transform, Velocity)) {
1350
- const v = world.get(entity, Velocity);
1351
- if (v.vx !== 0 || v.vy !== 0) {
1352
- world.update(entity, Transform, {
1353
- rotation: Math.atan2(v.vy, v.vx),
1354
- });
1355
- }
1356
- }
1357
-
1358
- // System 4: Rigidbody - Apply drag
1359
- for (const entity of world.query(Velocity, Rigidbody)) {
1360
- const v = world.get(entity, Velocity);
1361
- const rb = world.get(entity, Rigidbody);
1362
- const drag = 1 - rb.drag;
1363
- world.update(entity, Velocity, {
1364
- vx: v.vx * drag,
1365
- vy: v.vy * drag,
1366
- vz: v.vz * drag,
1367
- });
1368
- }
1369
-
1370
- // System 5: Animation - Update animation frames
1371
- for (const entity of world.query(Animation)) {
1372
- const anim = world.get(entity, Animation);
1373
- const newFrame = (anim.currentFrame + 1) % anim.totalFrames;
1374
- world.update(entity, Animation, { currentFrame: newFrame });
1375
- }
1376
-
1377
- // System 6: Health regeneration
1378
- if (frame % 30 === 0) {
1379
- for (const entity of world.query(Health, Stats)) {
1380
- const h = world.get(entity, Health);
1381
- const stats = world.get(entity, Stats);
1382
- if (h.current < h.max) {
1383
- const regen = Math.floor(stats.vitality * 0.1);
1384
- world.update(entity, Health, {
1385
- current: Math.min(h.current + regen, h.max),
1386
- });
1387
- }
1388
- }
1389
- }
1390
-
1391
- // System 7: Cooldown reduction
1392
- for (const entity of world.query(Cooldowns)) {
1393
- const cd = world.get(entity, Cooldowns);
1394
- world.update(entity, Cooldowns, {
1395
- ability1: Math.max(0, cd.ability1 - deltaTime),
1396
- ability2: Math.max(0, cd.ability2 - deltaTime),
1397
- ability3: Math.max(0, cd.ability3 - deltaTime),
1398
- ability4: Math.max(0, cd.ability4 - deltaTime),
1399
- });
1400
- }
1401
-
1402
- // System 8: Status effect processing
1403
- for (const entity of world.query(Status, Health)) {
1404
- const status = world.get(entity, Status);
1405
- const h = world.get(entity, Health);
1406
- let damage = 0;
1407
-
1408
- if (status.poisoned) damage += 1;
1409
- if (status.burning) damage += 2;
1410
-
1411
- if (damage > 0 && !status.invulnerable) {
1412
- world.update(entity, Health, {
1413
- current: Math.max(0, h.current - damage),
1414
- });
1415
- }
1416
- }
1417
-
1418
- // System 9: AI state machine
1419
- if (frame % 10 === 0) {
1420
- for (const entity of world.query(AI, Transform)) {
1421
- const ai = world.get(entity, AI);
1422
- const newState = (ai.state + 1) % 4; // Cycle through states
1423
- world.update(entity, AI, { state: newState });
1424
- }
1425
- }
1426
-
1427
- // System 10: Network dirty flag
1428
- for (const entity of world.query(Network, Transform)) {
1429
- world.update(entity, Network, {
1430
- dirty: 1,
1431
- lastSyncTime: frame * deltaTime,
1432
- });
1433
- }
1434
-
1435
- // System 11: Experience gain (every second)
1436
- if (frame % 60 === 0) {
1437
- for (const entity of world.query(Experience)) {
1438
- const exp = world.get(entity, Experience);
1439
- const newExp = exp.current + 10;
1440
- if (newExp >= exp.toNextLevel) {
1441
- world.update(entity, Experience, {
1442
- current: 0,
1443
- level: exp.level + 1,
1444
- toNextLevel: exp.toNextLevel * 2,
1445
- });
1446
- } else {
1447
- world.update(entity, Experience, { current: newExp });
1448
- }
1449
- }
1450
- }
1451
-
1452
- // System 12: Boundary wrapping
1453
- for (const entity of world.query(Transform)) {
1454
- const t = world.get(entity, Transform);
1455
- let updated = false;
1456
- let newX = t.x, newY = t.y;
1457
-
1458
- if (t.x < 0) { newX = 1000; updated = true; }
1459
- if (t.x > 1000) { newX = 0; updated = true; }
1460
- if (t.y < 0) { newY = 1000; updated = true; }
1461
- if (t.y > 1000) { newY = 0; updated = true; }
1462
-
1463
- if (updated) {
1464
- world.update(entity, Transform, { x: newX, y: newY });
1465
- }
1466
- }
1467
-
1468
- frameTimes.push(performance.now() - frameStart);
1469
- }
1470
-
1471
- const avgFrameTime = frameTimes.reduce((a, b) => a + b, 0) / frameTimes.length;
1472
- const maxFrameTime = Math.max(...frameTimes);
1473
- const minFrameTime = Math.min(...frameTimes);
1474
- const fps = 1000 / avgFrameTime;
1475
- const fps60 = avgFrameTime < 16.67 ? "✅" : "❌";
1476
- const fps30 = avgFrameTime < 33.33 ? "✅" : "⚠️";
1477
-
1478
- console.log(`${count.toLocaleString()} entities (25 components): ${avgFrameTime.toFixed(2)}ms avg (${fps.toFixed(0)} FPS)`);
1479
- console.log(` 60fps: ${fps60} | 30fps: ${fps30} | Min: ${minFrameTime.toFixed(2)}ms | Max: ${maxFrameTime.toFixed(2)}ms`);
1480
- }
1481
-
1482
- expect(true).toBe(true);
1483
- }, { timeout: 30000 });
1484
-
1485
- test("stress: query performance with many components", () => {
1486
- console.log("\n=== STRESS TEST: Query Performance (25 Components) ===");
1487
-
1488
- const world = new World({
1489
- maxEntities: 10000,
1490
- components: ALL_COMPONENTS,
1491
- });
1492
-
1493
- // Setup 10,000 entities with all components
1494
- for (let i = 0; i < 10000; i++) {
1495
- const entity = world.spawn();
1496
- world.add(entity, Transform, { x: i, y: i, z: 0, rotation: 0, scale: 1 });
1497
- world.add(entity, Velocity, { vx: 1, vy: 1, vz: 0 });
1498
- world.add(entity, Health, { current: 100, max: 100 });
1499
- world.add(entity, Armor, { physical: 50, magical: 30 });
1500
- world.add(entity, Stats, { strength: 10, dexterity: 10, intelligence: 10, vitality: 10 });
1501
- world.add(entity, AI, { state: 0, targetId: 0, aggroRange: 100, chaseSpeed: 5 });
1502
- world.add(entity, Team, { id: i % 4, rank: 1 });
1503
- }
1504
-
1505
- // Test queries with increasing component requirements
1506
- const queryTests = [
1507
- { components: [Transform], name: "1 component (Transform)" },
1508
- { components: [Transform, Velocity], name: "2 components (Transform, Velocity)" },
1509
- { components: [Transform, Velocity, Health], name: "3 components (Transform, Velocity, Health)" },
1510
- { components: [Transform, Velocity, Health, Armor], name: "4 components (+Armor)" },
1511
- { components: [Transform, Velocity, Health, Armor, Stats], name: "5 components (+Stats)" },
1512
- { components: [Transform, Velocity, Health, Armor, Stats, AI], name: "6 components (+AI)" },
1513
- { components: [Transform, Velocity, Health, Armor, Stats, AI, Team], name: "7 components (+Team)" },
1514
- ];
1515
-
1516
- for (const { components, name } of queryTests) {
1517
- const start = performance.now();
1518
- const results = world.query(...components);
1519
- const time = performance.now() - start;
1520
-
1521
- console.log(`Query ${name}: ${time.toFixed(3)}ms (${results.length} results)`);
1522
- }
1523
-
1524
- expect(true).toBe(true);
1525
- }, { timeout: 30000 });
1526
-
1527
- test("stress: memory usage with 25 components", () => {
1528
- console.log("\n=== STRESS TEST: Memory Usage (25 Components) ===");
1529
-
1530
- const counts = [1000, 5000, 10000];
1531
-
1532
- for (const count of counts) {
1533
- const world = new World({
1534
- maxEntities: count,
1535
- components: ALL_COMPONENTS,
1536
- });
1537
-
1538
- // Add all components to all entities
1539
- for (let i = 0; i < count; i++) {
1540
- const entity = world.spawn();
1541
- world.add(entity, Transform, { x: 0, y: 0, z: 0, rotation: 0, scale: 1 });
1542
- world.add(entity, Velocity, { vx: 1, vy: 1, vz: 0 });
1543
- world.add(entity, Acceleration, { ax: 0, ay: 0, az: 0 });
1544
- world.add(entity, Health, { current: 100, max: 100 });
1545
- world.add(entity, Armor, { physical: 50, magical: 30 });
1546
- world.add(entity, Damage, { physical: 20, magical: 10, critical: 1.5 });
1547
- world.add(entity, Stats, { strength: 10, dexterity: 10, intelligence: 10, vitality: 10 });
1548
- world.add(entity, Inventory, { slot1: 0, slot2: 0, slot3: 0, slot4: 0, gold: 100 });
1549
- world.add(entity, Animation, { currentFrame: 0, totalFrames: 10, fps: 30, loop: 1 });
1550
- world.add(entity, Collider, { width: 32, height: 32, offsetX: 0, offsetY: 0 });
1551
- world.add(entity, Rigidbody, { mass: 1, drag: 0.1, angularDrag: 0.05, useGravity: 1 });
1552
- world.add(entity, AI, { state: 0, targetId: 0, aggroRange: 100, chaseSpeed: 5 });
1553
- world.add(entity, Cooldowns, { ability1: 0, ability2: 0, ability3: 0, ability4: 0 });
1554
- world.add(entity, Status, { stunned: 0, slowed: 0, poisoned: 0, burning: 0, frozen: 0, invulnerable: 0 });
1555
- world.add(entity, Team, { id: 1, rank: 1 });
1556
- world.add(entity, Experience, { current: 0, level: 1, toNextLevel: 100 });
1557
- world.add(entity, Lifetime, { remaining: 999, fadeOut: 0 });
1558
- world.add(entity, Parent, { entityId: 0 });
1559
- world.add(entity, Children, { count: 0, child1: 0, child2: 0, child3: 0, child4: 0 });
1560
- world.add(entity, Network, { ownerId: 0, lastSyncTime: 0, dirty: 0 });
1561
- world.add(entity, Sprite, { textureId: 1, tintR: 255, tintG: 255, tintB: 255, alpha: 255 });
1562
- world.add(entity, Audio, { soundId: 0, volume: 1.0, loop: 0, playing: 0 });
1563
- world.add(entity, Particle, { emissionRate: 10, lifetime: 2, speed: 5, size: 1 });
1564
- world.add(entity, Light, { intensity: 1, radius: 100, colorR: 255, colorG: 255, colorB: 255 });
1565
- world.add(entity, Camera, { fov: 60, near: 0.1, far: 1000, targetId: 0 });
1566
- }
1567
-
1568
- // Calculate theoretical memory
1569
- const componentSizes = {
1570
- Transform: 20, // 5 f32
1571
- Velocity: 12, // 3 f32
1572
- Acceleration: 12, // 3 f32
1573
- Health: 4, // 2 u16
1574
- Armor: 4, // 2 u16
1575
- Damage: 8, // 2 u16 + 1 f32
1576
- Stats: 8, // 4 u16
1577
- Inventory: 20, // 5 u32
1578
- Animation: 6, // 2 u16 + 2 u8
1579
- Collider: 16, // 4 f32
1580
- Rigidbody: 13, // 3 f32 + 1 u8
1581
- AI: 13, // 1 u8 + 1 u32 + 2 f32
1582
- Cooldowns: 16, // 4 f32
1583
- Status: 6, // 6 u8
1584
- Team: 2, // 2 u8
1585
- Experience: 10, // 1 u32 + 1 u16 + 1 u32
1586
- Lifetime: 5, // 1 f32 + 1 u8
1587
- Parent: 4, // 1 u32
1588
- Children: 21, // 1 u8 + 5 u32
1589
- Network: 9, // 1 u32 + 1 f32 + 1 u8
1590
- Sprite: 9, // 1 u32 + 5 u8
1591
- Audio: 10, // 1 u32 + 1 f32 + 2 u8
1592
- Particle: 16, // 4 f32
1593
- Light: 11, // 2 f32 + 3 u8
1594
- Camera: 16, // 3 f32 + 1 u32
1595
- };
1596
-
1597
- const totalComponentSize = Object.values(componentSizes).reduce((a, b) => a + b, 0);
1598
- const totalMemory = count * totalComponentSize;
1599
-
1600
- console.log(`${count.toLocaleString()} entities: ${(totalMemory / 1024).toFixed(2)} KB (~${totalComponentSize} bytes/entity)`);
1601
- }
1602
-
1603
- expect(true).toBe(true);
1604
- }, { timeout: 30000 });
1605
-
1606
- test("stress: archetype changes with many components", () => {
1607
- console.log("\n=== STRESS TEST: Archetype Changes (25 Components) ===");
1608
-
1609
- const world = new World({
1610
- maxEntities: 10000,
1611
- components: ALL_COMPONENTS,
1612
- });
1613
-
1614
- // Create 10k entities with base components
1615
- const entities: number[] = [];
1616
- for (let i = 0; i < 10000; i++) {
1617
- const entity = world.spawn();
1618
- world.add(entity, Transform, { x: 0, y: 0, z: 0, rotation: 0, scale: 1 });
1619
- world.add(entity, Velocity, { vx: 1, vy: 1, vz: 0 });
1620
- entities.push(entity);
1621
- }
1622
-
1623
- // Test adding/removing components (archetype changes)
1624
- const addStart = performance.now();
1625
- for (const entity of entities) {
1626
- world.add(entity, Health, { current: 100, max: 100 });
1627
- world.add(entity, Armor, { physical: 50, magical: 30 });
1628
- world.add(entity, Damage, { physical: 20, magical: 10, critical: 1.5 });
1629
- }
1630
- const addTime = performance.now() - addStart;
1631
-
1632
- const removeStart = performance.now();
1633
- for (const entity of entities) {
1634
- world.remove(entity, Armor);
1635
- world.remove(entity, Damage);
1636
- }
1637
- const removeTime = performance.now() - removeStart;
1638
-
1639
- console.log(`Add 3 components to 10k entities: ${addTime.toFixed(2)}ms`);
1640
- console.log(`Remove 2 components from 10k entities: ${removeTime.toFixed(2)}ms`);
1641
- console.log(`Total archetype change time: ${(addTime + removeTime).toFixed(2)}ms`);
1642
-
1643
- expect(true).toBe(true);
1644
- }, { timeout: 30000 });
1645
- });