@vgai/engine 0.5.0 → 0.5.2

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 (538) hide show
  1. package/package.json +1 -1
  2. package/schemas/engine-capabilities.json +4 -4
  3. package/schemas/vgai-game.schema.json +2 -2
  4. package/src/adapter/authoring.ts +42 -0
  5. package/src/adapter/index.ts +3 -0
  6. package/src/adapter/ingest/overlay-apply.ts +50 -6
  7. package/src/adapter/ingest/scene-capture.ts +51 -2
  8. package/src/adapter/vgai-scene-game-adapter.ts +24 -0
  9. package/src/manifest/schema.ts +7 -3
  10. package/src/runtime/create-runtime.ts +97 -0
  11. package/src/runtime/game.ts +11 -1
  12. package/src/scene/asset-ref-check.ts +1 -1
  13. package/src/scene/scene-loader.ts +2 -2
  14. package/src/scene/user-data.ts +18 -0
  15. package/src/world3d-react/behavior.tsx +19 -11
  16. package/src/world3d-react/r3f-adapter.tsx +8 -0
  17. package/dist/adapter/adapter-surface.d.ts +0 -30
  18. package/dist/adapter/adapter-surface.d.ts.map +0 -1
  19. package/dist/adapter/adapter-surface.js +0 -18
  20. package/dist/adapter/authoring.d.ts +0 -461
  21. package/dist/adapter/authoring.d.ts.map +0 -1
  22. package/dist/adapter/authoring.js +0 -22
  23. package/dist/adapter/colyseus-networking-adapter.d.ts +0 -43
  24. package/dist/adapter/colyseus-networking-adapter.d.ts.map +0 -1
  25. package/dist/adapter/colyseus-networking-adapter.js +0 -38
  26. package/dist/adapter/first-party-systems.d.ts +0 -38
  27. package/dist/adapter/first-party-systems.d.ts.map +0 -1
  28. package/dist/adapter/first-party-systems.js +0 -77
  29. package/dist/adapter/game-adapter.d.ts +0 -133
  30. package/dist/adapter/game-adapter.d.ts.map +0 -1
  31. package/dist/adapter/game-adapter.js +0 -10
  32. package/dist/adapter/host-context.d.ts +0 -71
  33. package/dist/adapter/host-context.d.ts.map +0 -1
  34. package/dist/adapter/host-context.js +0 -16
  35. package/dist/adapter/index.d.ts +0 -22
  36. package/dist/adapter/index.d.ts.map +0 -1
  37. package/dist/adapter/index.js +0 -15
  38. package/dist/adapter/ingest/game-contract.d.ts +0 -53
  39. package/dist/adapter/ingest/game-contract.d.ts.map +0 -1
  40. package/dist/adapter/ingest/game-contract.js +0 -30
  41. package/dist/adapter/ingest/overlay-applier.d.ts +0 -118
  42. package/dist/adapter/ingest/overlay-applier.d.ts.map +0 -1
  43. package/dist/adapter/ingest/overlay-applier.js +0 -132
  44. package/dist/adapter/ingest/overlay-apply.d.ts +0 -60
  45. package/dist/adapter/ingest/overlay-apply.d.ts.map +0 -1
  46. package/dist/adapter/ingest/overlay-apply.js +0 -101
  47. package/dist/adapter/ingest/overlay-file.d.ts +0 -79
  48. package/dist/adapter/ingest/overlay-file.d.ts.map +0 -1
  49. package/dist/adapter/ingest/overlay-file.js +0 -66
  50. package/dist/adapter/ingest/overlay-report.d.ts +0 -113
  51. package/dist/adapter/ingest/overlay-report.d.ts.map +0 -1
  52. package/dist/adapter/ingest/overlay-report.js +0 -103
  53. package/dist/adapter/ingest/scene-capture.d.ts +0 -97
  54. package/dist/adapter/ingest/scene-capture.d.ts.map +0 -1
  55. package/dist/adapter/ingest/scene-capture.js +0 -248
  56. package/dist/adapter/ingest/upstream-pin.d.ts +0 -47
  57. package/dist/adapter/ingest/upstream-pin.d.ts.map +0 -1
  58. package/dist/adapter/ingest/upstream-pin.js +0 -50
  59. package/dist/adapter/loop-gate-report.d.ts +0 -34
  60. package/dist/adapter/loop-gate-report.d.ts.map +0 -1
  61. package/dist/adapter/loop-gate-report.js +0 -37
  62. package/dist/adapter/rapier-physics-adapter.d.ts +0 -12
  63. package/dist/adapter/rapier-physics-adapter.d.ts.map +0 -1
  64. package/dist/adapter/rapier-physics-adapter.js +0 -46
  65. package/dist/adapter/system-adapter.d.ts +0 -206
  66. package/dist/adapter/system-adapter.d.ts.map +0 -1
  67. package/dist/adapter/system-adapter.js +0 -13
  68. package/dist/adapter/transform.d.ts +0 -18
  69. package/dist/adapter/transform.d.ts.map +0 -1
  70. package/dist/adapter/transform.js +0 -1
  71. package/dist/adapter/vgai-scene-game-adapter.d.ts +0 -108
  72. package/dist/adapter/vgai-scene-game-adapter.d.ts.map +0 -1
  73. package/dist/adapter/vgai-scene-game-adapter.js +0 -762
  74. package/dist/ai/navigation.d.ts +0 -64
  75. package/dist/ai/navigation.d.ts.map +0 -1
  76. package/dist/ai/navigation.js +0 -133
  77. package/dist/animation/anim-graph-types.d.ts +0 -25
  78. package/dist/animation/anim-graph-types.d.ts.map +0 -1
  79. package/dist/animation/anim-graph-types.js +0 -1
  80. package/dist/animation/animation-clock.d.ts +0 -213
  81. package/dist/animation/animation-clock.d.ts.map +0 -1
  82. package/dist/animation/animation-clock.js +0 -301
  83. package/dist/animation/blend-node.d.ts +0 -22
  84. package/dist/animation/blend-node.d.ts.map +0 -1
  85. package/dist/animation/blend-node.js +0 -97
  86. package/dist/animation/camera-ownership.d.ts +0 -250
  87. package/dist/animation/camera-ownership.d.ts.map +0 -1
  88. package/dist/animation/camera-ownership.js +0 -169
  89. package/dist/animation/cinematic-cues.d.ts +0 -200
  90. package/dist/animation/cinematic-cues.d.ts.map +0 -1
  91. package/dist/animation/cinematic-cues.js +0 -213
  92. package/dist/animation/clip-map.d.ts +0 -12
  93. package/dist/animation/clip-map.d.ts.map +0 -1
  94. package/dist/animation/clip-map.js +0 -37
  95. package/dist/animation/gsap-registration.d.ts +0 -119
  96. package/dist/animation/gsap-registration.d.ts.map +0 -1
  97. package/dist/animation/gsap-registration.js +0 -92
  98. package/dist/animation/theatre-clock-binding.d.ts +0 -89
  99. package/dist/animation/theatre-clock-binding.d.ts.map +0 -1
  100. package/dist/animation/theatre-clock-binding.js +0 -29
  101. package/dist/animation/theatre-director.d.ts +0 -242
  102. package/dist/animation/theatre-director.d.ts.map +0 -1
  103. package/dist/animation/theatre-director.js +0 -235
  104. package/dist/animation/theatre-object-binding.d.ts +0 -305
  105. package/dist/animation/theatre-object-binding.d.ts.map +0 -1
  106. package/dist/animation/theatre-object-binding.js +0 -351
  107. package/dist/animation/xstate-animation-binding.d.ts +0 -56
  108. package/dist/animation/xstate-animation-binding.d.ts.map +0 -1
  109. package/dist/animation/xstate-animation-binding.js +0 -331
  110. package/dist/animation/xstate-animation-meta.d.ts +0 -266
  111. package/dist/animation/xstate-animation-meta.d.ts.map +0 -1
  112. package/dist/animation/xstate-animation-meta.js +0 -252
  113. package/dist/assets.d.ts +0 -26
  114. package/dist/assets.d.ts.map +0 -1
  115. package/dist/assets.js +0 -55
  116. package/dist/audio/index.d.ts +0 -4
  117. package/dist/audio/index.d.ts.map +0 -1
  118. package/dist/audio/index.js +0 -24
  119. package/dist/audio/tone-clock-binding.d.ts +0 -75
  120. package/dist/audio/tone-clock-binding.d.ts.map +0 -1
  121. package/dist/audio/tone-clock-binding.js +0 -29
  122. package/dist/audio/tone-context.d.ts +0 -114
  123. package/dist/audio/tone-context.d.ts.map +0 -1
  124. package/dist/audio/tone-context.js +0 -39
  125. package/dist/audio/tone-offline-render.d.ts +0 -90
  126. package/dist/audio/tone-offline-render.d.ts.map +0 -1
  127. package/dist/audio/tone-offline-render.js +0 -56
  128. package/dist/audio/wav-encode.d.ts +0 -38
  129. package/dist/audio/wav-encode.d.ts.map +0 -1
  130. package/dist/audio/wav-encode.js +0 -97
  131. package/dist/character/cloth-sim.d.ts +0 -179
  132. package/dist/character/cloth-sim.d.ts.map +0 -1
  133. package/dist/character/cloth-sim.js +0 -334
  134. package/dist/character/spring-chain.d.ts +0 -135
  135. package/dist/character/spring-chain.d.ts.map +0 -1
  136. package/dist/character/spring-chain.js +0 -165
  137. package/dist/core/game-loop.d.ts +0 -42
  138. package/dist/core/game-loop.d.ts.map +0 -1
  139. package/dist/core/game-loop.js +0 -167
  140. package/dist/core/seeded-random.d.ts +0 -69
  141. package/dist/core/seeded-random.d.ts.map +0 -1
  142. package/dist/core/seeded-random.js +0 -121
  143. package/dist/core/system-runner.d.ts +0 -120
  144. package/dist/core/system-runner.d.ts.map +0 -1
  145. package/dist/core/system-runner.js +0 -297
  146. package/dist/core/types.d.ts +0 -92
  147. package/dist/core/types.d.ts.map +0 -1
  148. package/dist/core/types.js +0 -25
  149. package/dist/data/data-asset.d.ts +0 -101
  150. package/dist/data/data-asset.d.ts.map +0 -1
  151. package/dist/data/data-asset.js +0 -122
  152. package/dist/data/data-check-core.d.ts +0 -110
  153. package/dist/data/data-check-core.d.ts.map +0 -1
  154. package/dist/data/data-check-core.js +0 -178
  155. package/dist/data/data-ref.d.ts +0 -79
  156. package/dist/data/data-ref.d.ts.map +0 -1
  157. package/dist/data/data-ref.js +0 -130
  158. package/dist/data/vite-plugin-data.d.ts +0 -79
  159. package/dist/data/vite-plugin-data.d.ts.map +0 -1
  160. package/dist/data/vite-plugin-data.js +0 -217
  161. package/dist/dev/console-bridge.d.ts +0 -22
  162. package/dist/dev/console-bridge.d.ts.map +0 -1
  163. package/dist/dev/console-bridge.js +0 -71
  164. package/dist/dev/debug-draw.d.ts +0 -24
  165. package/dist/dev/debug-draw.d.ts.map +0 -1
  166. package/dist/dev/debug-draw.js +0 -73
  167. package/dist/dev/logger.d.ts +0 -30
  168. package/dist/dev/logger.d.ts.map +0 -1
  169. package/dist/dev/logger.js +0 -90
  170. package/dist/dev/performance-profiler.d.ts +0 -60
  171. package/dist/dev/performance-profiler.d.ts.map +0 -1
  172. package/dist/dev/performance-profiler.js +0 -181
  173. package/dist/dev/webgl-gpu-timer.d.ts +0 -8
  174. package/dist/dev/webgl-gpu-timer.d.ts.map +0 -1
  175. package/dist/dev/webgl-gpu-timer.js +0 -52
  176. package/dist/ecs/component-manager.d.ts +0 -205
  177. package/dist/ecs/component-manager.d.ts.map +0 -1
  178. package/dist/ecs/component-manager.js +0 -706
  179. package/dist/ecs/game-component.d.ts +0 -146
  180. package/dist/ecs/game-component.d.ts.map +0 -1
  181. package/dist/ecs/game-component.js +0 -141
  182. package/dist/ecs/hmr-swap-report.d.ts +0 -53
  183. package/dist/ecs/hmr-swap-report.d.ts.map +0 -1
  184. package/dist/ecs/hmr-swap-report.js +0 -55
  185. package/dist/humanoid/bake.operation.d.ts +0 -119
  186. package/dist/humanoid/bake.operation.d.ts.map +0 -1
  187. package/dist/humanoid/bake.operation.js +0 -228
  188. package/dist/humanoid/body.d.ts +0 -101
  189. package/dist/humanoid/body.d.ts.map +0 -1
  190. package/dist/humanoid/body.js +0 -530
  191. package/dist/humanoid/clips.d.ts +0 -62
  192. package/dist/humanoid/clips.d.ts.map +0 -1
  193. package/dist/humanoid/clips.js +0 -121
  194. package/dist/humanoid/compose.d.ts +0 -75
  195. package/dist/humanoid/compose.d.ts.map +0 -1
  196. package/dist/humanoid/compose.js +0 -176
  197. package/dist/humanoid/generate.d.ts +0 -92
  198. package/dist/humanoid/generate.d.ts.map +0 -1
  199. package/dist/humanoid/generate.js +0 -124
  200. package/dist/humanoid/index.d.ts +0 -24
  201. package/dist/humanoid/index.d.ts.map +0 -1
  202. package/dist/humanoid/index.js +0 -18
  203. package/dist/humanoid/schema.d.ts +0 -35
  204. package/dist/humanoid/schema.d.ts.map +0 -1
  205. package/dist/humanoid/schema.js +0 -87
  206. package/dist/humanoid/skeleton.d.ts +0 -103
  207. package/dist/humanoid/skeleton.d.ts.map +0 -1
  208. package/dist/humanoid/skeleton.js +0 -289
  209. package/dist/index.d.ts +0 -37
  210. package/dist/index.d.ts.map +0 -1
  211. package/dist/index.js +0 -30
  212. package/dist/input/input-manager.d.ts +0 -945
  213. package/dist/input/input-manager.d.ts.map +0 -1
  214. package/dist/input/input-manager.js +0 -2207
  215. package/dist/input/input-types.d.ts +0 -174
  216. package/dist/input/input-types.d.ts.map +0 -1
  217. package/dist/input/input-types.js +0 -1
  218. package/dist/input/prompt-labels.d.ts +0 -21
  219. package/dist/input/prompt-labels.d.ts.map +0 -1
  220. package/dist/input/prompt-labels.js +0 -121
  221. package/dist/input/rebind-controller.d.ts +0 -59
  222. package/dist/input/rebind-controller.d.ts.map +0 -1
  223. package/dist/input/rebind-controller.js +0 -85
  224. package/dist/input/schema.d.ts +0 -16
  225. package/dist/input/schema.d.ts.map +0 -1
  226. package/dist/input/schema.js +0 -228
  227. package/dist/loader.d.ts +0 -43
  228. package/dist/loader.d.ts.map +0 -1
  229. package/dist/loader.js +0 -59
  230. package/dist/manifest/index.d.ts +0 -5
  231. package/dist/manifest/index.d.ts.map +0 -1
  232. package/dist/manifest/index.js +0 -8
  233. package/dist/manifest/load-file.d.ts +0 -4
  234. package/dist/manifest/load-file.d.ts.map +0 -1
  235. package/dist/manifest/load-file.js +0 -14
  236. package/dist/manifest/load.d.ts +0 -144
  237. package/dist/manifest/load.d.ts.map +0 -1
  238. package/dist/manifest/load.js +0 -273
  239. package/dist/manifest/schema.d.ts +0 -230
  240. package/dist/manifest/schema.d.ts.map +0 -1
  241. package/dist/manifest/schema.js +0 -394
  242. package/dist/physics/collision-system.d.ts +0 -41
  243. package/dist/physics/collision-system.d.ts.map +0 -1
  244. package/dist/physics/collision-system.js +0 -64
  245. package/dist/physics/physics-registry.d.ts +0 -45
  246. package/dist/physics/physics-registry.d.ts.map +0 -1
  247. package/dist/physics/physics-registry.js +0 -65
  248. package/dist/physics/transform-writer.d.ts +0 -16
  249. package/dist/physics/transform-writer.d.ts.map +0 -1
  250. package/dist/physics/transform-writer.js +0 -38
  251. package/dist/physics/trigger-dispatch.d.ts +0 -24
  252. package/dist/physics/trigger-dispatch.d.ts.map +0 -1
  253. package/dist/physics/trigger-dispatch.js +0 -80
  254. package/dist/react/game-state.d.ts +0 -156
  255. package/dist/react/game-state.d.ts.map +0 -1
  256. package/dist/react/game-state.js +0 -222
  257. package/dist/react/root-adapter.d.ts +0 -11
  258. package/dist/react/root-adapter.d.ts.map +0 -1
  259. package/dist/react/root-adapter.js +0 -41
  260. package/dist/react/unmanaged-root-detector.d.ts +0 -12
  261. package/dist/react/unmanaged-root-detector.d.ts.map +0 -1
  262. package/dist/react/unmanaged-root-detector.js +0 -45
  263. package/dist/react/use-data.d.ts +0 -60
  264. package/dist/react/use-data.d.ts.map +0 -1
  265. package/dist/react/use-data.js +0 -101
  266. package/dist/react/use-selection.d.ts +0 -75
  267. package/dist/react/use-selection.d.ts.map +0 -1
  268. package/dist/react/use-selection.js +0 -90
  269. package/dist/render/auto-batcher.d.ts +0 -34
  270. package/dist/render/auto-batcher.d.ts.map +0 -1
  271. package/dist/render/auto-batcher.js +0 -140
  272. package/dist/render/render-batch-system.d.ts +0 -33
  273. package/dist/render/render-batch-system.d.ts.map +0 -1
  274. package/dist/render/render-batch-system.js +0 -230
  275. package/dist/render/render-features.d.ts +0 -47
  276. package/dist/render/render-features.d.ts.map +0 -1
  277. package/dist/render/render-features.js +0 -105
  278. package/dist/render/render-settings.d.ts +0 -36
  279. package/dist/render/render-settings.d.ts.map +0 -1
  280. package/dist/render/render-settings.js +0 -51
  281. package/dist/runtime/create-runtime.d.ts +0 -249
  282. package/dist/runtime/create-runtime.d.ts.map +0 -1
  283. package/dist/runtime/create-runtime.js +0 -641
  284. package/dist/runtime/debug-bridge.d.ts +0 -257
  285. package/dist/runtime/debug-bridge.d.ts.map +0 -1
  286. package/dist/runtime/debug-bridge.js +0 -224
  287. package/dist/runtime/debug-registry.d.ts +0 -288
  288. package/dist/runtime/debug-registry.d.ts.map +0 -1
  289. package/dist/runtime/debug-registry.js +0 -513
  290. package/dist/runtime/frame-selector-cache.d.ts +0 -41
  291. package/dist/runtime/frame-selector-cache.d.ts.map +0 -1
  292. package/dist/runtime/frame-selector-cache.js +0 -65
  293. package/dist/runtime/game.d.ts +0 -496
  294. package/dist/runtime/game.d.ts.map +0 -1
  295. package/dist/runtime/game.js +0 -741
  296. package/dist/runtime/gameplay-rng-trap.d.ts +0 -48
  297. package/dist/runtime/gameplay-rng-trap.d.ts.map +0 -1
  298. package/dist/runtime/gameplay-rng-trap.js +0 -96
  299. package/dist/runtime/input-router.d.ts +0 -78
  300. package/dist/runtime/input-router.d.ts.map +0 -1
  301. package/dist/runtime/input-router.js +0 -171
  302. package/dist/runtime/mount-game.d.ts +0 -129
  303. package/dist/runtime/mount-game.d.ts.map +0 -1
  304. package/dist/runtime/mount-game.js +0 -152
  305. package/dist/runtime/mount-manifest.d.ts +0 -173
  306. package/dist/runtime/mount-manifest.d.ts.map +0 -1
  307. package/dist/runtime/mount-manifest.js +0 -297
  308. package/dist/runtime/render-audio-control.d.ts +0 -110
  309. package/dist/runtime/render-audio-control.d.ts.map +0 -1
  310. package/dist/runtime/render-audio-control.js +0 -111
  311. package/dist/runtime/render-control.d.ts +0 -291
  312. package/dist/runtime/render-control.d.ts.map +0 -1
  313. package/dist/runtime/render-control.js +0 -264
  314. package/dist/runtime/render-seed.d.ts +0 -57
  315. package/dist/runtime/render-seed.d.ts.map +0 -1
  316. package/dist/runtime/render-seed.js +0 -74
  317. package/dist/runtime/state-bridge.d.ts +0 -61
  318. package/dist/runtime/state-bridge.d.ts.map +0 -1
  319. package/dist/runtime/state-bridge.js +0 -63
  320. package/dist/runtime/types.d.ts +0 -253
  321. package/dist/runtime/types.d.ts.map +0 -1
  322. package/dist/runtime/types.js +0 -1
  323. package/dist/scene/asset-loaders.d.ts +0 -79
  324. package/dist/scene/asset-loaders.d.ts.map +0 -1
  325. package/dist/scene/asset-loaders.js +0 -141
  326. package/dist/scene/asset-paths.d.ts +0 -11
  327. package/dist/scene/asset-paths.d.ts.map +0 -1
  328. package/dist/scene/asset-paths.js +0 -108
  329. package/dist/scene/asset-ref-check.d.ts +0 -88
  330. package/dist/scene/asset-ref-check.d.ts.map +0 -1
  331. package/dist/scene/asset-ref-check.js +0 -214
  332. package/dist/scene/asset-registry.d.ts +0 -38
  333. package/dist/scene/asset-registry.d.ts.map +0 -1
  334. package/dist/scene/asset-registry.js +0 -66
  335. package/dist/scene/collider-dimensions.d.ts +0 -63
  336. package/dist/scene/collider-dimensions.d.ts.map +0 -1
  337. package/dist/scene/collider-dimensions.js +0 -98
  338. package/dist/scene/component-registry.d.ts +0 -22
  339. package/dist/scene/component-registry.d.ts.map +0 -1
  340. package/dist/scene/component-registry.js +0 -37
  341. package/dist/scene/defaults.d.ts +0 -252
  342. package/dist/scene/defaults.d.ts.map +0 -1
  343. package/dist/scene/defaults.js +0 -151
  344. package/dist/scene/geometries/index.d.ts +0 -8
  345. package/dist/scene/geometries/index.d.ts.map +0 -1
  346. package/dist/scene/geometries/index.js +0 -7
  347. package/dist/scene/geometries/terrain.d.ts +0 -2
  348. package/dist/scene/geometries/terrain.d.ts.map +0 -1
  349. package/dist/scene/geometries/terrain.js +0 -37
  350. package/dist/scene/geometry-registry.d.ts +0 -24
  351. package/dist/scene/geometry-registry.d.ts.map +0 -1
  352. package/dist/scene/geometry-registry.js +0 -20
  353. package/dist/scene/instance-registry.d.ts +0 -36
  354. package/dist/scene/instance-registry.d.ts.map +0 -1
  355. package/dist/scene/instance-registry.js +0 -55
  356. package/dist/scene/instancers/grid.d.ts +0 -2
  357. package/dist/scene/instancers/grid.d.ts.map +0 -1
  358. package/dist/scene/instancers/grid.js +0 -36
  359. package/dist/scene/instancers/index.d.ts +0 -8
  360. package/dist/scene/instancers/index.d.ts.map +0 -1
  361. package/dist/scene/instancers/index.js +0 -7
  362. package/dist/scene/light-camera-factory.d.ts +0 -23
  363. package/dist/scene/light-camera-factory.d.ts.map +0 -1
  364. package/dist/scene/light-camera-factory.js +0 -68
  365. package/dist/scene/material-factory.d.ts +0 -17
  366. package/dist/scene/material-factory.d.ts.map +0 -1
  367. package/dist/scene/material-factory.js +0 -197
  368. package/dist/scene/material-registry.d.ts +0 -40
  369. package/dist/scene/material-registry.d.ts.map +0 -1
  370. package/dist/scene/material-registry.js +0 -44
  371. package/dist/scene/materials/index.d.ts +0 -8
  372. package/dist/scene/materials/index.d.ts.map +0 -1
  373. package/dist/scene/materials/index.js +0 -7
  374. package/dist/scene/materials/water.d.ts +0 -2
  375. package/dist/scene/materials/water.d.ts.map +0 -1
  376. package/dist/scene/materials/water.js +0 -54
  377. package/dist/scene/parse.d.ts +0 -23
  378. package/dist/scene/parse.d.ts.map +0 -1
  379. package/dist/scene/parse.js +0 -179
  380. package/dist/scene/particles-factory.d.ts +0 -33
  381. package/dist/scene/particles-factory.d.ts.map +0 -1
  382. package/dist/scene/particles-factory.js +0 -282
  383. package/dist/scene/scene-apply.d.ts +0 -109
  384. package/dist/scene/scene-apply.d.ts.map +0 -1
  385. package/dist/scene/scene-apply.js +0 -370
  386. package/dist/scene/scene-diff-schema.d.ts +0 -285
  387. package/dist/scene/scene-diff-schema.d.ts.map +0 -1
  388. package/dist/scene/scene-diff-schema.js +0 -92
  389. package/dist/scene/scene-diff-types.d.ts +0 -40
  390. package/dist/scene/scene-diff-types.d.ts.map +0 -1
  391. package/dist/scene/scene-diff-types.js +0 -14
  392. package/dist/scene/scene-loader.d.ts +0 -167
  393. package/dist/scene/scene-loader.d.ts.map +0 -1
  394. package/dist/scene/scene-loader.js +0 -1211
  395. package/dist/scene/scene-query.d.ts +0 -11
  396. package/dist/scene/scene-query.d.ts.map +0 -1
  397. package/dist/scene/scene-query.js +0 -63
  398. package/dist/scene/scene-types.d.ts +0 -10
  399. package/dist/scene/scene-types.d.ts.map +0 -1
  400. package/dist/scene/scene-types.js +0 -8
  401. package/dist/scene/scene-version.d.ts +0 -26
  402. package/dist/scene/scene-version.d.ts.map +0 -1
  403. package/dist/scene/scene-version.js +0 -34
  404. package/dist/scene/schema/animation.d.ts +0 -8
  405. package/dist/scene/schema/animation.d.ts.map +0 -1
  406. package/dist/scene/schema/animation.js +0 -42
  407. package/dist/scene/schema/audio.d.ts +0 -13
  408. package/dist/scene/schema/audio.d.ts.map +0 -1
  409. package/dist/scene/schema/audio.js +0 -22
  410. package/dist/scene/schema/camera.d.ts +0 -28
  411. package/dist/scene/schema/camera.d.ts.map +0 -1
  412. package/dist/scene/schema/camera.js +0 -17
  413. package/dist/scene/schema/collider.d.ts +0 -40
  414. package/dist/scene/schema/collider.d.ts.map +0 -1
  415. package/dist/scene/schema/collider.js +0 -55
  416. package/dist/scene/schema/entity-ref.d.ts +0 -31
  417. package/dist/scene/schema/entity-ref.d.ts.map +0 -1
  418. package/dist/scene/schema/entity-ref.js +0 -61
  419. package/dist/scene/schema/entity.d.ts +0 -1036
  420. package/dist/scene/schema/entity.d.ts.map +0 -1
  421. package/dist/scene/schema/entity.js +0 -153
  422. package/dist/scene/schema/environment.d.ts +0 -425
  423. package/dist/scene/schema/environment.d.ts.map +0 -1
  424. package/dist/scene/schema/environment.js +0 -357
  425. package/dist/scene/schema/index.d.ts +0 -37
  426. package/dist/scene/schema/index.d.ts.map +0 -1
  427. package/dist/scene/schema/index.js +0 -19
  428. package/dist/scene/schema/instances.d.ts +0 -14
  429. package/dist/scene/schema/instances.d.ts.map +0 -1
  430. package/dist/scene/schema/instances.js +0 -28
  431. package/dist/scene/schema/joint.d.ts +0 -23
  432. package/dist/scene/schema/joint.d.ts.map +0 -1
  433. package/dist/scene/schema/joint.js +0 -23
  434. package/dist/scene/schema/light.d.ts +0 -38
  435. package/dist/scene/schema/light.d.ts.map +0 -1
  436. package/dist/scene/schema/light.js +0 -26
  437. package/dist/scene/schema/material.d.ts +0 -199
  438. package/dist/scene/schema/material.d.ts.map +0 -1
  439. package/dist/scene/schema/material.js +0 -103
  440. package/dist/scene/schema/mesh.d.ts +0 -48
  441. package/dist/scene/schema/mesh.d.ts.map +0 -1
  442. package/dist/scene/schema/mesh.js +0 -82
  443. package/dist/scene/schema/particles.d.ts +0 -2178
  444. package/dist/scene/schema/particles.d.ts.map +0 -1
  445. package/dist/scene/schema/particles.js +0 -321
  446. package/dist/scene/schema/physics.d.ts +0 -56
  447. package/dist/scene/schema/physics.d.ts.map +0 -1
  448. package/dist/scene/schema/physics.js +0 -37
  449. package/dist/scene/schema/scene-file.d.ts +0 -303
  450. package/dist/scene/schema/scene-file.d.ts.map +0 -1
  451. package/dist/scene/schema/scene-file.js +0 -256
  452. package/dist/scene/schema/shadow.d.ts +0 -28
  453. package/dist/scene/schema/shadow.d.ts.map +0 -1
  454. package/dist/scene/schema/shadow.js +0 -20
  455. package/dist/scene/schema/spline.d.ts +0 -25
  456. package/dist/scene/schema/spline.d.ts.map +0 -1
  457. package/dist/scene/schema/spline.js +0 -15
  458. package/dist/scene/schema/tuples.d.ts +0 -12
  459. package/dist/scene/schema/tuples.d.ts.map +0 -1
  460. package/dist/scene/schema/tuples.js +0 -14
  461. package/dist/scene/user-data.d.ts +0 -185
  462. package/dist/scene/user-data.d.ts.map +0 -1
  463. package/dist/scene/user-data.js +0 -144
  464. package/dist/setup/setup-audio.d.ts +0 -20
  465. package/dist/setup/setup-audio.d.ts.map +0 -1
  466. package/dist/setup/setup-audio.js +0 -41
  467. package/dist/setup/setup-particles.d.ts +0 -17
  468. package/dist/setup/setup-particles.d.ts.map +0 -1
  469. package/dist/setup/setup-particles.js +0 -16
  470. package/dist/setup/setup-physics.d.ts +0 -25
  471. package/dist/setup/setup-physics.d.ts.map +0 -1
  472. package/dist/setup/setup-physics.js +0 -51
  473. package/dist/setup/setup-renderer.d.ts +0 -69
  474. package/dist/setup/setup-renderer.d.ts.map +0 -1
  475. package/dist/setup/setup-renderer.js +0 -363
  476. package/dist/tools/define-tool.d.ts +0 -130
  477. package/dist/tools/define-tool.d.ts.map +0 -1
  478. package/dist/tools/define-tool.js +0 -96
  479. package/dist/world2d/authoring-2d.d.ts +0 -80
  480. package/dist/world2d/authoring-2d.d.ts.map +0 -1
  481. package/dist/world2d/authoring-2d.js +0 -195
  482. package/dist/world2d/capture-to-scene2d.d.ts +0 -5
  483. package/dist/world2d/capture-to-scene2d.d.ts.map +0 -1
  484. package/dist/world2d/capture-to-scene2d.js +0 -49
  485. package/dist/world2d/collision-2d.d.ts +0 -52
  486. package/dist/world2d/collision-2d.d.ts.map +0 -1
  487. package/dist/world2d/collision-2d.js +0 -70
  488. package/dist/world2d/components-2d.d.ts +0 -47
  489. package/dist/world2d/components-2d.d.ts.map +0 -1
  490. package/dist/world2d/components-2d.js +0 -77
  491. package/dist/world2d/index.d.ts +0 -34
  492. package/dist/world2d/index.d.ts.map +0 -1
  493. package/dist/world2d/index.js +0 -32
  494. package/dist/world2d/ingest-iframe-2d.d.ts +0 -82
  495. package/dist/world2d/ingest-iframe-2d.d.ts.map +0 -1
  496. package/dist/world2d/ingest-iframe-2d.js +0 -166
  497. package/dist/world2d/ingest2d.d.ts +0 -97
  498. package/dist/world2d/ingest2d.d.ts.map +0 -1
  499. package/dist/world2d/ingest2d.js +0 -40
  500. package/dist/world2d/physics2d-registry.d.ts +0 -24
  501. package/dist/world2d/physics2d-registry.d.ts.map +0 -1
  502. package/dist/world2d/physics2d-registry.js +0 -38
  503. package/dist/world2d/pixi-game-adapter.d.ts +0 -107
  504. package/dist/world2d/pixi-game-adapter.d.ts.map +0 -1
  505. package/dist/world2d/pixi-game-adapter.js +0 -210
  506. package/dist/world2d/pixi-surface.d.ts +0 -39
  507. package/dist/world2d/pixi-surface.d.ts.map +0 -1
  508. package/dist/world2d/pixi-surface.js +0 -47
  509. package/dist/world2d/scene-capture-2d.d.ts +0 -43
  510. package/dist/world2d/scene-capture-2d.d.ts.map +0 -1
  511. package/dist/world2d/scene-capture-2d.js +0 -92
  512. package/dist/world2d/scene2d-loader.d.ts +0 -27
  513. package/dist/world2d/scene2d-loader.d.ts.map +0 -1
  514. package/dist/world2d/scene2d-loader.js +0 -275
  515. package/dist/world2d/schema/entity2d.d.ts +0 -102
  516. package/dist/world2d/schema/entity2d.d.ts.map +0 -1
  517. package/dist/world2d/schema/entity2d.js +0 -119
  518. package/dist/world2d/schema/physics2d.d.ts +0 -46
  519. package/dist/world2d/schema/physics2d.d.ts.map +0 -1
  520. package/dist/world2d/schema/physics2d.js +0 -45
  521. package/dist/world2d/schema/sprite.d.ts +0 -42
  522. package/dist/world2d/schema/sprite.d.ts.map +0 -1
  523. package/dist/world2d/schema/sprite.js +0 -63
  524. package/dist/world2d/schema/tilemap.d.ts +0 -15
  525. package/dist/world2d/schema/tilemap.d.ts.map +0 -1
  526. package/dist/world2d/schema/tilemap.js +0 -19
  527. package/dist/world2d/schema/tuples2d.d.ts +0 -20
  528. package/dist/world2d/schema/tuples2d.d.ts.map +0 -1
  529. package/dist/world2d/schema/tuples2d.js +0 -19
  530. package/dist/world2d/system-adapters-2d.d.ts +0 -16
  531. package/dist/world2d/system-adapters-2d.d.ts.map +0 -1
  532. package/dist/world2d/system-adapters-2d.js +0 -37
  533. package/dist/world2d/transform-writer-2d.d.ts +0 -16
  534. package/dist/world2d/transform-writer-2d.d.ts.map +0 -1
  535. package/dist/world2d/transform-writer-2d.js +0 -22
  536. package/dist/world2d/types.d.ts +0 -59
  537. package/dist/world2d/types.d.ts.map +0 -1
  538. package/dist/world2d/types.js +0 -1
