@vgai/engine 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (541) hide show
  1. package/README.md +3 -1
  2. package/dist/adapter/authoring.d.ts +404 -0
  3. package/dist/adapter/authoring.d.ts.map +1 -0
  4. package/dist/adapter/authoring.js +22 -0
  5. package/dist/adapter/colyseus-networking-adapter.d.ts +43 -0
  6. package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -0
  7. package/dist/adapter/colyseus-networking-adapter.js +38 -0
  8. package/dist/adapter/first-party-systems.d.ts +38 -0
  9. package/dist/adapter/first-party-systems.d.ts.map +1 -0
  10. package/dist/adapter/first-party-systems.js +77 -0
  11. package/dist/adapter/game-adapter.d.ts +133 -0
  12. package/dist/adapter/game-adapter.d.ts.map +1 -0
  13. package/dist/adapter/game-adapter.js +10 -0
  14. package/dist/adapter/host-context.d.ts +73 -0
  15. package/dist/adapter/host-context.d.ts.map +1 -0
  16. package/dist/adapter/host-context.js +16 -0
  17. package/dist/adapter/index.d.ts +22 -0
  18. package/dist/adapter/index.d.ts.map +1 -0
  19. package/dist/adapter/index.js +15 -0
  20. package/dist/adapter/ingest/game-contract.d.ts +53 -0
  21. package/dist/adapter/ingest/game-contract.d.ts.map +1 -0
  22. package/dist/adapter/ingest/game-contract.js +30 -0
  23. package/dist/adapter/ingest/overlay-applier.d.ts +118 -0
  24. package/dist/adapter/ingest/overlay-applier.d.ts.map +1 -0
  25. package/dist/adapter/ingest/overlay-applier.js +132 -0
  26. package/dist/adapter/ingest/overlay-apply.d.ts +60 -0
  27. package/dist/adapter/ingest/overlay-apply.d.ts.map +1 -0
  28. package/dist/adapter/ingest/overlay-apply.js +101 -0
  29. package/dist/adapter/ingest/overlay-file.d.ts +79 -0
  30. package/dist/adapter/ingest/overlay-file.d.ts.map +1 -0
  31. package/dist/adapter/ingest/overlay-file.js +66 -0
  32. package/dist/adapter/ingest/overlay-report.d.ts +113 -0
  33. package/dist/adapter/ingest/overlay-report.d.ts.map +1 -0
  34. package/dist/adapter/ingest/overlay-report.js +103 -0
  35. package/dist/adapter/ingest/scene-capture.d.ts +97 -0
  36. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -0
  37. package/dist/adapter/ingest/scene-capture.js +248 -0
  38. package/dist/adapter/ingest/upstream-pin.d.ts +47 -0
  39. package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -0
  40. package/dist/adapter/ingest/upstream-pin.js +50 -0
  41. package/dist/adapter/loop-gate-report.d.ts +34 -0
  42. package/dist/adapter/loop-gate-report.d.ts.map +1 -0
  43. package/dist/adapter/loop-gate-report.js +37 -0
  44. package/dist/adapter/rapier-physics-adapter.d.ts +12 -0
  45. package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -0
  46. package/dist/adapter/rapier-physics-adapter.js +46 -0
  47. package/dist/adapter/system-adapter.d.ts +133 -0
  48. package/dist/adapter/system-adapter.d.ts.map +1 -0
  49. package/dist/adapter/system-adapter.js +13 -0
  50. package/dist/adapter/transform.d.ts +18 -0
  51. package/dist/adapter/transform.d.ts.map +1 -0
  52. package/dist/adapter/transform.js +1 -0
  53. package/dist/adapter/vgai-scene-game-adapter.d.ts +116 -0
  54. package/dist/adapter/vgai-scene-game-adapter.d.ts.map +1 -0
  55. package/dist/adapter/vgai-scene-game-adapter.js +714 -0
  56. package/dist/adapter/world-kind.d.ts +30 -0
  57. package/dist/adapter/world-kind.d.ts.map +1 -0
  58. package/dist/adapter/world-kind.js +18 -0
  59. package/dist/ai/navigation.d.ts +64 -0
  60. package/dist/ai/navigation.d.ts.map +1 -0
  61. package/dist/ai/navigation.js +133 -0
  62. package/dist/animation/anim-graph-types.d.ts +25 -0
  63. package/dist/animation/anim-graph-types.d.ts.map +1 -0
  64. package/dist/animation/anim-graph-types.js +1 -0
  65. package/dist/animation/animation-clock.d.ts +213 -0
  66. package/dist/animation/animation-clock.d.ts.map +1 -0
  67. package/dist/animation/animation-clock.js +301 -0
  68. package/dist/animation/blend-node.d.ts +22 -0
  69. package/dist/animation/blend-node.d.ts.map +1 -0
  70. package/dist/animation/blend-node.js +97 -0
  71. package/dist/animation/camera-ownership.d.ts +250 -0
  72. package/dist/animation/camera-ownership.d.ts.map +1 -0
  73. package/dist/animation/camera-ownership.js +169 -0
  74. package/dist/animation/cinematic-cues.d.ts +200 -0
  75. package/dist/animation/cinematic-cues.d.ts.map +1 -0
  76. package/dist/animation/cinematic-cues.js +213 -0
  77. package/dist/animation/clip-map.d.ts +12 -0
  78. package/dist/animation/clip-map.d.ts.map +1 -0
  79. package/dist/animation/clip-map.js +37 -0
  80. package/dist/animation/gsap-registration.d.ts +119 -0
  81. package/dist/animation/gsap-registration.d.ts.map +1 -0
  82. package/dist/animation/gsap-registration.js +92 -0
  83. package/dist/animation/theatre-clock-binding.d.ts +89 -0
  84. package/dist/animation/theatre-clock-binding.d.ts.map +1 -0
  85. package/dist/animation/theatre-clock-binding.js +29 -0
  86. package/dist/animation/theatre-director.d.ts +242 -0
  87. package/dist/animation/theatre-director.d.ts.map +1 -0
  88. package/dist/animation/theatre-director.js +235 -0
  89. package/dist/animation/theatre-object-binding.d.ts +305 -0
  90. package/dist/animation/theatre-object-binding.d.ts.map +1 -0
  91. package/dist/animation/theatre-object-binding.js +351 -0
  92. package/dist/animation/xstate-animation-binding.d.ts +111 -0
  93. package/dist/animation/xstate-animation-binding.d.ts.map +1 -0
  94. package/dist/animation/xstate-animation-binding.js +307 -0
  95. package/dist/animation/xstate-animation-meta.d.ts +228 -0
  96. package/dist/animation/xstate-animation-meta.d.ts.map +1 -0
  97. package/dist/animation/xstate-animation-meta.js +221 -0
  98. package/dist/assets.d.ts +26 -0
  99. package/dist/assets.d.ts.map +1 -0
  100. package/dist/assets.js +55 -0
  101. package/dist/audio/index.d.ts +4 -0
  102. package/dist/audio/index.d.ts.map +1 -0
  103. package/dist/audio/index.js +24 -0
  104. package/dist/audio/tone-clock-binding.d.ts +75 -0
  105. package/dist/audio/tone-clock-binding.d.ts.map +1 -0
  106. package/dist/audio/tone-clock-binding.js +29 -0
  107. package/dist/audio/tone-context.d.ts +114 -0
  108. package/dist/audio/tone-context.d.ts.map +1 -0
  109. package/dist/audio/tone-context.js +39 -0
  110. package/dist/audio/tone-offline-render.d.ts +90 -0
  111. package/dist/audio/tone-offline-render.d.ts.map +1 -0
  112. package/dist/audio/tone-offline-render.js +56 -0
  113. package/dist/audio/wav-encode.d.ts +38 -0
  114. package/dist/audio/wav-encode.d.ts.map +1 -0
  115. package/dist/audio/wav-encode.js +97 -0
  116. package/dist/core/game-loop.d.ts +21 -0
  117. package/dist/core/game-loop.d.ts.map +1 -0
  118. package/dist/core/game-loop.js +130 -0
  119. package/dist/core/system-runner.d.ts +120 -0
  120. package/dist/core/system-runner.d.ts.map +1 -0
  121. package/dist/core/system-runner.js +286 -0
  122. package/dist/core/types.d.ts +58 -0
  123. package/dist/core/types.d.ts.map +1 -0
  124. package/dist/core/types.js +25 -0
  125. package/dist/data/data-asset.d.ts +101 -0
  126. package/dist/data/data-asset.d.ts.map +1 -0
  127. package/dist/data/data-asset.js +122 -0
  128. package/dist/data/data-check-core.d.ts +110 -0
  129. package/dist/data/data-check-core.d.ts.map +1 -0
  130. package/dist/data/data-check-core.js +178 -0
  131. package/dist/data/data-ref.d.ts +79 -0
  132. package/dist/data/data-ref.d.ts.map +1 -0
  133. package/dist/data/data-ref.js +130 -0
  134. package/dist/data/vite-plugin-data.d.ts +79 -0
  135. package/dist/data/vite-plugin-data.d.ts.map +1 -0
  136. package/dist/data/vite-plugin-data.js +217 -0
  137. package/dist/dev/console-bridge.d.ts +22 -0
  138. package/dist/dev/console-bridge.d.ts.map +1 -0
  139. package/dist/dev/console-bridge.js +71 -0
  140. package/dist/dev/debug-draw.d.ts +24 -0
  141. package/dist/dev/debug-draw.d.ts.map +1 -0
  142. package/dist/dev/debug-draw.js +73 -0
  143. package/dist/dev/logger.d.ts +30 -0
  144. package/dist/dev/logger.d.ts.map +1 -0
  145. package/dist/dev/logger.js +90 -0
  146. package/dist/ecs/component-manager.d.ts +189 -0
  147. package/dist/ecs/component-manager.d.ts.map +1 -0
  148. package/dist/ecs/component-manager.js +676 -0
  149. package/dist/ecs/game-component.d.ts +97 -0
  150. package/dist/ecs/game-component.d.ts.map +1 -0
  151. package/dist/ecs/game-component.js +80 -0
  152. package/dist/ecs/hmr-swap-report.d.ts +53 -0
  153. package/dist/ecs/hmr-swap-report.d.ts.map +1 -0
  154. package/dist/ecs/hmr-swap-report.js +55 -0
  155. package/dist/index.d.ts +37 -0
  156. package/dist/index.d.ts.map +1 -0
  157. package/dist/index.js +30 -0
  158. package/dist/input/input-manager.d.ts +596 -0
  159. package/dist/input/input-manager.d.ts.map +1 -0
  160. package/dist/input/input-manager.js +1564 -0
  161. package/dist/input/input-types.d.ts +171 -0
  162. package/dist/input/input-types.d.ts.map +1 -0
  163. package/dist/input/input-types.js +1 -0
  164. package/dist/input/prompt-labels.d.ts +21 -0
  165. package/dist/input/prompt-labels.d.ts.map +1 -0
  166. package/dist/input/prompt-labels.js +121 -0
  167. package/dist/input/rebind-controller.d.ts +59 -0
  168. package/dist/input/rebind-controller.d.ts.map +1 -0
  169. package/dist/input/rebind-controller.js +85 -0
  170. package/dist/input/schema.d.ts +16 -0
  171. package/dist/input/schema.d.ts.map +1 -0
  172. package/dist/input/schema.js +228 -0
  173. package/dist/loader.d.ts +43 -0
  174. package/dist/loader.d.ts.map +1 -0
  175. package/dist/loader.js +59 -0
  176. package/dist/manifest/index.d.ts +5 -0
  177. package/dist/manifest/index.d.ts.map +1 -0
  178. package/dist/manifest/index.js +8 -0
  179. package/dist/manifest/load-file.d.ts +4 -0
  180. package/dist/manifest/load-file.d.ts.map +1 -0
  181. package/dist/manifest/load-file.js +14 -0
  182. package/dist/manifest/load.d.ts +117 -0
  183. package/dist/manifest/load.d.ts.map +1 -0
  184. package/dist/manifest/load.js +255 -0
  185. package/dist/manifest/schema.d.ts +173 -0
  186. package/dist/manifest/schema.d.ts.map +1 -0
  187. package/dist/manifest/schema.js +302 -0
  188. package/dist/physics/collision-system.d.ts +41 -0
  189. package/dist/physics/collision-system.d.ts.map +1 -0
  190. package/dist/physics/collision-system.js +64 -0
  191. package/dist/physics/physics-registry.d.ts +45 -0
  192. package/dist/physics/physics-registry.d.ts.map +1 -0
  193. package/dist/physics/physics-registry.js +65 -0
  194. package/dist/physics/transform-writer.d.ts +16 -0
  195. package/dist/physics/transform-writer.d.ts.map +1 -0
  196. package/dist/physics/transform-writer.js +38 -0
  197. package/dist/physics/trigger-dispatch.d.ts +24 -0
  198. package/dist/physics/trigger-dispatch.d.ts.map +1 -0
  199. package/dist/physics/trigger-dispatch.js +80 -0
  200. package/dist/react/game-state.d.ts +105 -0
  201. package/dist/react/game-state.d.ts.map +1 -0
  202. package/dist/react/game-state.js +144 -0
  203. package/dist/react/use-data.d.ts +60 -0
  204. package/dist/react/use-data.d.ts.map +1 -0
  205. package/dist/react/use-data.js +96 -0
  206. package/dist/react/use-selection.d.ts +75 -0
  207. package/dist/react/use-selection.d.ts.map +1 -0
  208. package/dist/react/use-selection.js +90 -0
  209. package/dist/render/auto-batcher.d.ts +34 -0
  210. package/dist/render/auto-batcher.d.ts.map +1 -0
  211. package/dist/render/auto-batcher.js +140 -0
  212. package/dist/render/render-batch-system.d.ts +33 -0
  213. package/dist/render/render-batch-system.d.ts.map +1 -0
  214. package/dist/render/render-batch-system.js +230 -0
  215. package/dist/render/render-features.d.ts +47 -0
  216. package/dist/render/render-features.d.ts.map +1 -0
  217. package/dist/render/render-features.js +105 -0
  218. package/dist/render/render-settings.d.ts +36 -0
  219. package/dist/render/render-settings.d.ts.map +1 -0
  220. package/dist/render/render-settings.js +51 -0
  221. package/dist/runtime/create-runtime.d.ts +280 -0
  222. package/dist/runtime/create-runtime.d.ts.map +1 -0
  223. package/dist/runtime/create-runtime.js +795 -0
  224. package/dist/runtime/frame-selector-cache.d.ts +41 -0
  225. package/dist/runtime/frame-selector-cache.d.ts.map +1 -0
  226. package/dist/runtime/frame-selector-cache.js +65 -0
  227. package/dist/runtime/game.d.ts +433 -0
  228. package/dist/runtime/game.d.ts.map +1 -0
  229. package/dist/runtime/game.js +569 -0
  230. package/dist/runtime/input-router.d.ts +78 -0
  231. package/dist/runtime/input-router.d.ts.map +1 -0
  232. package/dist/runtime/input-router.js +171 -0
  233. package/dist/runtime/mount-game.d.ts +129 -0
  234. package/dist/runtime/mount-game.d.ts.map +1 -0
  235. package/dist/runtime/mount-game.js +152 -0
  236. package/dist/runtime/mount-manifest.d.ts +119 -0
  237. package/dist/runtime/mount-manifest.d.ts.map +1 -0
  238. package/dist/runtime/mount-manifest.js +204 -0
  239. package/dist/runtime/render-audio-control.d.ts +110 -0
  240. package/dist/runtime/render-audio-control.d.ts.map +1 -0
  241. package/dist/runtime/render-audio-control.js +111 -0
  242. package/dist/runtime/render-control.d.ts +291 -0
  243. package/dist/runtime/render-control.d.ts.map +1 -0
  244. package/dist/runtime/render-control.js +264 -0
  245. package/dist/runtime/render-seed.d.ts +57 -0
  246. package/dist/runtime/render-seed.d.ts.map +1 -0
  247. package/dist/runtime/render-seed.js +74 -0
  248. package/dist/runtime/scene-ui-bridge.d.ts +69 -0
  249. package/dist/runtime/scene-ui-bridge.d.ts.map +1 -0
  250. package/dist/runtime/scene-ui-bridge.js +23 -0
  251. package/dist/runtime/scene-ui-data.d.ts +13 -0
  252. package/dist/runtime/scene-ui-data.d.ts.map +1 -0
  253. package/dist/runtime/scene-ui-data.js +92 -0
  254. package/dist/runtime/state-bridge.d.ts +47 -0
  255. package/dist/runtime/state-bridge.d.ts.map +1 -0
  256. package/dist/runtime/state-bridge.js +53 -0
  257. package/dist/runtime/types.d.ts +179 -0
  258. package/dist/runtime/types.d.ts.map +1 -0
  259. package/dist/runtime/types.js +1 -0
  260. package/dist/scene/asset-loaders.d.ts +79 -0
  261. package/dist/scene/asset-loaders.d.ts.map +1 -0
  262. package/dist/scene/asset-loaders.js +141 -0
  263. package/dist/scene/asset-paths.d.ts +11 -0
  264. package/dist/scene/asset-paths.d.ts.map +1 -0
  265. package/dist/scene/asset-paths.js +108 -0
  266. package/dist/scene/asset-ref-check.d.ts +88 -0
  267. package/dist/scene/asset-ref-check.d.ts.map +1 -0
  268. package/dist/scene/asset-ref-check.js +214 -0
  269. package/dist/scene/asset-registry.d.ts +30 -0
  270. package/dist/scene/asset-registry.d.ts.map +1 -0
  271. package/dist/scene/asset-registry.js +50 -0
  272. package/dist/scene/collider-dimensions.d.ts +63 -0
  273. package/dist/scene/collider-dimensions.d.ts.map +1 -0
  274. package/dist/scene/collider-dimensions.js +98 -0
  275. package/dist/scene/component-registry.d.ts +17 -0
  276. package/dist/scene/component-registry.d.ts.map +1 -0
  277. package/dist/scene/component-registry.js +28 -0
  278. package/dist/scene/defaults.d.ts +252 -0
  279. package/dist/scene/defaults.d.ts.map +1 -0
  280. package/dist/scene/defaults.js +151 -0
  281. package/dist/scene/geometries/index.d.ts +8 -0
  282. package/dist/scene/geometries/index.d.ts.map +1 -0
  283. package/dist/scene/geometries/index.js +7 -0
  284. package/dist/scene/geometries/terrain.d.ts +2 -0
  285. package/dist/scene/geometries/terrain.d.ts.map +1 -0
  286. package/dist/scene/geometries/terrain.js +37 -0
  287. package/dist/scene/geometry-registry.d.ts +24 -0
  288. package/dist/scene/geometry-registry.d.ts.map +1 -0
  289. package/dist/scene/geometry-registry.js +20 -0
  290. package/dist/scene/instance-registry.d.ts +36 -0
  291. package/dist/scene/instance-registry.d.ts.map +1 -0
  292. package/dist/scene/instance-registry.js +55 -0
  293. package/dist/scene/instancers/grid.d.ts +2 -0
  294. package/dist/scene/instancers/grid.d.ts.map +1 -0
  295. package/dist/scene/instancers/grid.js +36 -0
  296. package/dist/scene/instancers/index.d.ts +8 -0
  297. package/dist/scene/instancers/index.d.ts.map +1 -0
  298. package/dist/scene/instancers/index.js +7 -0
  299. package/dist/scene/light-camera-factory.d.ts +23 -0
  300. package/dist/scene/light-camera-factory.d.ts.map +1 -0
  301. package/dist/scene/light-camera-factory.js +68 -0
  302. package/dist/scene/material-factory.d.ts +17 -0
  303. package/dist/scene/material-factory.d.ts.map +1 -0
  304. package/dist/scene/material-factory.js +197 -0
  305. package/dist/scene/material-registry.d.ts +40 -0
  306. package/dist/scene/material-registry.d.ts.map +1 -0
  307. package/dist/scene/material-registry.js +44 -0
  308. package/dist/scene/materials/index.d.ts +8 -0
  309. package/dist/scene/materials/index.d.ts.map +1 -0
  310. package/dist/scene/materials/index.js +7 -0
  311. package/dist/scene/materials/water.d.ts +2 -0
  312. package/dist/scene/materials/water.d.ts.map +1 -0
  313. package/dist/scene/materials/water.js +54 -0
  314. package/dist/scene/parse.d.ts +23 -0
  315. package/dist/scene/parse.d.ts.map +1 -0
  316. package/dist/scene/parse.js +179 -0
  317. package/dist/scene/particles-factory.d.ts +33 -0
  318. package/dist/scene/particles-factory.d.ts.map +1 -0
  319. package/dist/scene/particles-factory.js +282 -0
  320. package/dist/scene/scene-apply.d.ts +109 -0
  321. package/dist/scene/scene-apply.d.ts.map +1 -0
  322. package/dist/scene/scene-apply.js +326 -0
  323. package/dist/scene/scene-diff-schema.d.ts +285 -0
  324. package/dist/scene/scene-diff-schema.d.ts.map +1 -0
  325. package/dist/scene/scene-diff-schema.js +92 -0
  326. package/dist/scene/scene-diff-types.d.ts +40 -0
  327. package/dist/scene/scene-diff-types.d.ts.map +1 -0
  328. package/dist/scene/scene-diff-types.js +14 -0
  329. package/dist/scene/scene-loader.d.ts +177 -0
  330. package/dist/scene/scene-loader.d.ts.map +1 -0
  331. package/dist/scene/scene-loader.js +1213 -0
  332. package/dist/scene/scene-query.d.ts +11 -0
  333. package/dist/scene/scene-query.d.ts.map +1 -0
  334. package/dist/scene/scene-query.js +63 -0
  335. package/dist/scene/scene-types.d.ts +10 -0
  336. package/dist/scene/scene-types.d.ts.map +1 -0
  337. package/dist/scene/scene-types.js +8 -0
  338. package/dist/scene/scene-version.d.ts +26 -0
  339. package/dist/scene/scene-version.d.ts.map +1 -0
  340. package/dist/scene/scene-version.js +34 -0
  341. package/dist/scene/schema/animation.d.ts +8 -0
  342. package/dist/scene/schema/animation.d.ts.map +1 -0
  343. package/dist/scene/schema/animation.js +42 -0
  344. package/dist/scene/schema/audio.d.ts +13 -0
  345. package/dist/scene/schema/audio.d.ts.map +1 -0
  346. package/dist/scene/schema/audio.js +22 -0
  347. package/dist/scene/schema/camera.d.ts +28 -0
  348. package/dist/scene/schema/camera.d.ts.map +1 -0
  349. package/dist/scene/schema/camera.js +17 -0
  350. package/dist/scene/schema/collider.d.ts +40 -0
  351. package/dist/scene/schema/collider.d.ts.map +1 -0
  352. package/dist/scene/schema/collider.js +55 -0
  353. package/dist/scene/schema/entity-ref.d.ts +31 -0
  354. package/dist/scene/schema/entity-ref.d.ts.map +1 -0
  355. package/dist/scene/schema/entity-ref.js +61 -0
  356. package/dist/scene/schema/entity.d.ts +1036 -0
  357. package/dist/scene/schema/entity.d.ts.map +1 -0
  358. package/dist/scene/schema/entity.js +153 -0
  359. package/dist/scene/schema/environment.d.ts +425 -0
  360. package/dist/scene/schema/environment.d.ts.map +1 -0
  361. package/dist/scene/schema/environment.js +357 -0
  362. package/dist/scene/schema/index.d.ts +39 -0
  363. package/dist/scene/schema/index.d.ts.map +1 -0
  364. package/dist/scene/schema/index.js +20 -0
  365. package/dist/scene/schema/instances.d.ts +14 -0
  366. package/dist/scene/schema/instances.d.ts.map +1 -0
  367. package/dist/scene/schema/instances.js +28 -0
  368. package/dist/scene/schema/joint.d.ts +23 -0
  369. package/dist/scene/schema/joint.d.ts.map +1 -0
  370. package/dist/scene/schema/joint.js +23 -0
  371. package/dist/scene/schema/light.d.ts +38 -0
  372. package/dist/scene/schema/light.d.ts.map +1 -0
  373. package/dist/scene/schema/light.js +26 -0
  374. package/dist/scene/schema/material.d.ts +199 -0
  375. package/dist/scene/schema/material.d.ts.map +1 -0
  376. package/dist/scene/schema/material.js +99 -0
  377. package/dist/scene/schema/mesh.d.ts +48 -0
  378. package/dist/scene/schema/mesh.d.ts.map +1 -0
  379. package/dist/scene/schema/mesh.js +82 -0
  380. package/dist/scene/schema/particles.d.ts +2178 -0
  381. package/dist/scene/schema/particles.d.ts.map +1 -0
  382. package/dist/scene/schema/particles.js +321 -0
  383. package/dist/scene/schema/physics.d.ts +56 -0
  384. package/dist/scene/schema/physics.d.ts.map +1 -0
  385. package/dist/scene/schema/physics.js +37 -0
  386. package/dist/scene/schema/scene-file.d.ts +304 -0
  387. package/dist/scene/schema/scene-file.d.ts.map +1 -0
  388. package/dist/scene/schema/scene-file.js +262 -0
  389. package/dist/scene/schema/shadow.d.ts +28 -0
  390. package/dist/scene/schema/shadow.d.ts.map +1 -0
  391. package/dist/scene/schema/shadow.js +20 -0
  392. package/dist/scene/schema/spline.d.ts +25 -0
  393. package/dist/scene/schema/spline.d.ts.map +1 -0
  394. package/dist/scene/schema/spline.js +15 -0
  395. package/dist/scene/schema/tuples.d.ts +12 -0
  396. package/dist/scene/schema/tuples.d.ts.map +1 -0
  397. package/dist/scene/schema/tuples.js +14 -0
  398. package/dist/scene/schema/ui.d.ts +880 -0
  399. package/dist/scene/schema/ui.d.ts.map +1 -0
  400. package/dist/scene/schema/ui.js +506 -0
  401. package/dist/scene/user-data.d.ts +171 -0
  402. package/dist/scene/user-data.d.ts.map +1 -0
  403. package/dist/scene/user-data.js +137 -0
  404. package/dist/setup/setup-audio.d.ts +20 -0
  405. package/dist/setup/setup-audio.d.ts.map +1 -0
  406. package/dist/setup/setup-audio.js +41 -0
  407. package/dist/setup/setup-particles.d.ts +17 -0
  408. package/dist/setup/setup-particles.d.ts.map +1 -0
  409. package/dist/setup/setup-particles.js +16 -0
  410. package/dist/setup/setup-physics.d.ts +25 -0
  411. package/dist/setup/setup-physics.d.ts.map +1 -0
  412. package/dist/setup/setup-physics.js +51 -0
  413. package/dist/setup/setup-renderer.d.ts +69 -0
  414. package/dist/setup/setup-renderer.d.ts.map +1 -0
  415. package/dist/setup/setup-renderer.js +363 -0
  416. package/dist/tools/define-tool.d.ts +105 -0
  417. package/dist/tools/define-tool.d.ts.map +1 -0
  418. package/dist/tools/define-tool.js +82 -0
  419. package/dist/world2d/authoring-2d.d.ts +76 -0
  420. package/dist/world2d/authoring-2d.d.ts.map +1 -0
  421. package/dist/world2d/authoring-2d.js +176 -0
  422. package/dist/world2d/capture-to-scene2d.d.ts +5 -0
  423. package/dist/world2d/capture-to-scene2d.d.ts.map +1 -0
  424. package/dist/world2d/capture-to-scene2d.js +49 -0
  425. package/dist/world2d/collision-2d.d.ts +52 -0
  426. package/dist/world2d/collision-2d.d.ts.map +1 -0
  427. package/dist/world2d/collision-2d.js +70 -0
  428. package/dist/world2d/components-2d.d.ts +47 -0
  429. package/dist/world2d/components-2d.d.ts.map +1 -0
  430. package/dist/world2d/components-2d.js +77 -0
  431. package/dist/world2d/index.d.ts +34 -0
  432. package/dist/world2d/index.d.ts.map +1 -0
  433. package/dist/world2d/index.js +32 -0
  434. package/dist/world2d/ingest-iframe-2d.d.ts +82 -0
  435. package/dist/world2d/ingest-iframe-2d.d.ts.map +1 -0
  436. package/dist/world2d/ingest-iframe-2d.js +166 -0
  437. package/dist/world2d/ingest2d.d.ts +97 -0
  438. package/dist/world2d/ingest2d.d.ts.map +1 -0
  439. package/dist/world2d/ingest2d.js +40 -0
  440. package/dist/world2d/physics2d-registry.d.ts +24 -0
  441. package/dist/world2d/physics2d-registry.d.ts.map +1 -0
  442. package/dist/world2d/physics2d-registry.js +38 -0
  443. package/dist/world2d/pixi-game-adapter.d.ts +107 -0
  444. package/dist/world2d/pixi-game-adapter.d.ts.map +1 -0
  445. package/dist/world2d/pixi-game-adapter.js +211 -0
  446. package/dist/world2d/pixi-surface.d.ts +39 -0
  447. package/dist/world2d/pixi-surface.d.ts.map +1 -0
  448. package/dist/world2d/pixi-surface.js +47 -0
  449. package/dist/world2d/scene-capture-2d.d.ts +43 -0
  450. package/dist/world2d/scene-capture-2d.d.ts.map +1 -0
  451. package/dist/world2d/scene-capture-2d.js +92 -0
  452. package/dist/world2d/scene2d-loader.d.ts +27 -0
  453. package/dist/world2d/scene2d-loader.d.ts.map +1 -0
  454. package/dist/world2d/scene2d-loader.js +274 -0
  455. package/dist/world2d/schema/entity2d.d.ts +102 -0
  456. package/dist/world2d/schema/entity2d.d.ts.map +1 -0
  457. package/dist/world2d/schema/entity2d.js +119 -0
  458. package/dist/world2d/schema/physics2d.d.ts +46 -0
  459. package/dist/world2d/schema/physics2d.d.ts.map +1 -0
  460. package/dist/world2d/schema/physics2d.js +45 -0
  461. package/dist/world2d/schema/sprite.d.ts +42 -0
  462. package/dist/world2d/schema/sprite.d.ts.map +1 -0
  463. package/dist/world2d/schema/sprite.js +63 -0
  464. package/dist/world2d/schema/tilemap.d.ts +15 -0
  465. package/dist/world2d/schema/tilemap.d.ts.map +1 -0
  466. package/dist/world2d/schema/tilemap.js +19 -0
  467. package/dist/world2d/schema/tuples2d.d.ts +20 -0
  468. package/dist/world2d/schema/tuples2d.d.ts.map +1 -0
  469. package/dist/world2d/schema/tuples2d.js +19 -0
  470. package/dist/world2d/system-adapters-2d.d.ts +16 -0
  471. package/dist/world2d/system-adapters-2d.d.ts.map +1 -0
  472. package/dist/world2d/system-adapters-2d.js +37 -0
  473. package/dist/world2d/transform-writer-2d.d.ts +16 -0
  474. package/dist/world2d/transform-writer-2d.d.ts.map +1 -0
  475. package/dist/world2d/transform-writer-2d.js +22 -0
  476. package/dist/world2d/types.d.ts +53 -0
  477. package/dist/world2d/types.d.ts.map +1 -0
  478. package/dist/world2d/types.js +1 -0
  479. package/package.json +18 -4
  480. package/src/adapter/authoring.ts +10 -0
  481. package/src/adapter/first-party-systems.ts +23 -34
  482. package/src/adapter/index.ts +1 -3
  483. package/src/adapter/system-adapter.ts +15 -22
  484. package/src/adapter/vgai-scene-game-adapter.ts +5 -14
  485. package/src/animation/anim-graph-types.ts +12 -43
  486. package/src/animation/animation-clock.ts +479 -0
  487. package/src/animation/camera-ownership.ts +467 -0
  488. package/src/animation/cinematic-cues.ts +451 -0
  489. package/src/animation/clip-map.ts +41 -0
  490. package/src/animation/gsap-registration.ts +184 -0
  491. package/src/animation/theatre-clock-binding.ts +111 -0
  492. package/src/animation/theatre-director.ts +347 -0
  493. package/src/animation/theatre-object-binding.ts +661 -0
  494. package/src/animation/xstate-animation-binding.ts +398 -0
  495. package/src/animation/xstate-animation-meta.ts +282 -0
  496. package/src/audio/index.ts +39 -7
  497. package/src/audio/tone-clock-binding.ts +98 -0
  498. package/src/audio/tone-context.ts +129 -0
  499. package/src/audio/tone-offline-render.ts +167 -0
  500. package/src/audio/wav-encode.ts +119 -0
  501. package/src/core/game-loop.ts +17 -0
  502. package/src/core/types.ts +13 -0
  503. package/src/data/data-asset.ts +167 -0
  504. package/src/data/data-check-core.ts +242 -0
  505. package/src/data/data-ref.ts +145 -0
  506. package/src/data/vite-plugin-data.ts +290 -0
  507. package/src/index.ts +48 -0
  508. package/src/input/input-manager.ts +1206 -32
  509. package/src/input/input-types.ts +155 -3
  510. package/src/input/prompt-labels.ts +122 -0
  511. package/src/input/rebind-controller.ts +105 -0
  512. package/src/input/schema.ts +206 -52
  513. package/src/react/use-data.ts +114 -0
  514. package/src/react/use-selection.tsx +135 -0
  515. package/src/runtime/create-runtime.ts +30 -9
  516. package/src/runtime/render-audio-control.ts +168 -0
  517. package/src/runtime/render-control.ts +522 -0
  518. package/src/runtime/render-seed.ts +79 -0
  519. package/src/runtime/types.ts +0 -5
  520. package/src/scene/asset-loaders.ts +10 -36
  521. package/src/scene/asset-paths.ts +0 -2
  522. package/src/scene/asset-ref-check.ts +248 -0
  523. package/src/scene/parse.ts +133 -0
  524. package/src/scene/scene-loader.ts +81 -96
  525. package/src/scene/schema/animation.ts +30 -79
  526. package/src/scene/schema/entity.ts +20 -0
  527. package/src/scene/schema/index.ts +2 -12
  528. package/src/scene/user-data.ts +8 -9
  529. package/src/setup/setup-renderer.ts +9 -2
  530. package/src/tools/define-tool.ts +152 -0
  531. package/src/animation/anim-graph.ts +0 -406
  532. package/src/animation/anim-system.ts +0 -28
  533. package/src/animation/property-track.ts +0 -178
  534. package/src/animation/schema.ts +0 -204
  535. package/src/audio/ambient.ts +0 -300
  536. package/src/audio/impacts.ts +0 -212
  537. package/src/audio/movement.ts +0 -140
  538. package/src/audio/musical.ts +0 -200
  539. package/src/audio/ui-sounds.ts +0 -171
  540. package/src/audio/vehicle.ts +0 -235
  541. package/src/audio/weapons.ts +0 -152
@@ -0,0 +1,795 @@
1
+ import * as THREE from 'three';
2
+ import { VgaiSceneGameAdapter } from '../adapter/vgai-scene-game-adapter';
3
+ import { assertNever } from '../adapter/world-kind';
4
+ import { createAssetCache } from '../assets';
5
+ import { createGameLoop } from '../core/game-loop';
6
+ import { createHostRenderer } from '../setup/setup-renderer';
7
+ import { createGame, createWorldInstance, isFirstPartyMounted, } from './game';
8
+ import { createInputRouter, stackOrder } from './input-router';
9
+ // Re-export the camera-precedence helper from its new home so existing importers
10
+ // (`import { adoptSceneCamera } from '@engine/runtime/create-runtime'`) keep working.
11
+ export { adoptSceneCamera } from '../adapter/vgai-scene-game-adapter';
12
+ /**
13
+ * Register a threejs world for a freshly-mounted game onto the Game shell.
14
+ * Shared by `createGameRuntime` and every headless test harness
15
+ * (`test/game-root.test.ts`, `test/frame-order.test.ts`,
16
+ * `test/game-two-worlds.test.ts`) so there is exactly one code path for this
17
+ * wiring (T7.1 slice 1, id-generalized in slice 3 for the two-world proof —
18
+ * `docs/GAME-ROOT-DESIGN.md` §8 stage 3) — `physics`/`collisions`/`camera`/
19
+ * `frame` are populated from the mount's first-party `GameContext`/
20
+ * `VgaiMountedGame` when available, left `undefined` otherwise (an external
21
+ * adapter's mount has none of these first-party handles — `frame` in
22
+ * particular is what makes `GameInternal.runFrame` (T7.1 slice 2) fall back
23
+ * to a single opaque `mounted.update` call per substep for such a world).
24
+ */
25
+ function firstPartyExtras(mounted) {
26
+ if (!isFirstPartyMounted(mounted))
27
+ return {};
28
+ return {
29
+ physics: mounted.ctx.physics,
30
+ collisions: mounted.ctx.collisions,
31
+ camera: mounted.ctx.camera,
32
+ frame: mounted.frame,
33
+ };
34
+ }
35
+ /**
36
+ * Register a threejs world onto the Game shell. `opts.id` defaults to
37
+ * `'main'` — the single-world callers (`createGameRuntime`, slice-1/2 tests)
38
+ * are unaffected; a second/third world (T7.1 slice 3's two-world proof, and
39
+ * real multi-world manifests later) pass an explicit id. This is the ONE
40
+ * registration code path for a threejs world, whatever its id.
41
+ */
42
+ export function registerThreeWorld(game, adapter, mounted, opts) {
43
+ const world = createWorldInstance({
44
+ id: opts?.id ?? 'main',
45
+ kind: 'threejs',
46
+ pausable: opts?.pausable ?? true,
47
+ adapter,
48
+ mounted,
49
+ scene: mounted.scene,
50
+ ...firstPartyExtras(mounted),
51
+ });
52
+ // T7.2 slice 2 — one-time world-wiring backfill (closes the KNOWN GAP
53
+ // documented on `resolveWorldInstance` in `ecs/component-manager.ts`):
54
+ // scene-authored components were attached by the scene loader DURING the
55
+ // `mount()` call that just returned `mounted`, before this `WorldInstance`
56
+ // existed — so `instance.world` was left `undefined` for every one of
57
+ // them. `adoptWorld` backfills it now, once, for every already-attached
58
+ // instance still missing a world. Attaches made AFTER this point (e.g.
59
+ // runtime-spawned entities once the game is ticking) still resolve their
60
+ // world directly at attach time, unaffected by this call.
61
+ if (isFirstPartyMounted(mounted)) {
62
+ mounted.ctx.components.adoptWorld(world);
63
+ }
64
+ game.registerWorld(world);
65
+ return world;
66
+ }
67
+ /** Thin alias kept for existing single-world callers (`createGameRuntime`,
68
+ * `test/game-root.test.ts`, `test/frame-order.test.ts`) — registers the
69
+ * default `'main'` world via the SAME `registerThreeWorld` code path. */
70
+ export function registerDefaultThreeWorld(game, adapter, mounted) {
71
+ return registerThreeWorld(game, adapter, mounted);
72
+ }
73
+ /**
74
+ * Register a pixijs world onto the Game shell — the pixi analog of
75
+ * {@link registerThreeWorld}, mirroring the wiring
76
+ * `world2d/pixi-game-adapter.ts`'s `createWorld2DRuntime` performs for its
77
+ * OWN (separate) Game, and `test/game-three-plus-pixi.test.ts` performs by
78
+ * hand onto a SHARED Game. `createGameRuntime`'s worlds path
79
+ * (T6.1 slice 1) is the first PRODUCTION caller that registers a pixijs
80
+ * world alongside other worlds on one Game; this is its one wiring code
81
+ * path, so a second/third pixijs world (or a future caller) never
82
+ * re-derives it. `adapter` is typed as the real {@link Pixi2DGameAdapter}
83
+ * shape (T7.5 — previously the generic `GameAdapter`, which forced a cast at
84
+ * every call site since a pixi adapter's `mount` takes a `World2DHost`, not
85
+ * `HostContext`); `WorldInstance.adapter` itself only needs `.id`
86
+ * (`AdapterHandle`, `runtime/game.ts`), so this passes through with zero cast.
87
+ *
88
+ * Section 7.4-5 (design-gap catalog), UPDATED by wave 2 (D-Y1,
89
+ * docs/EXTERNAL-REACT-AND-MODULE-DESIGN.md §1/§4 slice S1): this guard used
90
+ * to THROW for a non-first-party mount, reasoning "today this can never
91
+ * actually fire (Pixi2DGameAdapter.mount is typed to always return a
92
+ * MountedGame2D, whose ctx/physics2d/collisions2d/frame are all REQUIRED
93
+ * fields, so there is no foreign-mount shape that could reach here)". Wave 2
94
+ * proved that reasoning stale: a `kind: 'pixijs'` `{ module }` adapter's mount
95
+ * result IS exactly such a foreign shape — `Pixi2DGameAdapter`'s TYPE promises
96
+ * `MountedGame2D`, but `adapter-resolver.ts`'s `resolveAllWorlds` reaches it
97
+ * only via `adapter as unknown as Pixi2DGameAdapter` (a custom module's actual
98
+ * return value is merely `MountedPixiWorld`-shaped, T7.5's cross-kind union),
99
+ * so the "never actually fire" premise no longer holds the moment a custom
100
+ * pixi module world reaches the worlds path. Rather than keep the throw (which
101
+ * would re-create exactly the kind of dead end D-X6 named and D-Y1 exists to
102
+ * remove), this now degrades EXACTLY like `registerThreeWorld`'s own foreign-
103
+ * mount handling above (`firstPartyExtras`): `physics2d`/`collisions2d`/
104
+ * `frame`/component-adoption are omitted for a non-first-party mount instead
105
+ * of read from a `.ctx` that doesn't exist — `stage` (the one field every
106
+ * `MountedPixiWorld`, first-party or not, actually carries) is always real.
107
+ */
108
+ function pixiFirstPartyExtras(mounted) {
109
+ // Same structural check `firstPartyExtras`/`isFirstPartyMounted` use — see
110
+ // this function's own doc comment (above `registerPixiWorld`) for why this
111
+ // stays a plain boolean read rather than a type-guarded `isFirstPartyMounted`
112
+ // call (that guard's threejs-shaped return type would narrow `mounted` to
113
+ // `never` here).
114
+ if (mounted.firstParty !== true)
115
+ return {};
116
+ return {
117
+ physics2d: mounted.ctx.physics2d,
118
+ collisions2d: mounted.ctx.collisions2d,
119
+ frame: mounted.frame,
120
+ };
121
+ }
122
+ export function registerPixiWorld(game, adapter, mounted, opts) {
123
+ const id = opts?.id ?? 'main';
124
+ const mountedIsFirstParty = mounted.firstParty === true;
125
+ const world = createWorldInstance({
126
+ id,
127
+ kind: 'pixijs',
128
+ pausable: opts?.pausable ?? true,
129
+ adapter,
130
+ mounted,
131
+ stage: mounted.stage,
132
+ ...pixiFirstPartyExtras(mounted),
133
+ });
134
+ // Mirrors `registerThreeWorld`'s adoptWorld backfill above — the pixi
135
+ // world's scene-authored components attach during `mount()`, before this
136
+ // `WorldInstance` exists. Skipped for a non-first-party mount (a foreign
137
+ // `{ module }` adapter's `ComponentManager` doesn't exist to adopt into —
138
+ // same "nothing for a custom adapter to backfill" reasoning
139
+ // `registerReactWorld`'s doc comment records for react).
140
+ if (mountedIsFirstParty)
141
+ mounted.ctx.components.adoptWorld(world);
142
+ game.registerWorld(world);
143
+ return world;
144
+ }
145
+ /**
146
+ * Register a react world onto the Game shell (T6.2 slice 1,
147
+ * `docs/REACT-WORLD-DESIGN.md` §1.B) — the react analog of
148
+ * {@link registerThreeWorld}/{@link registerPixiWorld}. A react world has no
149
+ * `ComponentManager` to adopt (D8: react entities host no GameComponents —
150
+ * there is nothing for `adoptWorld` to backfill, unlike the three/pixi
151
+ * siblings above) and no `frame` hooks (react's own `createRoot` schedules
152
+ * its commits; `GameInternal.runFrame` correctly leaves a world with no
153
+ * `frame` untouched by its opaque-`update` fallback too, since
154
+ * `MountedReactGame` declares no `update`). `adapter` is typed as the real
155
+ * {@link ReactWorldAdapter} shape (T7.5, same reasoning as
156
+ * `registerPixiWorld`'s doc comment above) — `WorldInstance.adapter` only
157
+ * needs `.id`, so this passes through with zero cast.
158
+ */
159
+ export function registerReactWorld(game, adapter, mounted, container, opts) {
160
+ const world = createWorldInstance({
161
+ id: opts?.id ?? 'main',
162
+ kind: 'react',
163
+ pausable: opts?.pausable ?? true,
164
+ adapter,
165
+ mounted,
166
+ container,
167
+ });
168
+ game.registerWorld(world);
169
+ return world;
170
+ }
171
+ function isWorldsConfig(config) {
172
+ return 'worlds' in config;
173
+ }
174
+ /**
175
+ * Bootstrap the game host and mount a game adapter.
176
+ *
177
+ * `createGameRuntime` is the HOST: it owns the canvas/renderer(s), the loop,
178
+ * the UI overlay, and the asset cache, and it mounts a {@link GameAdapter}
179
+ * (or several, via the worlds path — T6.1 slice 1) against that context. It
180
+ * knows nothing about `.vscn`, `GameComponent`, Rapier, or `GameSetupFn` —
181
+ * those live inside the first-party {@link VgaiSceneGameAdapter}, which is
182
+ * the default implementer when no explicit `adapter` is supplied on the
183
+ * legacy path. Dispatches on `config`'s shape: {@link WorldsRuntimeConfig}
184
+ * (`worlds` present) goes through {@link createWorldsGameRuntime};
185
+ * everything else is the byte-compatible legacy single-world path.
186
+ */
187
+ export async function createGameRuntime(config) {
188
+ if (isWorldsConfig(config)) {
189
+ return createWorldsGameRuntime(config);
190
+ }
191
+ const { canvas, width, height, adapter, ...sceneConfig } = config;
192
+ const w = Math.max(1, width ?? canvas.width);
193
+ const h = Math.max(1, height ?? canvas.height);
194
+ // --- Host primitives (neutral — no first-party assumptions) ---
195
+ const renderer = createHostRenderer(canvas, w, h);
196
+ const assets = createAssetCache();
197
+ let uiContainer;
198
+ const existingUi = document.getElementById('example-ui');
199
+ if (existingUi instanceof HTMLDivElement) {
200
+ uiContainer = existingUi;
201
+ }
202
+ else {
203
+ uiContainer = document.createElement('div');
204
+ uiContainer.id = 'example-ui';
205
+ uiContainer.style.cssText =
206
+ 'position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:2;';
207
+ (canvas.parentElement ?? document.getElementById('ui-overlay') ?? document.body).appendChild(uiContainer);
208
+ }
209
+ const extraUpdaters = new Set();
210
+ const loopHandle = {
211
+ onUpdate(fn) {
212
+ extraUpdaters.add(fn);
213
+ return () => extraUpdaters.delete(fn);
214
+ },
215
+ };
216
+ // --- Game root (T7.1 slice 1 + slice 2) ---
217
+ // The loop's update callback used to close directly over `mounted`, which
218
+ // only exists after `game.mount(host)` resolves. The Game shell (and thus
219
+ // its `loop`) must exist BEFORE mount so the mount can see `host.game` — so
220
+ // the callback instead consults a mutable ref, set once mount resolves.
221
+ // Behavior is identical: `loop.start()` still only runs after mount, same
222
+ // pause/step/stop semantics below. `drivesOwnLoop` handling now lives
223
+ // INSIDE `game.runFrame` (T7.1 slice 2) rather than being checked here —
224
+ // for today's single first-party (or single opaque host-driven) world this
225
+ // is byte-identical to the old `if (!mountedRef.drivesOwnLoop) mountedRef
226
+ // .update?.(dt)` guard (see `runFrame`'s doc comment in `runtime/game.ts`).
227
+ // `game` is referenced here before its `const` below only textually — this
228
+ // closure isn't invoked until `loop.start()`, well after `game` exists.
229
+ let mountedRef = null;
230
+ const loop = createGameLoop({
231
+ fixedTimestep: 1 / 60,
232
+ maxSubSteps: 8,
233
+ update: (dt) => {
234
+ if (mountedRef)
235
+ game.runFrame(dt);
236
+ for (const fn of extraUpdaters)
237
+ fn(dt);
238
+ },
239
+ });
240
+ const game = createGame({ loop, assets });
241
+ const host = {
242
+ three: THREE,
243
+ surface: { canvas, width: w, height: h },
244
+ renderer,
245
+ loop: loopHandle,
246
+ assets,
247
+ ui: uiContainer,
248
+ game,
249
+ requestSystem: () => null,
250
+ };
251
+ // --- Mount the game adapter (first-party default, or an explicit adapter) ---
252
+ const gameAdapter = adapter ?? new VgaiSceneGameAdapter(sceneConfig);
253
+ const mounted = await gameAdapter.mount(host);
254
+ mountedRef = mounted;
255
+ // Register the default (and, in this slice, only) threejs world now that
256
+ // mount has produced a MountedGame — the world's `threeRoot()` must return
257
+ // the SAME scene instance the adapter mounted (identity is the contract).
258
+ registerDefaultThreeWorld(game, gameAdapter, mounted);
259
+ // Expose scene & camera for dev tools / e2e tests
260
+ if (import.meta.env?.DEV) {
261
+ const w = window;
262
+ w['__vgaiScene'] = mounted.scene;
263
+ w['__vgaiCamera'] = mounted.camera;
264
+ }
265
+ loop.start();
266
+ function fullCleanup() {
267
+ loop.stop();
268
+ mounted.dispose();
269
+ renderer.dispose();
270
+ renderer.forceContextLoss();
271
+ // Retract the dev/e2e globals THIS session published (identity-guarded so
272
+ // a newer session's globals are never clobbered by an older session's
273
+ // stop). Leaving them dangling let e2e waits pass vacuously against a
274
+ // stopped game's scene/camera.
275
+ if (import.meta.env?.DEV) {
276
+ const w = window;
277
+ if (w['__vgaiScene'] === mounted.scene)
278
+ delete w['__vgaiScene'];
279
+ if (w['__vgaiCamera'] === mounted.camera)
280
+ delete w['__vgaiCamera'];
281
+ }
282
+ }
283
+ return {
284
+ stop: fullCleanup,
285
+ // D10/T7.6: `Game.play` (`runtime/game.ts`) is the real control surface —
286
+ // it fans out over every `pausable` world (just the one default world on
287
+ // this legacy single-world path) via its loop-gate/audio-gate capability,
288
+ // reporting honestly wherever a world can't actually be gated, and keeps
289
+ // `render` running every substep while paused (the old `loop.timeScale =
290
+ // 0` here starved the accumulator entirely — per D1, zero substeps means
291
+ // zero renders too — which is the bug this replaces, not a behavior to
292
+ // preserve).
293
+ pause() {
294
+ game.play.pause();
295
+ },
296
+ resume() {
297
+ game.play.resume();
298
+ },
299
+ step() {
300
+ game.play.step();
301
+ },
302
+ resize(rw, rh) {
303
+ const safeWidth = Math.max(1, rw);
304
+ const safeHeight = Math.max(1, rh);
305
+ renderer.setSize(safeWidth, safeHeight);
306
+ mounted.resize?.(safeWidth, safeHeight);
307
+ },
308
+ get scene() {
309
+ return mounted.scene;
310
+ },
311
+ get camera() {
312
+ return mounted.camera;
313
+ },
314
+ get mounted() {
315
+ return mounted;
316
+ },
317
+ game,
318
+ };
319
+ }
320
+ // ---------------------------------------------------------------------------
321
+ // Worlds path (T6.1 slice 1 — docs/MULTI-WORLD-DESIGN.md §1.A/B/C)
322
+ // ---------------------------------------------------------------------------
323
+ /** A stand-in `THREE.WebGLRenderer` for headless (`headless:true`) threejs
324
+ * worlds — mirrors `VgaiSceneGameAdapter.mount`'s own `headlessComposer`
325
+ * pattern (`adapter/vgai-scene-game-adapter.ts`): a headless first-party
326
+ * mount never calls a render-phase method on `host.renderer` at all, so
327
+ * this only needs to satisfy the handful of calls THIS file itself makes
328
+ * (`setPixelRatio`/`setClearColor`/`setSize` at mount, `dispose`/
329
+ * `forceContextLoss` at teardown) — never a real GL call. Node conformance
330
+ * tests only; never used when `headless` is left `false`/absent. */
331
+ function createHeadlessRendererStub() {
332
+ return {
333
+ setPixelRatio() { },
334
+ setClearColor() { },
335
+ setSize() { },
336
+ dispose() { },
337
+ forceContextLoss() { },
338
+ };
339
+ }
340
+ /**
341
+ * Derive a REAL default `hitTest` for a pixijs world from its own mounted
342
+ * stage (D5 §2a/COMPOSITION-DESIGN.md proof item 2: "pixi `EventBoundary
343
+ * .hitTest`") — used only when the caller's `WorldMountSpec` didn't declare
344
+ * an explicit `hitTest`. `(x, y)` are container-relative pixels, matching
345
+ * the coordinates the pixi surface renders at (the stage itself carries no
346
+ * pan/zoom — that lives on `pixi-surface.ts`'s child `world` container, per
347
+ * its own doc comment), so this is a correct global hit test with no extra
348
+ * coordinate mapping. Duck-typed against `mounted.ctx.app` (never a
349
+ * value-import of `pixi.js`) so this file stays type-only w.r.t. Pixi;
350
+ * returns `undefined` (no default claim) if the mount's `app`/event
351
+ * boundary isn't shaped as expected — degrade to "does not claim" rather
352
+ * than throw. A non-first-party mount (D-Y1, wave 2 — same finding
353
+ * `registerPixiWorld`'s doc comment records: a `{ module }` adapter's mount
354
+ * has no `.ctx` at all) has nothing to duck-type here either, so this checks
355
+ * the SAME `firstParty` brand FIRST and degrades identically — never claims,
356
+ * never throws.
357
+ */
358
+ function derivePixiHitTest(mounted) {
359
+ if (mounted.firstParty !== true)
360
+ return undefined;
361
+ const app = mounted.ctx.app;
362
+ const rootBoundary = app?.renderer?.events?.rootBoundary;
363
+ if (!rootBoundary || typeof rootBoundary.hitTest !== 'function')
364
+ return undefined;
365
+ return (x, y) => rootBoundary.hitTest(x, y) != null;
366
+ }
367
+ /** Mount one threejs `WorldMountSpec` (canvas + renderer construction, per
368
+ * D5 §1/§4, then `registerThreeWorld`). Split out of
369
+ * `createWorldsGameRuntime` purely to keep that function's own branching
370
+ * simple — see `mountOnePixiWorld` for the pixijs sibling. */
371
+ async function mountOneThreeWorld(spec, ctx) {
372
+ const { game, canvas, w, h, dpr, isBottom, headless, uiContainer, loopHandle, assets } = ctx;
373
+ const renderer = headless
374
+ ? createHeadlessRendererStub()
375
+ : createHostRenderer(canvas, w, h, undefined, {
376
+ alpha: !isBottom,
377
+ preserveDrawingBuffer: true,
378
+ });
379
+ renderer.setSize(w, h, false); // backing resolution only — CSS stacking owns layout size
380
+ // Defect E4.R1 (Fable review): `createHostRenderer`'s OWN construction-time
381
+ // `setSize` call (`setup-renderer.ts`, `updateStyle` defaulting `true` —
382
+ // unchanged there on purpose, see below) stamps `canvas.style.width`/
383
+ // `.height` to literal `${w}px`/`${h}px` the moment the renderer is built,
384
+ // OVERWRITING the container-relative `100%`/`100%` the surface-stacking
385
+ // loop above just set. The `renderer.setSize(w, h, false)` line right above
386
+ // this comment does NOT undo that stamp (updateStyle:false only skips
387
+ // TOUCHING style, it can't un-stamp a previous call) — so without this
388
+ // re-assertion, every worlds-path threejs canvas' on-screen size was
389
+ // permanently pinned to whatever `w`/`h` it happened to mount at (usually
390
+ // the manifest's `resolution`, since standalone builds mount before a real
391
+ // container size is known — see `mount-manifest.ts`). Re-asserting here
392
+ // makes the CSS layout size and the render-buffer resolution fully
393
+ // independent, exactly like the react DOM world layer already is: this
394
+ // fixes the template-standalone bug (broken at any viewport other than the
395
+ // manifest's `resolution`, which is also Playwright's DEFAULT viewport —
396
+ // why it went unnoticed) and, for free, tri-world's pre-existing
397
+ // resize-staleness (the worlds-path `resize()` below always calls
398
+ // `setSize(rw, rh, false)`, so nothing else was ever going to update this
399
+ // canvas' CSS after mount). `createHostRenderer`'s own default
400
+ // (`updateStyle:true`) is intentionally left alone — the LEGACY
401
+ // single-canvas path (editor play-mode's non-multi-world branch) still
402
+ // relies on that construction-time stamp + its own later `updateStyle:true`
403
+ // resizes for byte-identical behavior; this fix touches only the
404
+ // worlds-path canvas, after the fact.
405
+ canvas.style.width = '100%';
406
+ canvas.style.height = '100%';
407
+ renderer.setPixelRatio(dpr);
408
+ if (!isBottom)
409
+ renderer.setClearColor(0x000000, 0); // D5 §1: alpha-clear above the bottom layer
410
+ const host = {
411
+ three: THREE,
412
+ surface: { canvas, width: w, height: h },
413
+ renderer,
414
+ loop: loopHandle,
415
+ assets,
416
+ ui: uiContainer,
417
+ headless,
418
+ game,
419
+ requestSystem: () => null,
420
+ };
421
+ const mounted = await spec.adapter.mount(host);
422
+ registerThreeWorld(game, spec.adapter, mounted, { id: spec.id, pausable: spec.pausable });
423
+ return {
424
+ mountedEntry: { id: spec.id, kind: 'threejs', element: canvas, mounted, renderer },
425
+ routerEntry: { id: spec.id, zOrder: spec.zOrder ?? 0, canvas, hitTest: spec.hitTest },
426
+ };
427
+ }
428
+ /** Mount one pixijs `WorldMountSpec` (via its own `World2DHost`, per D5
429
+ * §1/§3/§4, then `registerPixiWorld`) — the pixi sibling of
430
+ * `mountOneThreeWorld` above. Derives a real default `hitTest` from the
431
+ * mounted stage when the spec didn't declare one (see `derivePixiHitTest`).
432
+ *
433
+ * Unlike `mountOneThreeWorld`, this needs no explicit `canvas.style.width`/
434
+ * `.height` re-assertion (E4.R1). `pixi-surface.ts` constructs its
435
+ * `Application` with `autoDensity: true`, which makes PIXI ITSELF re-stamp
436
+ * `canvas.style.width`/`.height` (real CSS px, matching the LOGICAL
437
+ * width/height passed to `resize()`) on every `app.renderer.resize()` call —
438
+ * including the one this world's `mounted.resize?.()` triggers from the
439
+ * worlds-path `resize()` below. So while a pixi world's canvas can start
440
+ * pinned to the mount-time `w`/`h` (same as threejs, until PIXI's own
441
+ * construction-time resize runs), it self-heals the moment ANY real
442
+ * `session.resize(rw, rh)` fires — every shipped standalone entry
443
+ * (`packages/editor/template/src/main.ts`, `examples/tri-world/src/main.ts`)
444
+ * already calls `session.resize()` unconditionally right after mount, so
445
+ * this never surfaces as a lasting bug the way threejs' buffer-only resize
446
+ * did (never self-healing, by design — see above). */
447
+ async function mountOnePixiWorld(spec, ctx) {
448
+ const { game, canvas, w, h, dpr, isBottom, uiContainer } = ctx;
449
+ const pixiHost = {
450
+ canvas,
451
+ width: w,
452
+ height: h,
453
+ ui: uiContainer,
454
+ dpr,
455
+ transparent: !isBottom,
456
+ preserveDrawingBuffer: true,
457
+ };
458
+ const mounted = await spec.adapter.mount(pixiHost);
459
+ registerPixiWorld(game, spec.adapter, mounted, {
460
+ id: spec.id,
461
+ pausable: spec.pausable,
462
+ });
463
+ return {
464
+ mountedEntry: {
465
+ id: spec.id,
466
+ kind: 'pixijs',
467
+ element: canvas,
468
+ mounted,
469
+ renderer: undefined,
470
+ },
471
+ routerEntry: {
472
+ id: spec.id,
473
+ zOrder: spec.zOrder ?? 0,
474
+ canvas,
475
+ hitTest: spec.hitTest ?? derivePixiHitTest(mounted),
476
+ },
477
+ };
478
+ }
479
+ /**
480
+ * Mount one react `WorldMountSpec` (T6.2 slice 1, `docs/REACT-WORLD-DESIGN.md`
481
+ * §1.B/§1.C) — the react sibling of `mountOneThreeWorld`/`mountOnePixiWorld`.
482
+ * Unlike its canvas-backed siblings this returns NO `routerEntry`: a react
483
+ * world's DOM-root layer participates in D5's z-order/box stacking (the
484
+ * caller still creates and positions its `<div>` exactly like a canvas — see
485
+ * `createWorldsGameRuntime`'s stack-building loop) but needs no entry in the
486
+ * delegating router's hit-test loop (§1.C — "DOM layers need no entry in the
487
+ * router's hit-test loop"): the layer's own `pointer-events` discipline
488
+ * (this file sets `none` on the layer by default, mirroring the existing
489
+ * `uiContainer`/HUD rule; the mounted react tree opts specific elements back
490
+ * in with `pointer-events:auto`) is what lets its interactive elements claim
491
+ * events NATIVELY, via the real DOM, with zero router involvement — and lets
492
+ * a click over its non-interactive (transparent) area fall through to the
493
+ * canvas below it via the SAME native DOM hit-testing (a `pointer-events:none`
494
+ * element is invisible to hit-testing entirely, so the click lands on
495
+ * whatever real DOM element is beneath it — the router's normal
496
+ * canvas-vs-canvas forwarding, unaffected by this layer's presence).
497
+ */
498
+ async function mountOneReactWorld(spec, game,
499
+ /** The ALREADY-created, already-stacked (position/z-index set, appended to
500
+ * `container`) DOM-root layer for this world — see
501
+ * `createWorldsGameRuntime`'s surface-stack loop, which builds a `<div>`
502
+ * for every react-kind spec up front, in the SAME pass that builds every
503
+ * other world's canvas. This function must reuse that exact element (never
504
+ * create its own) so `WorldInstance.reactRoot()` returns the SAME node
505
+ * that is actually positioned in the stack. */
506
+ layer) {
507
+ layer.style.pointerEvents = 'none';
508
+ // Size the layer like the shared uiContainer does: an absolutely-positioned
509
+ // layer with no width/height collapses to zero content size, so a child's
510
+ // own position:absolute offsets resolve against a degenerate containing
511
+ // block (found by the T6.2 slice-3 e2e — clicks landed outside the game).
512
+ layer.style.width = '100%';
513
+ layer.style.height = '100%';
514
+ const reactHost = { container: layer, game };
515
+ const mounted = await spec.adapter.mount(reactHost);
516
+ registerReactWorld(game, spec.adapter, mounted, layer, {
517
+ id: spec.id,
518
+ pausable: spec.pausable,
519
+ });
520
+ return {
521
+ mountedEntry: {
522
+ id: spec.id,
523
+ kind: 'react',
524
+ element: layer,
525
+ mounted,
526
+ renderer: undefined,
527
+ },
528
+ };
529
+ }
530
+ /**
531
+ * The worlds-path implementer behind {@link createGameRuntime} (T6.1 slice
532
+ * 1; react worlds added T6.2 slice 1). Builds ONE surface per world — a
533
+ * canvas for threejs/pixijs, a DOM-root `<div>` layer for react — stacked
534
+ * per COMPOSITION-DESIGN D5 §1, z-order/ties exactly matching
535
+ * `manifest/load.ts`'s sort, ONE `Game`, and registers every world onto it
536
+ * via `registerThreeWorld`/`registerPixiWorld`/`registerReactWorld` — the
537
+ * SAME wiring `test/game-three-plus-pixi.test.ts` proves by hand for the
538
+ * three/pixi pair. Worlds MOUNT in `worlds` ARRAY order (GAME-ROOT-DESIGN §4's
539
+ * "manifest declaration order" — the frame/registration axis), independent of
540
+ * `zOrder` (the canvas-stacking/rendering axis) — the two orders can differ
541
+ * and both are honored correctly.
542
+ */
543
+ /**
544
+ * Dev/e2e-only `window.__vgaiScene`/`__vgaiCamera` exposure for the worlds
545
+ * path's DEFAULT world (E4) — split out of `createWorldsGameRuntime` purely
546
+ * to keep that function's own cyclomatic complexity down. Mirrors the
547
+ * legacy single-world path's identical exposure (above, in this same file),
548
+ * using the SAME "first threejs world, else none" default-world rule the
549
+ * `GameSession.scene`/`.camera` getters alias (`Game.defaultWorld`): a
550
+ * non-threejs default world (or none at all) publishes neither global,
551
+ * exactly like those getters return `undefined` in that case. Returns a
552
+ * retraction callback — identity-guarded (a newer session's globals must
553
+ * never be clobbered by an older session's stop, same reason the legacy
554
+ * path's own cleanup guards it) — that is a no-op when nothing was
555
+ * published (non-DEV build, or non-threejs default world).
556
+ */
557
+ function installDefaultWorldDevGlobals(game) {
558
+ if (!import.meta.env?.DEV)
559
+ return () => { };
560
+ const defaultMounted = game.defaultWorld.mounted;
561
+ if (defaultMounted.kind !== 'threejs')
562
+ return () => { };
563
+ const scene = defaultMounted.scene;
564
+ const camera = defaultMounted.camera;
565
+ const w = window;
566
+ w['__vgaiScene'] = scene;
567
+ w['__vgaiCamera'] = camera;
568
+ return () => {
569
+ if (w['__vgaiScene'] === scene)
570
+ delete w['__vgaiScene'];
571
+ if (w['__vgaiCamera'] === camera)
572
+ delete w['__vgaiCamera'];
573
+ };
574
+ }
575
+ /**
576
+ * Mount + register every world spec, in ARRAY (declaration) order — split
577
+ * out of `createWorldsGameRuntime` purely to keep that function's own
578
+ * cyclomatic complexity down (E4, same reason `mountOneThreeWorld`/
579
+ * `mountOnePixiWorld` are already split out). React worlds contribute NO
580
+ * router entry (docs/REACT-WORLD-DESIGN.md §1.C — "DOM layers need no entry
581
+ * in the router's hit-test loop"): their layer's own `pointer-events`
582
+ * discipline handles claim/fall-through natively, with zero router
583
+ * involvement (see `mountOneReactWorld`'s doc comment).
584
+ */
585
+ async function mountAllWorldSpecs(mountSpecs, bottomId, inputs) {
586
+ const { surfacesById, ...shared } = inputs;
587
+ const mountedEntries = [];
588
+ const routerEntries = [];
589
+ for (const spec of mountSpecs) {
590
+ const isBottom = spec.id === bottomId;
591
+ if (spec.kind === 'react') {
592
+ const layer = surfacesById.get(spec.id);
593
+ const { mountedEntry } = await mountOneReactWorld(spec, shared.game, layer);
594
+ mountedEntries.push(mountedEntry);
595
+ continue;
596
+ }
597
+ const canvas = surfacesById.get(spec.id);
598
+ const oneCtx = { ...shared, canvas, isBottom };
599
+ let mountedEntry;
600
+ let routerEntry;
601
+ if (spec.kind === 'threejs') {
602
+ ({ mountedEntry, routerEntry } = await mountOneThreeWorld(spec, oneCtx));
603
+ }
604
+ else if (spec.kind === 'pixijs') {
605
+ ({ mountedEntry, routerEntry } = await mountOnePixiWorld(spec, oneCtx));
606
+ }
607
+ else {
608
+ // Exhaustiveness guard (§7.4-2): 'react' was already handled by the
609
+ // early `continue` above, so only a hypothetical 4th `WorldKind` can
610
+ // reach here — fail loudly rather than silently defaulting. `spec`
611
+ // itself (not `spec.kind`) is what TS has narrowed to `never`, since
612
+ // `WorldMountSpec` is a discriminated union at the object level.
613
+ assertNever(spec, 'create-runtime mount loop');
614
+ }
615
+ mountedEntries.push(mountedEntry);
616
+ routerEntries.push(routerEntry);
617
+ }
618
+ return { mountedEntries, routerEntries };
619
+ }
620
+ async function createWorldsGameRuntime(config) {
621
+ const { container, worlds: specs, width, height, headless = false } = config;
622
+ if (specs.length === 0) {
623
+ throw new Error('createGameRuntime: `worlds` must contain at least one world.');
624
+ }
625
+ const mountSpecs = specs;
626
+ const w = Math.max(1, width ?? container.clientWidth ?? 0);
627
+ const h = Math.max(1, height ?? container.clientHeight ?? 0);
628
+ const dpr = headless
629
+ ? 1
630
+ : Math.min(typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1, 2);
631
+ if (!container.style.position)
632
+ container.style.position = 'relative';
633
+ // --- Surface stack (D5 §1): DOM/z-order follows zOrder, ties -> array
634
+ // order — computed FIRST (bottom -> top) so both the z-index assignment
635
+ // below and the router's default-claim rule share one definition. A react
636
+ // world's DOM-root layer shares this SAME stacking pass (T6.2 slice 1,
637
+ // docs/REACT-WORLD-DESIGN.md §1.B — "one stacking model, no special case")
638
+ // even though it is a `<div>`, not a canvas, and carries no `hitTest` (the
639
+ // router never sees react entries at all — see the dispatch loop below). ---
640
+ const stacked = stackOrder(mountSpecs.map((spec) => ({ id: spec.id, zOrder: spec.zOrder ?? 0, hitTest: spec.hitTest })));
641
+ const bottomId = stacked[0]?.id;
642
+ const kindById = new Map(mountSpecs.map((spec) => [spec.id, spec.kind]));
643
+ const surfacesById = new Map();
644
+ stacked.forEach((entry, i) => {
645
+ const isReact = kindById.get(entry.id) === 'react';
646
+ const surface = document.createElement(isReact ? 'div' : 'canvas');
647
+ if (!isReact) {
648
+ const canvas = surface;
649
+ canvas.width = w;
650
+ canvas.height = h;
651
+ }
652
+ // Defect E4.R1 (Fable review): layout size is ALWAYS container-relative
653
+ // (`width:100%;height:100%`), fully decoupled from the surface's
654
+ // intrinsic buffer resolution (`w`/`h`, set on the canvas element's
655
+ // `width`/`height` ATTRIBUTES above — a device-pixel/render-resolution
656
+ // concern only). Without this, a canvas with no CSS size falls back to
657
+ // its `width`/`height` attribute as its CSS layout size too, so whatever
658
+ // stamped those attributes (`createHostRenderer`'s construction-time
659
+ // `setSize`, `mountOneThreeWorld` below) pins the ON-SCREEN size — see
660
+ // that function's doc comment for the concrete bug this caused (template
661
+ // standalone at any viewport ≠ the manifest resolution). Setting it HERE
662
+ // too (not just in `mountOneThreeWorld`) means every surface, whatever
663
+ // kind, starts container-relative from its very first paint, before any
664
+ // per-kind mount work has even run.
665
+ surface.style.cssText = `position:absolute;top:0;left:0;width:100%;height:100%;z-index:${i + 1};`;
666
+ container.appendChild(surface);
667
+ surfacesById.set(entry.id, surface);
668
+ });
669
+ // One shared UI overlay, above every canvas. I8 render-integration seam
670
+ // (docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md §15 I8, C1 review note):
671
+ // the legacy single-world path (above) reuses/stamps a stable `#example-ui`
672
+ // id so a capture host has a reliable selector for "the HUD/letterbox
673
+ // overlay" — this worlds path had no such id at all, so a render-mode page
674
+ // built on `createWorldsGameRuntime` (multi-world manifests, including a
675
+ // react-HUD-over-threejs cinematic) had no stable selector to composite-
676
+ // screenshot. `vgai-ui-overlay` is deliberately a DIFFERENT id than the
677
+ // legacy path's `example-ui` (not reused/aliased) — the two runtime paths
678
+ // mount genuinely different DOM subtrees, and collapsing them onto one id
679
+ // would make a caller's selector ambiguous about which mount produced it.
680
+ const uiContainer = document.createElement('div');
681
+ uiContainer.id = 'vgai-ui-overlay';
682
+ uiContainer.style.cssText =
683
+ `position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;` +
684
+ `z-index:${stacked.length + 1};`;
685
+ container.appendChild(uiContainer);
686
+ const assets = createAssetCache();
687
+ const extraUpdaters = new Set();
688
+ const loopHandle = {
689
+ onUpdate(fn) {
690
+ extraUpdaters.add(fn);
691
+ return () => extraUpdaters.delete(fn);
692
+ },
693
+ };
694
+ let started = false;
695
+ const loop = createGameLoop({
696
+ fixedTimestep: 1 / 60,
697
+ maxSubSteps: 8,
698
+ update: (dt) => {
699
+ if (started)
700
+ game.runFrame(dt);
701
+ for (const fn of extraUpdaters)
702
+ fn(dt);
703
+ },
704
+ });
705
+ const game = createGame({ loop, assets });
706
+ // --- Mount + register every world, in ARRAY (declaration) order. ---
707
+ // Split out into its own top-level function purely to keep
708
+ // `createWorldsGameRuntime`'s own cyclomatic complexity down (E4) — same
709
+ // reason `mountOneThreeWorld`/`mountOnePixiWorld` are already split out
710
+ // below.
711
+ const { mountedEntries, routerEntries } = await mountAllWorldSpecs(mountSpecs, bottomId, {
712
+ game,
713
+ surfacesById,
714
+ w,
715
+ h,
716
+ dpr,
717
+ headless,
718
+ uiContainer,
719
+ loopHandle,
720
+ assets,
721
+ });
722
+ // --- Delegating input router (D5 §2a) ---
723
+ const router = createInputRouter(container, routerEntries);
724
+ started = true;
725
+ loop.start();
726
+ // Expose scene & camera for dev tools / e2e tests — mirrors the legacy
727
+ // single-world path's identical dev-only exposure above, generalized to
728
+ // the worlds path's DEFAULT world (E4, docs/unified-world-editor/
729
+ // 27-visual-react-editing.md §7 E4): `mountGameFromManifest`/
730
+ // `mountManifestWorlds` route every caller (including a single-threejs-
731
+ // world scaffold project) through THIS path, so a caller migrating off
732
+ // the legacy `{canvas, adapter}` call must not silently lose
733
+ // `window.__vgaiScene`/`__vgaiCamera` — real e2e/dev tooling depends on
734
+ // them (`packages/editor/e2e/tests/04-standalone-game.spec.ts`). Split
735
+ // into its own top-level helper (with its retraction counterpart below)
736
+ // purely to keep this function's own cyclomatic complexity down.
737
+ const retractDevGlobals = installDefaultWorldDevGlobals(game);
738
+ function fullCleanup() {
739
+ loop.stop();
740
+ router.dispose();
741
+ for (const entry of mountedEntries) {
742
+ entry.mounted.dispose();
743
+ entry.renderer?.dispose();
744
+ entry.renderer?.forceContextLoss();
745
+ container.removeChild(entry.element);
746
+ }
747
+ container.removeChild(uiContainer);
748
+ retractDevGlobals();
749
+ }
750
+ return {
751
+ stop: fullCleanup,
752
+ // D10/T7.6: `Game.play` fans out per-world pausable/loop-gate/audio-gate
753
+ // semantics itself now (see the legacy path's identical comment above) —
754
+ // a `pausable: false` world (a menu/HUD world) keeps ticking while every
755
+ // other world freezes, which the old blind `for (const entry of
756
+ // mountedEntries) entry.mounted.setPaused?.(true)` fan-out (with no
757
+ // `pausable` check at all) could never express.
758
+ pause() {
759
+ game.play.pause();
760
+ },
761
+ resume() {
762
+ game.play.resume();
763
+ },
764
+ step() {
765
+ game.play.step();
766
+ },
767
+ resize(rw, rh) {
768
+ const safeWidth = Math.max(1, rw);
769
+ const safeHeight = Math.max(1, rh);
770
+ for (const entry of mountedEntries) {
771
+ entry.renderer?.setSize(safeWidth, safeHeight, false);
772
+ entry.mounted.resize?.(safeWidth, safeHeight);
773
+ }
774
+ },
775
+ // `GameSession.scene`/`.camera`/`.mounted` alias `Game.defaultWorld` (T6.1
776
+ // slice 1's "first threejs world, else first world" rule) — a legacy
777
+ // convenience shaped for the threejs-only past. T7.5 narrows the read via
778
+ // the `kind` discriminant instead of a blind `.scene`/`.camera` cast
779
+ // through a nonexistent property (identical behavior to before: still
780
+ // `undefined` for a worlds-path session whose default world isn't
781
+ // threejs — real per-world surface routing for that case is T7.6's).
782
+ get scene() {
783
+ const m = game.defaultWorld.mounted;
784
+ return (m.kind === 'threejs' ? m.scene : undefined);
785
+ },
786
+ get camera() {
787
+ const m = game.defaultWorld.mounted;
788
+ return (m.kind === 'threejs' ? m.camera : undefined);
789
+ },
790
+ get mounted() {
791
+ return game.defaultWorld.mounted;
792
+ },
793
+ game,
794
+ };
795
+ }