@@ -1,706 +0,0 @@
1
- import { PHASE_ORDER } from '../core/types';
2
- import { isFirstPartyMounted } from '../runtime/game';
3
- import { GameComponent, linkGameComponentHmrClasses, } from './game-component';
4
- import { logHmrSwapMiss } from './hmr-swap-report';
5
- /**
6
- * Find the `WorldInstance` (T7.1, `docs/GAME-ROOT-DESIGN.md`) that owns this
7
- * manager's `ctx`, for wiring `GameComponent.world` at attach (T7.2, D8 —
8
- * `docs/GAME-COMPONENT-GENERALIZATION.md` §2). Matches by `mounted.ctx ===
9
- * ctx` identity (the same probe `isFirstPartyMounted` narrows for) rather
10
- * than by id, so it works unmodified whether this manager belongs to the
11
- * default world or a second/third registered world (`test/
12
- * game-two-roots.test.ts`).
13
- *
14
- * WORLD-WIRING STATE (T7.2 slice 2 — was a KNOWN GAP in slice 1, now closed
15
- * two ways): `ctx.game`/`ctx.roots` exist from the start of `mount()`, but
16
- * the `WorldInstance` itself is constructed by `registerThreeWorld` in
17
- * `create-runtime.ts` only AFTER `mount()` returns — i.e. AFTER
18
- * scene-authored components have already been attached by the scene loader
19
- * during that same `mount()` call. For those attaches, THIS function still
20
- * resolves to `undefined` (the world genuinely doesn't exist yet — there is
21
- * no way around that ordering; the `WorldInstance` wraps `mounted`, which
22
- * only exists once `mount()` returns). What closes the gap is
23
- * `adoptWorld()` below: `registerThreeWorld` calls it immediately after
24
- * building the `WorldInstance`, and it backfills `instance.world` for every
25
- * already-attached instance still missing one — a one-time wiring
26
- * completion at registration, not a lazy getter and not a per-frame sync.
27
- * Only a component attached AFTER that backfill and before the NEXT world's
28
- * registration (there is no such window in the single-manager-per-world
29
- * architecture) would still see a stale `undefined`; in practice every
30
- * instance ends up with a defined, correct `world` by the time
31
- * `createGameRuntime`/`registerThreeWorld` returns.
32
- */
33
- function resolveWorldInstance(ctx) {
34
- if (!ctx.game)
35
- return undefined;
36
- for (const w of ctx.game.roots) {
37
- if (isFirstPartyMounted(w.mounted) && w.mounted.ctx === ctx)
38
- return w;
39
- }
40
- return undefined;
41
- }
42
- /** Best-effort human label for an error message — a node's `label` (PIXI.Container,
43
- * v8) or `name` (THREE.Object3D, and PIXI's deprecated alias), falling back to a
44
- * generic placeholder when neither is set. */
45
- function describeNode(node) {
46
- const n = node;
47
- const label = typeof n.label === 'string' && n.label
48
- ? n.label
49
- : typeof n.name === 'string' && n.name
50
- ? n.name
51
- : undefined;
52
- return label ? `"${label}"` : '(unnamed entity)';
53
- }
54
- /**
55
- * Manages GameComponent instances attached to entities (Object3Ds).
56
- *
57
- * Tracks instances per Object3D and per phase. Registers one tick function per
58
- * phase via `ctx.systems.setComponentTick(phase, ...)` (T7.1 slice 2) — a
59
- * dedicated slot, not a bare `add()`ed system. `SystemRunner.runPhase` always
60
- * runs the componentTick slot after every engine-bucket system and before
61
- * every game-bucket system in that phase, so component updates interleave
62
- * correctly with engine systems BY STRUCTURE: an engine system `add()`ed
63
- * after this manager is constructed still runs before its tick (see
64
- * `test/system-runs-before-component-tick.test.ts`).
65
- *
66
- * Kind (T7.2 slice 2, D8 §3): a manager is constructed for exactly ONE
67
- * `AdapterSurface` (`opts.kind`, default `'threejs'` — every existing call site
68
- * omits it and gets the same manager slice 1 built). `attach()` enforces two
69
- * rules against that kind, loudly:
70
- * - a `'react'`-kind manager throws on EVERY attach (react entities host no
71
- * GameComponents — render from game state via the T7.4 state bridge);
72
- * - any other kind throws when the component's `static declaredKind` is set
73
- * and doesn't match this manager's kind (`'any'` is exempt — a
74
- * kind-agnostic component may attach wherever components are legal).
75
- * A `'pixijs'`-kind manager wires `rigidBody`/`collider` from `opts.physics2d`
76
- * (a `Physics2DRegistry`) instead of the 3D `physics: PhysicsRegistry`
77
- * parameter — the field names match by design (`world2d/types.ts`), so this
78
- * is a retype of the wiring, not a new shape. `physics` is optional (T7.3
79
- * slice 2) — a `'pixijs'`-kind manager never reads it (`resolvePhysicsRefs`
80
- * below only reaches the 3D branch for a non-pixijs manager), so a caller
81
- * building one no longer has to construct a throwaway 3D `PhysicsRegistry`
82
- * just to satisfy this signature.
83
- */
84
- export function createComponentManager(ctx, physics, opts) {
85
- const kind = opts?.kind ?? 'threejs';
86
- const physics2d = opts?.physics2d;
87
- const byPhase = new Map();
88
- const byEntity = new Map();
89
- // --- HMR registry-keyed identity (T5.4) ----------------------------------
90
- // `hotSwap` used to match live instances by `inst.constructor.name` — fragile
91
- // under minification, a dev renaming a class, or two distinct classes that
92
- // happen to share a runtime name. Both trees walked by `applyComponents`
93
- // (`scene/component-registry.ts`) and `applyComponents2D`
94
- // (`world2d/scene2d-loader.ts`) now pass the `componentRegistry` name the
95
- // class was looked up under — plus the raw pre-`schema.parse` authored data —
96
- // into `attach()`'s optional third argument, recorded here keyed by instance
97
- // identity (a WeakMap: no explicit cleanup needed, entries die with the
98
- // instance). An instance attached WITHOUT this info (an ad-hoc, code-attached
99
- // component — `playground/main.ts`'s `ctx.components.attach(cube, new Cls())`,
100
- // and this file's own `component-lifecycle.test.ts`) has no registry key on
101
- // record; `hotSwap` falls back to the legacy `constructor.name` compare for
102
- // those ONLY, preserving pre-T5.4 behavior for the ad-hoc path exactly (see
103
- // `hotSwap` below).
104
- const registryKeyOf = new WeakMap();
105
- const authoredPropsOf = new WeakMap();
106
- // --- Frame-boundary init flush (T1.11) -----------------------------------
107
- // `pendingInit` holds every attached-but-not-yet-initialized instance. An
108
- // instance is pushed into its phase's `byPhase` list immediately on attach
109
- // (so getComponents()/hotSwap() see it right away), but the per-phase tick
110
- // below FILTERS OUT anything still in `pendingInit` — so an instance can
111
- // never receive update() before its init() has resolved, no matter how many
112
- // frames init() takes (a real async load included).
113
- //
114
- // `initPromises` tracks in-flight init() calls (keyed by instance) so:
115
- // (a) init() is called exactly once per instance, whether it was started
116
- // by the automatic per-frame flush below or by an explicit
117
- // `initAll()` call (they share the same `startInit` helper), and
118
- // (b) `initAll()` can `await` an init() the automatic flush already
119
- // kicked off, instead of double-invoking it.
120
- //
121
- // Semantics: init() is awaited (via a chained promise) before the instance
122
- // is removed from `pendingInit`; only then is it eligible for update(). A
123
- // throwing/rejecting init() is isolated the same way update()/dispose() are
124
- // (T1.8) — logged loudly, never left to crash the frame loop or wedge the
125
- // instance out of ticking forever (it's still removed from `pendingInit`
126
- // after the error, matching "degrade loudly, don't hang").
127
- const pendingInit = new Set();
128
- const initPromises = new Map();
129
- // --- Pending-init re-arm on hotSwap (§7.1-6, probe8) ---------------------
130
- // `swapOneIfMatched` may land while an instance's init() is still in flight
131
- // (`initPromises.has(inst)` — the OLD class's init hasn't settled yet). The
132
- // live instance's prototype is swapped to the NEW class SYNCHRONOUSLY at
133
- // swap time, but the in-flight promise chain still belongs to the OLD
134
- // class's init call — its trailing `.then()` below is what clears
135
- // `pendingInit`/`initPromises` once it settles, and nothing else re-triggers
136
- // init() for the new class. `pendingReinit` records "a swap landed while
137
- // this instance's init was in flight" so that SAME trailing `.then()` can,
138
- // instead of clearing pendingInit, re-kick `startInit` for the (by-then
139
- // already-swapped) instance — whose `.init` now resolves to the NEW class's
140
- // method. `pendingInit` membership is deliberately left untouched across the
141
- // whole handoff: the instance must stay ineligible for update() from the
142
- // moment the OLD init started until the NEW init resolves, exactly as it
143
- // was mid-old-init (T1.11's "never before init() resolves" invariant, now
144
- // spanning a swap instead of being violated by one). A Set, not a Map to a
145
- // specific class: by the time this fires, the CURRENT prototype (whichever
146
- // class most recently swapped in) is what `inst.init` resolves to, so
147
- // multiple swaps mid-flight collapse to exactly one re-kicked init for the
148
- // LAST class — never a double-invoke, never a lost update.
149
- const pendingReinit = new Set();
150
- function startInit(inst) {
151
- const existing = initPromises.get(inst);
152
- if (existing)
153
- return existing;
154
- const p = Promise.resolve()
155
- .then(() => inst.init?.(ctx))
156
- .catch((err) => {
157
- console.error(`[component-manager] ${inst.constructor.name}.init() threw:`, err);
158
- })
159
- .then(() => {
160
- if (pendingReinit.has(inst)) {
161
- // A hotSwap landed mid-init (probe8): don't clear pendingInit — the
162
- // instance must stay un-ticked until the NEW class's init (kicked
163
- // off below) resolves. Clear initPromises first so the re-kicked
164
- // startInit doesn't see itself as "already in flight" and return
165
- // this (already-settling) promise back out.
166
- pendingReinit.delete(inst);
167
- initPromises.delete(inst);
168
- startInit(inst);
169
- return;
170
- }
171
- pendingInit.delete(inst);
172
- initPromises.delete(inst);
173
- });
174
- initPromises.set(inst, p);
175
- return p;
176
- }
177
- /**
178
- * Kick off init() for anything attached since the last flush that hasn't
179
- * been started yet. Fire-and-forget (not awaited) — called at the start of
180
- * every phase tick below, so a component attached mid-frame (e.g. from a
181
- * trigger callback) gets its init() started as soon as possible without
182
- * game code ever calling `initAll()` manually. Idempotent / cheap no-op
183
- * when `pendingInit` is empty or everything in it is already in flight.
184
- */
185
- function flushPendingInitAuto() {
186
- for (const inst of pendingInit)
187
- startInit(inst);
188
- }
189
- // --- In-tick structural-change deferral state ---------------------------
190
- // A component's update() may call attach()/detach() (a Unity-MonoBehaviour
191
- // style spawn/despawn-from-update pattern). Mutating `byPhase` mid-iteration
192
- // would (a) skip a sibling when a splice shifts indices, and (b) tick a fresh
193
- // instance before its init() ran. So while a phase loop is running we DEFER
194
- // structural changes and apply them only after the loop completes.
195
- let isTicking = false;
196
- // Entities whose detach was requested during the current tick — applied
197
- // (dispose + removal) after the phase loop completes.
198
- const pendingDetach = [];
199
- // Components whose attach was requested during the current tick — applied
200
- // after the phase loop completes, so they start ticking next frame (never
201
- // before their init()).
202
- const pendingAttach = [];
203
- // Live set of instances detached this tick. The running phase loop iterates a
204
- // snapshot, so this guard ensures an instance detached earlier in the frame is
205
- // not update()'d again (use-after-dispose protection).
206
- const detachedThisTick = new Set();
207
- for (const phase of PHASE_ORDER) {
208
- byPhase.set(phase, []);
209
- }
210
- function flushPending() {
211
- // Detaches win within a flush: an attach and a detach of the same entity
212
- // issued in one tick resolves to detached, regardless of issue order
213
- // (NOT "resolves in the order the caller issued the two kinds of op" —
214
- // that was never actually true; see probe7 "attach-detach" finding).
215
- // Detaches are applied first (as before), and every entity they touch is
216
- // collected into `detachedEntities` so a same-tick pending attach whose
217
- // target is in that set can be skipped below rather than landing a
218
- // component on an entity the caller already despawned this tick.
219
- let detachedEntities = null;
220
- if (pendingDetach.length > 0) {
221
- const batch = pendingDetach.splice(0);
222
- detachedEntities = new Set(batch);
223
- for (const node of batch)
224
- performDetach(node);
225
- }
226
- if (pendingAttach.length > 0) {
227
- const batch = pendingAttach.splice(0);
228
- for (const { node, instance, registry } of batch) {
229
- if (detachedEntities?.has(node)) {
230
- // The target entity was detached in this same flush: the attach
231
- // never actually happened (it was never pushed into byPhase/
232
- // byEntity), so there is nothing to detach/dispose here — just
233
- // purge it from init tracking so it never gets init()'d.
234
- pendingInit.delete(instance);
235
- initPromises.delete(instance);
236
- pendingReinit.delete(instance);
237
- continue;
238
- }
239
- performAttach(node, instance, registry);
240
- }
241
- }
242
- detachedThisTick.clear();
243
- }
244
- // Register the ONE component-tick function for each phase, via the
245
- // dedicated slot (T7.1 slice 2) — NOT ctx.systems.add(). This is what
246
- // makes "engine systems before component ticks" hold by structure: the
247
- // slot always runs after the phase's entire engine bucket, regardless of
248
- // when this manager was constructed relative to other systems.add() calls.
249
- for (const phase of PHASE_ORDER) {
250
- ctx.systems.setComponentTick(phase, (dt) => {
251
- // Frame-boundary init flush (T1.11): every phase tick starts by kicking
252
- // off init() for anything pending — no manual `initAll()` call required
253
- // from game code. See `flushPendingInitAuto` above.
254
- flushPendingInitAuto();
255
- isTicking = true;
256
- try {
257
- // Snapshot the phase list: an in-loop attach must not be ticked this frame,
258
- // and an in-loop detach (which only marks `detachedThisTick`) must not shift
259
- // the indices we are iterating and skip a sibling.
260
- const list = byPhase.get(phase).slice();
261
- for (let i = 0; i < list.length; i++) {
262
- const inst = list[i];
263
- // A component detached earlier this frame must NOT be update()'d again.
264
- if (detachedThisTick.has(inst))
265
- continue;
266
- // A component whose init() hasn't resolved yet must NOT be
267
- // update()'d — this is the actual guarantee behind T1.11: never
268
- // before init() resolves, regardless of how it got attached.
269
- if (pendingInit.has(inst))
270
- continue;
271
- // Isolate per component: a throwing update() must not stop siblings
272
- // in the same phase from ticking, and must not leave the manager
273
- // wedged (isTicking stuck true, pending attach/detach never
274
- // flushed). Failures are loud — logged with the component name —
275
- // never swallowed silently.
276
- try {
277
- ctx.game?.profiler.beginComponent();
278
- inst.update(dt, ctx);
279
- }
280
- catch (err) {
281
- console.error(`[component-manager] ${inst.constructor.name}.update() threw:`, err);
282
- }
283
- finally {
284
- ctx.game?.profiler.endComponent(inst.constructor.name);
285
- }
286
- }
287
- }
288
- finally {
289
- // Guaranteed even if something above throws unexpectedly (e.g. a
290
- // future edit removes the per-component try/catch): isTicking must
291
- // never get stuck true, and structural changes queued during this
292
- // tick must still flush before the next tick runs.
293
- isTicking = false;
294
- flushPending();
295
- }
296
- });
297
- }
298
- function migratePhase(inst, from, to) {
299
- if (from === to)
300
- return;
301
- const oldList = byPhase.get(from);
302
- const idx = oldList.indexOf(inst);
303
- if (idx !== -1)
304
- oldList.splice(idx, 1);
305
- byPhase.get(to).push(inst);
306
- }
307
- /**
308
- * `hotSwap`'s per-instance identity check + swap (T5.4) — split out of
309
- * `hotSwap` itself purely to keep that function's cognitive complexity
310
- * down; matching semantics are documented on `hotSwap` above. Returns
311
- * whether `inst` matched `name` (and was therefore swapped).
312
- */
313
- function swapOneIfMatched(inst, name, NewClass) {
314
- const trackedKey = registryKeyOf.get(inst);
315
- const isMatch = trackedKey !== undefined ? trackedKey === name : inst.constructor.name === name;
316
- if (!isMatch)
317
- return false;
318
- const oldPhase = inst.constructor.phase ?? 'gameLogic';
319
- const newPhase = NewClass.phase ?? 'gameLogic';
320
- linkGameComponentHmrClasses(inst.constructor, NewClass);
321
- Object.setPrototypeOf(inst, NewClass.prototype);
322
- migratePhase(inst, oldPhase, newPhase);
323
- reparseSchemaOnSwap(inst, NewClass, name);
324
- // §7.1-6 / probe8: if this instance's init() is still in flight (attached,
325
- // startInit already called, promise not yet settled), record that a swap
326
- // landed mid-init so `startInit`'s trailing `.then()` re-kicks a fresh
327
- // init() for the NEW class once the OLD one settles, instead of silently
328
- // clearing `pendingInit` and leaving the new init never invoked. An
329
- // instance that is merely QUEUED (attached, `startInit` not yet called —
330
- // `pendingInit.has(inst)` but no `initPromises` entry) needs no help here:
331
- // its eventual first `startInit` call naturally reads the (already
332
- // swapped) NEW prototype's `init`. An instance whose init already fully
333
- // resolved (no `initPromises` entry, `pendingInit` doesn't have it either)
334
- // is intentionally left alone — a swap is a live-patch, not a remount.
335
- if (initPromises.has(inst)) {
336
- pendingReinit.add(inst);
337
- }
338
- return true;
339
- }
340
- /**
341
- * Schema re-parse on swap (T5.4, docs/BACKBONE-TASKS.md's T5.4 detail note).
342
- * `inst` just had its prototype swapped to `NewClass`; if `NewClass` declares
343
- * a `static schema` AND `inst` was attached with recorded authored props
344
- * (registry-keyed instances only — ad-hoc instances have none and are
345
- * skipped, nothing to validate), re-validate those props against the NEW
346
- * schema:
347
- * - success: `next = schema.parse(authoredData)`. Merge ONLY fields
348
- * `next` has that `inst` does NOT already own — i.e. fields the NEW
349
- * schema added (which get their schema default/authored value) — the
350
- * merge never touches a field already present on `inst`, so mutated
351
- * runtime/gameplay state always wins over a re-derived authored value.
352
- * Authored-VALUE edits (the user changing a field in the inspector)
353
- * flow through the normal scene-edit path, never through this merge.
354
- * - failure (old authored data no longer valid against the new schema,
355
- * e.g. a field's type changed or a new field has no default): loud
356
- * swap-miss warning, `inst`'s fields are left exactly as they were
357
- * (last-good props) — never partially or invalidly overwritten.
358
- */
359
- function reparseSchemaOnSwap(inst, NewClass, key) {
360
- const schema = NewClass.schema;
361
- if (!schema)
362
- return;
363
- const authored = authoredPropsOf.get(inst);
364
- if (authored === undefined)
365
- return;
366
- const result = schema.safeParse(authored);
367
- if (!result.success) {
368
- logHmrSwapMiss({ key, reason: 'schema-reparse-failed', error: result.error.message });
369
- return;
370
- }
371
- const next = result.data;
372
- const target = inst;
373
- for (const [field, value] of Object.entries(next)) {
374
- if (!Object.hasOwn(target, field)) {
375
- target[field] = value;
376
- }
377
- }
378
- }
379
- /**
380
- * Enforce the attach rules (T7.2 slice 2, D8 §3) BEFORE any structural
381
- * change happens — called synchronously from the public `attach()`, ahead
382
- * of the isTicking defer, so the throw always surfaces to the caller of
383
- * `attach()` and never fires later out of a deferred `flushPending()` (mid
384
- * -tick attaches are validated up front, at the moment they're requested).
385
- */
386
- function validateAttach(node, instance) {
387
- const ComponentClass = instance.constructor;
388
- const declaredKind = ComponentClass.declaredKind ?? 'threejs';
389
- const label = describeNode(node);
390
- if (kind === 'react') {
391
- throw new Error(`${ComponentClass.name}: cannot attach to entity ${label} — this manager's world is a ` +
392
- "'react' world, and react entities host no GameComponents; render from game state " +
393
- 'via the T7.4 state bridge instead.');
394
- }
395
- if (declaredKind !== 'any' && declaredKind !== kind) {
396
- throw new Error(`${ComponentClass.name}: declared kind '${declaredKind}' does not match entity ` +
397
- `${label}'s world kind '${kind}' — set \`static declaredKind\` on ${ComponentClass.name} ` +
398
- "if it is meant to attach in a non-default-kind world (or declare 'any' for a " +
399
- 'kind-agnostic component).');
400
- }
401
- }
402
- /** Resolve rigidBody/collider refs for `node` from this manager's kind-appropriate
403
- * registry: `Physics2DRegistry` for a 'pixijs' manager, `PhysicsRegistry` otherwise
404
- * (`physics` is only optional for a 'pixijs' manager, which never reaches this
405
- * branch — every real threejs call site still passes it). */
406
- function resolvePhysicsRefs(node) {
407
- if (kind === 'pixijs') {
408
- return physics2d?.get(node);
409
- }
410
- return physics?.get(node);
411
- }
412
- function performAttach(node, instance, registry) {
413
- instance.node = node;
414
- instance.world = resolveWorldInstance(ctx);
415
- const refs = resolvePhysicsRefs(node);
416
- instance.rigidBody = refs?.body ?? null;
417
- instance.collider = refs?.collider ?? null;
418
- if (registry) {
419
- registryKeyOf.set(instance, registry.key);
420
- authoredPropsOf.set(instance, registry.props);
421
- }
422
- const phase = instance.constructor.phase ?? 'gameLogic';
423
- byPhase.get(phase).push(instance);
424
- let entityList = byEntity.get(node);
425
- if (!entityList) {
426
- entityList = [];
427
- byEntity.set(node, entityList);
428
- }
429
- entityList.push(instance);
430
- pendingInit.add(instance);
431
- }
432
- function performDetach(node) {
433
- const instances = byEntity.get(node);
434
- if (!instances)
435
- return;
436
- for (const inst of instances) {
437
- // Isolate dispose() the same way as update(): one component's dispose
438
- // throwing must not stop the rest of this entity's components (or the
439
- // rest of a pending-detach batch) from being cleaned up and removed.
440
- try {
441
- inst.dispose?.(ctx);
442
- }
443
- catch (err) {
444
- console.error(`[component-manager] ${inst.constructor.name}.dispose() threw:`, err);
445
- }
446
- const phase = inst.constructor.phase ?? 'gameLogic';
447
- const phaseList = byPhase.get(phase);
448
- const idx = phaseList.indexOf(inst);
449
- if (idx !== -1)
450
- phaseList.splice(idx, 1);
451
- // Also drop it from the pending-init tracking so a not-yet-flushed
452
- // init() never (re)starts on an already-disposed component (keeps
453
- // detach symmetric with clear()). The in-flight promise, if any, is
454
- // simply abandoned — its `.then()` continuation is now a no-op since
455
- // both maps no longer reference this instance. `pendingReinit` too —
456
- // a swap recorded mid-init on an instance now being detached must not
457
- // re-kick an init() for a component that no longer exists.
458
- pendingInit.delete(inst);
459
- initPromises.delete(inst);
460
- pendingReinit.delete(inst);
461
- }
462
- byEntity.delete(node);
463
- }
464
- return {
465
- /** Attach a component instance to an entity node (Object3D in a threejs
466
- * world, PIXI.Container in a pixijs world). Validates the attach rules
467
- * (§3: react-world throw, kind-mismatch throw) synchronously before any
468
- * structural change, whether or not a phase tick is currently running.
469
- * `registry` (T5.4, optional) records the `componentRegistry` name +
470
- * pre-`schema.parse` authored data a scene-authored attach looked the
471
- * class up under — `hotSwap` keys on it instead of the live
472
- * `constructor.name`. Omit it for an ad-hoc, code-attached instance (no
473
- * registry entry) — `hotSwap` falls back to matching those by class name,
474
- * unchanged from pre-T5.4 behavior. */
475
- attach(node, instance, registry) {
476
- validateAttach(node, instance);
477
- // Called from inside a phase loop (e.g. a spawn-from-update): defer so the
478
- // new instance starts ticking next frame, after a subsequent initAll().
479
- if (isTicking) {
480
- pendingAttach.push({ node, instance, ...(registry ? { registry } : {}) });
481
- return;
482
- }
483
- performAttach(node, instance, registry);
484
- },
485
- /**
486
- * Call init() on all components that haven't been initialized yet, and
487
- * wait for them to resolve. Components attached since the last call are
488
- * covered. Safe to call even when the automatic per-frame flush has
489
- * already started some of them — `startInit` is idempotent per instance,
490
- * so this awaits the same in-flight promise rather than double-invoking
491
- * init(). Manual calls remain useful when subsequent code depends on an
492
- * init()'s side effects having completed synchronously (the automatic
493
- * flush only guarantees "ticking starts once init resolves", not
494
- * "resolved by the time this function returns").
495
- */
496
- async initAll() {
497
- const batch = [...pendingInit];
498
- await Promise.all(batch.map(startInit));
499
- },
500
- /** Remove all components from an entity node, calling dispose. */
501
- detach(node) {
502
- // Called from inside a phase loop (e.g. a despawn-from-update): mark the
503
- // entity's instances so the running loop skips them, and defer the actual
504
- // dispose + removal until the loop completes.
505
- if (isTicking) {
506
- const instances = byEntity.get(node);
507
- if (!instances)
508
- return;
509
- for (const inst of instances)
510
- detachedThisTick.add(inst);
511
- pendingDetach.push(node);
512
- return;
513
- }
514
- performDetach(node);
515
- },
516
- /**
517
- * Backfill `instance.world` for every already-attached instance whose
518
- * world is still undefined (T7.2 slice 2 — closes the KNOWN GAP
519
- * documented on `resolveWorldInstance` above). A ONE-TIME wiring
520
- * completion, called exactly once by `registerThreeWorld`
521
- * (`create-runtime.ts`) right after this manager's `WorldInstance` is
522
- * constructed — NOT a lazy getter, NOT a per-frame sync: a plain
523
- * backfill pass over the existing `byEntity` bookkeeping, with no
524
- * ongoing cost once it returns. Idempotent and safe to call more than
525
- * once (only ever touches instances still missing a world).
526
- */
527
- adoptWorld(world) {
528
- for (const [, instances] of byEntity) {
529
- for (const inst of instances) {
530
- if (inst.world === undefined)
531
- inst.world = world;
532
- }
533
- }
534
- },
535
- /** Get all component instances for an entity node. */
536
- getComponents(node) {
537
- return byEntity.get(node) ?? [];
538
- },
539
- /**
540
- * Get the first attached instance of a given component class on an
541
- * entity node — the typed single-component sibling of `getComponents`
542
- * (issue #99: `onTriggerEnter`'s `other` is a raw node; this is how a
543
- * handler asks "does my trigger partner have a `Collectible` (or
544
- * whatever marker) component?" instead of an untyped `getComponents`
545
- * walk or a fragile `other.name === '...'` display-name check).
546
- * Matches via `instanceof`, so a subclass of `cls` is returned too
547
- * (same subclass handling as `queryByComponent`). Same ordering as
548
- * `getComponents` (attach order for that node) and, like
549
- * `getComponents`, does NOT exclude instances pending-detach this tick
550
- * — a caller inside a phase tick that cares should filter itself, same
551
- * as any other `getComponents` consumer. `undefined` if the node has no
552
- * attached instance of `cls`.
553
- */
554
- getComponent(node, cls) {
555
- const instances = byEntity.get(node);
556
- if (!instances)
557
- return undefined;
558
- for (const inst of instances) {
559
- if (inst instanceof cls)
560
- return inst;
561
- }
562
- return undefined;
563
- },
564
- /**
565
- * Query all live attached instances of a given component class within
566
- * THIS manager — the per-world half of the §5 cross-world query
567
- * (`docs/GAME-ROOT-DESIGN.md` §5; `Game.queryByComponent` in
568
- * `runtime/game.ts` is the game-level aggregation over every world's
569
- * manager).
570
- *
571
- * Returns instances (not nodes), `instanceof cls`, in stable attach
572
- * order — walked from the existing `byEntity` bookkeeping (the same
573
- * structure `hotSwap`/`clear` iterate), not a new index, so correctness
574
- * tracks the manager's real attach/detach state rather than a
575
- * maintained-in-parallel cache. Order note: this is attach order
576
- * PER ENTITY (the order components were pushed for that Object3D),
577
- * concatenated in `byEntity`'s insertion order (first-attach order of
578
- * the entities themselves) — for the common case of each entity
579
- * attached once this coincides with global chronological attach order;
580
- * it would not for an entity re-attached to after other entities were
581
- * attached in between, which this manager does not otherwise track a
582
- * global order for.
583
- *
584
- * Excludes instances pending-detach this tick (`detachedThisTick`) —
585
- * mirrors the same use-after-dispose guard the phase tick applies, so a
586
- * query run mid-tick (e.g. from inside a component's `update`) never
587
- * returns something already torn down this frame.
588
- */
589
- queryByComponent(cls) {
590
- const result = [];
591
- for (const [, instances] of byEntity) {
592
- for (const inst of instances) {
593
- if (detachedThisTick.has(inst))
594
- continue;
595
- if (inst instanceof cls)
596
- result.push(inst);
597
- }
598
- }
599
- return result;
600
- },
601
- /**
602
- * Hot-swap prototypes for every live instance registered under `name`
603
- * (T5.4). State on `this` survives; method bodies update to the new
604
- * class. `name` is matched against each instance's RECORDED registry key
605
- * (`registryKeyOf`, set by `attach()`'s optional `registry` argument) —
606
- * not the live `constructor.name`, which is fragile under minification, a
607
- * class rename, or two distinct classes sharing one runtime name (a
608
- * registry key is stable identity chosen at scene-author time,
609
- * independent of what the class calls itself). An instance attached
610
- * WITHOUT a registry key (ad-hoc/code-attached — no `ComponentRegistry`
611
- * entry) has no such recorded identity, so it falls back to the legacy
612
- * `constructor.name` compare — this is the ONLY place that fallback
613
- * applies; a registry-keyed instance never matches by name, so two
614
- * registry-keyed classes that happen to share a `constructor.name` (e.g.
615
- * both minified to the same short name) swap independently.
616
- *
617
- * Zero matches is a loud swap-miss (`hmr-swap-report.ts`), never a silent
618
- * no-op — this is "renamed class → loud miss" (docs/BACKBONE-TASKS.md's
619
- * T5.4 detail note): if a dev renames a component's export, the hot
620
- * update arrives keyed under the NEW name, no live instance is recorded
621
- * under it yet (the registry/scene JSON hasn't been re-authored), so
622
- * nothing matches and the warning fires — the OLD class stays in effect
623
- * rather than the rename silently doing nothing.
624
- *
625
- * GameComponent-subclass guard (§7.1-5, probe7): every editor HMR call
626
- * site passes any `typeof value === 'function'` module export — that used
627
- * to include a plain helper function sharing a live component's registry
628
- * key/name, which this method would happily `Object.setPrototypeOf` onto
629
- * a live instance, leaving it with no `update()` (a caught-and-logged
630
- * `TypeError` every frame, forever). `NewClass` is now required to have
631
- * `GameComponent` somewhere in its prototype chain — anything else is
632
- * refused OUTRIGHT, loudly, with NO instance touched. This is a second,
633
- * independent line of defense behind the call-site filter
634
- * (`play-mode.ts`'s HMR handlers now filter to `value.prototype
635
- * instanceof GameComponent` before ever calling this) — belt AND
636
- * suspenders, so a future caller that forgets to filter still can't
637
- * corrupt a live instance.
638
- *
639
- * `opts.warnOnMiss` (default `true`, §7.1-4/probe2b): the multi-world HMR
640
- * fan-out (`play-mode.ts`'s `hotSwapAcrossWorlds`) calls this once per
641
- * first-party world and must not let a per-world zero-match here look
642
- * like "renamed class" when the name simply lives in a DIFFERENT world's
643
- * manager — the fan-out helper suppresses this method's own warning
644
- * (`{ warnOnMiss: false }`) and emits the ONE real warning itself only
645
- * when the total across every world is zero. Every other existing caller
646
- * (`applyBrowserComponentEdit`, the dev HMR demos, every hotSwap unit
647
- * test) omits `opts` and keeps today's per-call warn-on-miss behavior
648
- * unchanged. Returns the number of live instances swapped, so a caller
649
- * that needs to aggregate across several managers (the fan-out helper)
650
- * can sum it without re-deriving it from a side-channel.
651
- */
652
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: the GameComponent-subclass guard (§7.1-5) and the warnOnMiss option are both load-bearing early-exit branches ahead of the existing match loop — splitting them into separate helpers would obscure that hotSwap has exactly one guarded entry point
653
- hotSwap(name, NewClass, opts) {
654
- if (typeof NewClass !== 'function' || !(NewClass.prototype instanceof GameComponent)) {
655
- // biome-ignore lint/suspicious/noConsole: structured, greppable — mirrors logHmrSwapMiss's own deliberate direct console.warn
656
- console.warn(`[component-manager] hotSwap("${name}"): refused — ` +
657
- `"${NewClass?.name ?? String(NewClass)}" does not ` +
658
- 'extend GameComponent (no GameComponent in its prototype chain), so no live instance ' +
659
- 'was touched. This guards against a plain function/class export sharing a registry ' +
660
- 'key/name with a real component (§7.1-5, T5.4).');
661
- return 0;
662
- }
663
- let matched = 0;
664
- for (const [, instances] of byEntity) {
665
- for (const inst of instances) {
666
- if (swapOneIfMatched(inst, name, NewClass))
667
- matched++;
668
- }
669
- }
670
- const warnOnMiss = opts?.warnOnMiss ?? true;
671
- if (matched === 0 && warnOnMiss) {
672
- logHmrSwapMiss({ key: name, reason: 'no-live-instance' });
673
- }
674
- return matched;
675
- },
676
- /** Dispose all components and clear all maps. */
677
- clear() {
678
- // Isolate dispose() exactly like performDetach: one throwing dispose
679
- // must not abort the rest of teardown (the remaining instances still
680
- // get dispose()'d, and the maps/state below still get reset). A
681
- // teardown path that aborts partway through is unrecoverable (a retry
682
- // is a no-op since byEntity etc. are never cleared) — see the probe5
683
- // "dispose-abort" finding this closes.
684
- for (const [, instances] of byEntity) {
685
- for (const inst of instances) {
686
- try {
687
- inst.dispose?.(ctx);
688
- }
689
- catch (err) {
690
- console.error(`[component-manager] ${inst.constructor.name}.dispose() threw:`, err);
691
- }
692
- }
693
- }
694
- byEntity.clear();
695
- for (const phase of PHASE_ORDER) {
696
- byPhase.set(phase, []);
697
- }
698
- pendingInit.clear();
699
- initPromises.clear();
700
- pendingReinit.clear();
701
- pendingAttach.length = 0;
702
- pendingDetach.length = 0;
703
- detachedThisTick.clear();
704
- },
705
- };
706
- }