@vgai/engine 0.5.21 → 0.5.23

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 (339) hide show
  1. package/README.md +6 -3
  2. package/dist/adapter/adapter-module.d.ts.map +1 -1
  3. package/dist/adapter/adapter-module.js +2 -4
  4. package/dist/adapter/adapter-surface.d.ts +1 -1
  5. package/dist/adapter/adapter-surface.js +1 -1
  6. package/dist/adapter/authoring.d.ts +19 -21
  7. package/dist/adapter/authoring.d.ts.map +1 -1
  8. package/dist/adapter/authoring.js +2 -2
  9. package/dist/adapter/binding.d.ts +341 -0
  10. package/dist/adapter/binding.d.ts.map +1 -0
  11. package/dist/adapter/binding.js +148 -0
  12. package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -1
  13. package/dist/adapter/colyseus-networking-adapter.js +23 -4
  14. package/dist/adapter/entrypoint-selection-readers.d.ts +24 -0
  15. package/dist/adapter/entrypoint-selection-readers.d.ts.map +1 -0
  16. package/dist/adapter/entrypoint-selection-readers.js +63 -0
  17. package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -1
  18. package/dist/adapter/entrypoint-selection-source.js +1 -44
  19. package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
  20. package/dist/adapter/finders/scenes-from-entrypoint-selection.js +1 -52
  21. package/dist/adapter/host-context.d.ts +46 -15
  22. package/dist/adapter/host-context.d.ts.map +1 -1
  23. package/dist/adapter/host-context.js +2 -9
  24. package/dist/adapter/index.d.ts +11 -12
  25. package/dist/adapter/index.d.ts.map +1 -1
  26. package/dist/adapter/index.js +9 -10
  27. package/dist/adapter/ingest/game-contract.d.ts +0 -3
  28. package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
  29. package/dist/adapter/ingest/game-contract.js +0 -3
  30. package/dist/adapter/ingest/scene-capture.d.ts +19 -23
  31. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
  32. package/dist/adapter/ingest/scene-capture.js +143 -17
  33. package/dist/adapter/ingest/visible-capture-window.d.ts +34 -0
  34. package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
  35. package/dist/adapter/manifest-interpreter.d.ts +59 -0
  36. package/dist/adapter/manifest-interpreter.d.ts.map +1 -0
  37. package/dist/adapter/manifest-interpreter.js +50 -0
  38. package/dist/adapter/native-debug-module.d.ts +71 -3
  39. package/dist/adapter/native-debug-module.d.ts.map +1 -1
  40. package/dist/adapter/native-debug-module.js +146 -10
  41. package/dist/adapter/rapier-physics-adapter.d.ts +31 -1
  42. package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -1
  43. package/dist/adapter/rapier-physics-adapter.js +50 -36
  44. package/dist/adapter/renderer-config.d.ts +3 -3
  45. package/dist/adapter/renderer-config.js +3 -3
  46. package/dist/adapter/root-adapter.d.ts +7 -7
  47. package/dist/adapter/root-adapter.js +4 -4
  48. package/dist/adapter/system-adapter.d.ts +45 -7
  49. package/dist/adapter/system-adapter.d.ts.map +1 -1
  50. package/dist/adapter/system-seam-contract.d.ts +3 -3
  51. package/dist/adapter/system-seam-contract.js +3 -3
  52. package/dist/adapter/system-slot.d.ts +168 -0
  53. package/dist/adapter/system-slot.d.ts.map +1 -0
  54. package/dist/adapter/system-slot.js +229 -0
  55. package/dist/adapter/transform.d.ts +11 -1
  56. package/dist/adapter/transform.d.ts.map +1 -1
  57. package/dist/ai/navigation.d.ts +17 -1
  58. package/dist/ai/navigation.d.ts.map +1 -1
  59. package/dist/ai/navigation.js +26 -5
  60. package/dist/animation/anim-graph-types.d.ts +11 -3
  61. package/dist/animation/anim-graph-types.d.ts.map +1 -1
  62. package/dist/animation/theatre-clock-binding.d.ts +4 -4
  63. package/dist/animation/theatre-clock-binding.js +4 -4
  64. package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
  65. package/dist/animation/xstate-animation-binding.js +2 -2
  66. package/dist/animation/xstate-animation-meta.d.ts +13 -0
  67. package/dist/animation/xstate-animation-meta.d.ts.map +1 -1
  68. package/dist/animation/xstate-animation-meta.js +14 -0
  69. package/dist/asset-formats/index.d.ts +4 -7
  70. package/dist/asset-formats/index.d.ts.map +1 -1
  71. package/dist/asset-formats/index.js +4 -7
  72. package/dist/asset-formats/render-env.d.ts +5 -8
  73. package/dist/asset-formats/render-env.d.ts.map +1 -1
  74. package/dist/asset-formats/render-env.js +3 -6
  75. package/dist/asset-parse-error.d.ts +2 -5
  76. package/dist/asset-parse-error.d.ts.map +1 -1
  77. package/dist/asset-parse-error.js +2 -5
  78. package/dist/audio/bus-mixer.d.ts +101 -0
  79. package/dist/audio/bus-mixer.d.ts.map +1 -0
  80. package/dist/audio/bus-mixer.js +115 -0
  81. package/dist/canvas-react/index.d.ts +5 -12
  82. package/dist/canvas-react/index.d.ts.map +1 -1
  83. package/dist/canvas-react/index.js +5 -12
  84. package/dist/canvas-react/pixi-react-root-factory.d.ts +34 -21
  85. package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
  86. package/dist/canvas-react/pixi-react-root-factory.js +274 -36
  87. package/dist/core/countdown-timer.d.ts +118 -0
  88. package/dist/core/countdown-timer.d.ts.map +1 -0
  89. package/dist/core/countdown-timer.js +137 -0
  90. package/dist/core/deferred-commands.d.ts +127 -0
  91. package/dist/core/deferred-commands.d.ts.map +1 -0
  92. package/dist/core/deferred-commands.js +132 -0
  93. package/dist/core/frame-pacing.d.ts +1 -1
  94. package/dist/core/frame-pacing.js +2 -2
  95. package/dist/core/game-loop.d.ts +1 -3
  96. package/dist/core/game-loop.d.ts.map +1 -1
  97. package/dist/core/game-loop.js +1 -3
  98. package/dist/core/seeded-random.d.ts +1 -1
  99. package/dist/core/sim-clock.d.ts +19 -8
  100. package/dist/core/sim-clock.d.ts.map +1 -1
  101. package/dist/core/sim-clock.js +18 -7
  102. package/dist/core/types.d.ts +1 -3
  103. package/dist/core/types.d.ts.map +1 -1
  104. package/dist/defaults.d.ts +6 -14
  105. package/dist/defaults.d.ts.map +1 -1
  106. package/dist/defaults.js +6 -14
  107. package/dist/dev/instruments.d.ts +38 -11
  108. package/dist/dev/instruments.d.ts.map +1 -1
  109. package/dist/dev/instruments.js +81 -11
  110. package/dist/dev/register-render-vitals.d.ts +1 -4
  111. package/dist/dev/register-render-vitals.d.ts.map +1 -1
  112. package/dist/dev/register-render-vitals.js +1 -4
  113. package/dist/dev/render-debug-adapter.d.ts +2 -3
  114. package/dist/dev/render-debug-adapter.d.ts.map +1 -1
  115. package/dist/dev/render-debug-adapter.js +2 -3
  116. package/dist/dev/render-vitals.d.ts +3 -3
  117. package/dist/dev/render-vitals.js +3 -3
  118. package/dist/dev/webgl-frame-capture.d.ts +1 -1
  119. package/dist/dev/webgl-frame-capture.js +1 -1
  120. package/dist/index.d.ts +2 -5
  121. package/dist/index.d.ts.map +1 -1
  122. package/dist/index.js +2 -4
  123. package/dist/input/host-pointer.d.ts +122 -0
  124. package/dist/input/host-pointer.d.ts.map +1 -0
  125. package/dist/input/host-pointer.js +160 -0
  126. package/dist/input/input-manager.d.ts +33 -5
  127. package/dist/input/input-manager.d.ts.map +1 -1
  128. package/dist/input/input-manager.js +48 -13
  129. package/dist/input/rebind-controller.d.ts.map +1 -1
  130. package/dist/input/rebind-controller.js +6 -8
  131. package/dist/input/schema.d.ts.map +1 -1
  132. package/dist/input/schema.js +56 -28
  133. package/dist/manifest/filename.d.ts.map +1 -1
  134. package/dist/manifest/filename.js +8 -12
  135. package/dist/manifest/load.d.ts +10 -10
  136. package/dist/manifest/load.d.ts.map +1 -1
  137. package/dist/manifest/load.js +9 -6
  138. package/dist/manifest/schema.d.ts +17 -21
  139. package/dist/manifest/schema.d.ts.map +1 -1
  140. package/dist/manifest/schema.js +45 -35
  141. package/dist/physics/collider-dimensions.d.ts +8 -12
  142. package/dist/physics/collider-dimensions.d.ts.map +1 -1
  143. package/dist/physics/collider-dimensions.js +8 -12
  144. package/dist/pixi/scene-capture.d.ts +11 -13
  145. package/dist/pixi/scene-capture.d.ts.map +1 -1
  146. package/dist/react/world-state.d.ts +1 -1
  147. package/dist/react/world-state.d.ts.map +1 -1
  148. package/dist/render/directional-shadow-fit.d.ts +88 -0
  149. package/dist/render/directional-shadow-fit.d.ts.map +1 -0
  150. package/dist/render/directional-shadow-fit.js +133 -0
  151. package/dist/render/environment-capture.d.ts +68 -0
  152. package/dist/render/environment-capture.d.ts.map +1 -0
  153. package/dist/render/environment-capture.js +90 -0
  154. package/dist/render/ibl-override-material.d.ts +97 -0
  155. package/dist/render/ibl-override-material.d.ts.map +1 -0
  156. package/dist/render/ibl-override-material.js +114 -0
  157. package/dist/render/light-camera-factory.d.ts +5 -7
  158. package/dist/render/light-camera-factory.d.ts.map +1 -1
  159. package/dist/render/light-camera-factory.js +8 -11
  160. package/dist/render/material-factory.js +1 -1
  161. package/dist/render/render-batch-system.d.ts.map +1 -1
  162. package/dist/render/render-features.d.ts +17 -6
  163. package/dist/render/render-features.d.ts.map +1 -1
  164. package/dist/render/render-features.js +0 -28
  165. package/dist/runtime/create-runtime.d.ts +34 -25
  166. package/dist/runtime/create-runtime.d.ts.map +1 -1
  167. package/dist/runtime/create-runtime.js +218 -93
  168. package/dist/runtime/debug-registry.d.ts +77 -8
  169. package/dist/runtime/debug-registry.d.ts.map +1 -1
  170. package/dist/runtime/debug-registry.js +3 -3
  171. package/dist/runtime/dev-build.d.ts +2 -2
  172. package/dist/runtime/dev-build.js +2 -2
  173. package/dist/runtime/game-input-seams.d.ts +51 -0
  174. package/dist/runtime/game-input-seams.d.ts.map +1 -0
  175. package/dist/runtime/game-input-seams.js +86 -0
  176. package/dist/runtime/game.d.ts +104 -40
  177. package/dist/runtime/game.d.ts.map +1 -1
  178. package/dist/runtime/game.js +119 -61
  179. package/dist/runtime/mount-game.d.ts +0 -16
  180. package/dist/runtime/mount-game.d.ts.map +1 -1
  181. package/dist/runtime/mount-game.js +26 -41
  182. package/dist/runtime/mount-manifest.d.ts +22 -14
  183. package/dist/runtime/mount-manifest.d.ts.map +1 -1
  184. package/dist/runtime/mount-manifest.js +22 -20
  185. package/dist/runtime/playtest.d.ts +22 -0
  186. package/dist/runtime/playtest.d.ts.map +1 -0
  187. package/dist/runtime/playtest.js +12 -0
  188. package/dist/runtime/state-bridge.d.ts +2 -3
  189. package/dist/runtime/state-bridge.d.ts.map +1 -1
  190. package/dist/runtime/state-bridge.js +1 -0
  191. package/dist/setup/setup-audio.d.ts +12 -0
  192. package/dist/setup/setup-audio.d.ts.map +1 -1
  193. package/dist/setup/setup-audio.js +9 -10
  194. package/dist/setup/setup-renderer.d.ts +2 -25
  195. package/dist/setup/setup-renderer.d.ts.map +1 -1
  196. package/dist/setup/setup-renderer.js +2 -31
  197. package/dist/world3d-react/index.d.ts +15 -25
  198. package/dist/world3d-react/index.d.ts.map +1 -1
  199. package/dist/world3d-react/index.js +15 -25
  200. package/dist/world3d-react/r3f-root-factory.d.ts +28 -34
  201. package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
  202. package/dist/world3d-react/r3f-root-factory.js +403 -57
  203. package/dist/world3d-react/rapier-physics-bridge.d.ts +30 -17
  204. package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
  205. package/dist/world3d-react/rapier-physics-bridge.js +143 -58
  206. package/package.json +1 -1
  207. package/schemas/engine-capabilities.json +16 -14
  208. package/schemas/vgai-project.schema.json +35 -26
  209. package/src/adapter/adapter-module.ts +2 -4
  210. package/src/adapter/adapter-surface.ts +1 -1
  211. package/src/adapter/authoring.ts +19 -21
  212. package/src/adapter/binding.ts +468 -0
  213. package/src/adapter/colyseus-networking-adapter.ts +20 -5
  214. package/src/adapter/entrypoint-selection-readers.ts +66 -0
  215. package/src/adapter/entrypoint-selection-source.ts +1 -40
  216. package/src/adapter/finders/scenes-from-entrypoint-selection.ts +1 -48
  217. package/src/adapter/host-context.ts +45 -15
  218. package/src/adapter/index.ts +23 -11
  219. package/src/adapter/ingest/game-contract.ts +0 -3
  220. package/src/adapter/ingest/scene-capture.ts +190 -39
  221. package/src/adapter/ingest/visible-capture-window.ts +35 -0
  222. package/src/adapter/manifest-interpreter.ts +64 -0
  223. package/src/adapter/native-debug-module.ts +248 -15
  224. package/src/adapter/rapier-physics-adapter.ts +76 -20
  225. package/src/adapter/renderer-config.ts +3 -3
  226. package/src/adapter/root-adapter.ts +7 -7
  227. package/src/adapter/system-adapter.ts +43 -8
  228. package/src/adapter/system-seam-contract.ts +3 -3
  229. package/src/adapter/system-slot.ts +291 -0
  230. package/src/adapter/transform.ts +18 -1
  231. package/src/ai/navigation.ts +30 -3
  232. package/src/animation/anim-graph-types.ts +11 -3
  233. package/src/animation/theatre-clock-binding.ts +4 -4
  234. package/src/animation/xstate-animation-binding.ts +2 -2
  235. package/src/animation/xstate-animation-meta.ts +16 -0
  236. package/src/asset-formats/index.ts +4 -7
  237. package/src/asset-formats/render-env.ts +3 -6
  238. package/src/asset-parse-error.ts +2 -5
  239. package/src/audio/bus-mixer.ts +161 -0
  240. package/src/canvas-react/index.ts +5 -21
  241. package/src/canvas-react/pixi-react-root-factory.tsx +301 -39
  242. package/src/core/countdown-timer.ts +188 -0
  243. package/src/core/deferred-commands.ts +174 -0
  244. package/src/core/frame-pacing.ts +2 -2
  245. package/src/core/game-loop.ts +1 -3
  246. package/src/core/seeded-random.ts +1 -1
  247. package/src/core/sim-clock.ts +19 -8
  248. package/src/core/types.ts +1 -3
  249. package/src/defaults.ts +6 -14
  250. package/src/dev/instruments.ts +108 -11
  251. package/src/dev/register-render-vitals.ts +1 -4
  252. package/src/dev/render-debug-adapter.ts +2 -3
  253. package/src/dev/render-vitals.ts +3 -3
  254. package/src/dev/webgl-frame-capture.ts +1 -1
  255. package/src/index.ts +2 -5
  256. package/src/input/host-pointer.ts +230 -0
  257. package/src/input/input-manager.ts +49 -13
  258. package/src/input/input-types.ts +1 -1
  259. package/src/input/rebind-controller.ts +6 -8
  260. package/src/input/schema.ts +216 -188
  261. package/src/manifest/filename.ts +8 -12
  262. package/src/manifest/load.ts +17 -13
  263. package/src/manifest/schema.ts +55 -47
  264. package/src/physics/collider-dimensions.ts +8 -12
  265. package/src/pixi/scene-capture.ts +12 -15
  266. package/src/react/world-state.tsx +1 -1
  267. package/src/render/directional-shadow-fit.ts +156 -0
  268. package/src/render/environment-capture.ts +102 -0
  269. package/src/render/ibl-override-material.ts +170 -0
  270. package/src/render/light-camera-factory.ts +8 -11
  271. package/src/render/material-factory.ts +1 -1
  272. package/src/render/render-batch-system.ts +14 -6
  273. package/src/render/render-features.ts +17 -35
  274. package/src/runtime/create-runtime.ts +270 -108
  275. package/src/runtime/debug-registry.ts +84 -8
  276. package/src/runtime/dev-build.ts +2 -2
  277. package/src/runtime/game-input-seams.ts +108 -0
  278. package/src/runtime/game.ts +231 -85
  279. package/src/runtime/mount-game.ts +31 -57
  280. package/src/runtime/mount-manifest.ts +47 -36
  281. package/src/runtime/playtest.ts +22 -0
  282. package/src/runtime/state-bridge.ts +3 -3
  283. package/src/setup/setup-audio.ts +21 -11
  284. package/src/setup/setup-renderer.ts +2 -61
  285. package/src/world3d-react/index.ts +15 -31
  286. package/src/world3d-react/r3f-root-factory.tsx +451 -60
  287. package/src/world3d-react/rapier-physics-bridge.tsx +154 -58
  288. package/dist/adapter/setup-three-root-adapter.d.ts +0 -86
  289. package/dist/adapter/setup-three-root-adapter.d.ts.map +0 -1
  290. package/dist/adapter/setup-three-root-adapter.js +0 -908
  291. package/dist/animation/clip-map.d.ts +0 -12
  292. package/dist/animation/clip-map.d.ts.map +0 -1
  293. package/dist/animation/clip-map.js +0 -31
  294. package/dist/asset-registry.d.ts +0 -38
  295. package/dist/asset-registry.d.ts.map +0 -1
  296. package/dist/asset-registry.js +0 -66
  297. package/dist/canvas-react/engine-bridge.d.ts +0 -45
  298. package/dist/canvas-react/engine-bridge.d.ts.map +0 -1
  299. package/dist/canvas-react/engine-bridge.js +0 -45
  300. package/dist/canvas-react/pixi-react-adapter.d.ts +0 -77
  301. package/dist/canvas-react/pixi-react-adapter.d.ts.map +0 -1
  302. package/dist/canvas-react/pixi-react-adapter.js +0 -294
  303. package/dist/canvas-react/world-context.d.ts +0 -98
  304. package/dist/canvas-react/world-context.d.ts.map +0 -1
  305. package/dist/canvas-react/world-context.js +0 -173
  306. package/dist/dev/debug-draw.d.ts +0 -24
  307. package/dist/dev/debug-draw.d.ts.map +0 -1
  308. package/dist/dev/debug-draw.js +0 -73
  309. package/dist/render/auto-batcher.d.ts +0 -34
  310. package/dist/render/auto-batcher.d.ts.map +0 -1
  311. package/dist/render/auto-batcher.js +0 -140
  312. package/dist/render/lod.d.ts +0 -13
  313. package/dist/render/lod.d.ts.map +0 -1
  314. package/dist/render/lod.js +0 -16
  315. package/dist/runtime/types.d.ts +0 -303
  316. package/dist/runtime/types.d.ts.map +0 -1
  317. package/dist/runtime/types.js +0 -1
  318. package/dist/world3d-react/engine-bridge.d.ts +0 -47
  319. package/dist/world3d-react/engine-bridge.d.ts.map +0 -1
  320. package/dist/world3d-react/engine-bridge.js +0 -73
  321. package/dist/world3d-react/r3f-adapter.d.ts +0 -58
  322. package/dist/world3d-react/r3f-adapter.d.ts.map +0 -1
  323. package/dist/world3d-react/r3f-adapter.js +0 -634
  324. package/dist/world3d-react/world-context.d.ts +0 -182
  325. package/dist/world3d-react/world-context.d.ts.map +0 -1
  326. package/dist/world3d-react/world-context.js +0 -235
  327. package/src/adapter/setup-three-root-adapter.ts +0 -1032
  328. package/src/animation/clip-map.ts +0 -34
  329. package/src/asset-registry.ts +0 -89
  330. package/src/canvas-react/engine-bridge.ts +0 -59
  331. package/src/canvas-react/pixi-react-adapter.tsx +0 -356
  332. package/src/canvas-react/world-context.ts +0 -253
  333. package/src/dev/debug-draw.ts +0 -80
  334. package/src/render/auto-batcher.ts +0 -168
  335. package/src/render/lod.ts +0 -17
  336. package/src/runtime/types.ts +0 -328
  337. package/src/world3d-react/engine-bridge.ts +0 -86
  338. package/src/world3d-react/r3f-adapter.tsx +0 -717
  339. package/src/world3d-react/world-context.ts +0 -358
@@ -1,182 +0,0 @@
1
- /**
2
- * `createR3FRootContext` — the ENGINE runtime an R3F world owns, split out of
3
- * `./r3f-adapter.tsx` so it stays react-free and headlessly unit-testable
4
- * (`test/world3d-react-context-parity.test.tsx`).
5
- *
6
- * Context parity with the classic first-party adapter
7
- * (`../adapter/setup-three-root-adapter.ts`) — closed here after a dogfood
8
- * build had to shim around three silent gaps:
9
- *
10
- * 1. **`ctx.debug` / `ctx.random` / `ctx.game` / `ctx.roots` are wired**
11
- * exactly the way the classic adapter wires them: the ONE game-scoped
12
- * debug registry (`getDebugRegistry(host.game)`) provides
13
- * `ctx.debug = registry.forRoot(id)`, so a component's
14
- * effect can `ctx.debug.registerStateProvider(...)` and be visible
15
- * to `vgai eval`/the editor's debug panels — previously a silent no-op.
16
- * 2. **`ctx.input` is the game-owned `InputManager` when a Game hosts the
17
- * root** (`host.game.input` —
18
- * the same instance the classic adapter uses when hosted), and this
19
- * module registers the SAME per-world debug-registry seams the classic
20
- * adapter registers (`setVirtualInputTarget`/`setInputActionsSource`/
21
- * `setInputTraceSource`), which is what makes `game.input.*` (the bot
22
- * input doctrine) work instead of throwing `DEBUG_INPUT_UNAVAILABLE`.
23
- * A bare/foreign mount receives `null`, visibly in the type, rather than
24
- * a getter that throws later inside a frame callback.
25
- * The project's input map is loaded through `Game.loadInputMap` (the
26
- * game-owned, load-once path every root shares) with the SAME default
27
- * path the classic adapter uses. A missing/unparseable map degrades
28
- * LOUDLY (console.error naming the path and what breaks) without
29
- * failing the mount — an R3F tree with no actions (e.g. a pure
30
- * OrbitControls demo, `examples/r3f-first-party`) is legal.
31
- * 3. **Absent subsystems are absent from the TYPE.** An R3F world builds no
32
- * Rapier world, composer, audio, particles, or debug-draw, and
33
- * {@link R3FGameContext} — the type `useGameContext()` hands back — simply
34
- * does not have those fields. Reaching for one is a compile error at the
35
- * call site, with the sanctioned alternative in that type's doc comment.
36
- * */
37
- import type * as THREE from 'three';
38
- import type { ThreeHostContext } from '../adapter';
39
- import type { SystemAdapters } from '../adapter/system-adapter';
40
- import { createSystemRunner } from '../core/system-runner';
41
- import { type DebugRegistry } from '../runtime/debug-registry';
42
- import type { ComponentContextBase, GameContext } from '../runtime/types';
43
- /** The classic adapter's default input-map path — the conventional location
44
- * every scaffolded project ships (`public/inputmaps/default.inputmap.json`). */
45
- export declare const DEFAULT_INPUT_MAP_PATH = "/inputmaps/default.inputmap.json";
46
- /**
47
- * The engine context an R3F world genuinely provides — what
48
- * `useGameContext()` hands back.
49
- *
50
- * An EXPLICIT `Pick`, never an `Omit`: a field added to `GameContext` later
51
- * must be deliberately admitted here, so this surface can never silently widen
52
- * into something an R3F mount does not build.
53
- *
54
- * **What an R3F world does NOT have, and what to reach for instead.** These
55
- * are not oversights — the mount builds none of them, so they are absent from
56
- * this type and naming one is a compile error rather than a crash mid-render:
57
- *
58
- * - **Physics** (`rapierWorld`, `rapier`, `physics`, `collisions`) — an R3F
59
- * world does not build the first-party Rapier runtime. Use a physics
60
- * solution inside the fiber tree (e.g. `@react-three/rapier`), or an
61
- * imperative three root whose `setup(ctx)` receives the first-party Rapier
62
- * runtime.
63
- * - **`composer`** — R3F renders through react-three-fiber against the host
64
- * renderer; there is no postprocessing `EffectComposer` here. Use
65
- * fiber-native postprocessing inside the tree.
66
- * - **`audio`** — no first-party audio context is built. Own a Web Audio graph
67
- * inside the tree (or use drei's audio helpers) and register it as this
68
- * world's `SystemAdapters.audio` via
69
- * `ctx.registerSystemAdapter('audio', …)`, so host mute/pause can still
70
- * silence it — `examples/third-person/src/lib/audio/sfx.ts` is the worked
71
- * reference.
72
- * - **`particles`** — no three.quarks particle runtime. Drive particles from
73
- * the fiber tree.
74
- * - **`debugDraw`** — no debug-draw helper. Add helper objects to the fiber
75
- * tree directly.
76
- *
77
- * For the full classic runtime, use an imperative three root instead: a module
78
- * exporting `setup(ctx): Promise<GameCleanup>`, which `SetupThreeRootAdapter`
79
- * mounts with the complete {@link GameContext}.
80
- */
81
- export type R3FGameContext = ComponentContextBase & Pick<GameContext, 'scene' | 'sceneIndex' | 'assets' | 'clock'> & {
82
- /** Fiber may install either a perspective or orthographic camera. */
83
- camera: THREE.Camera;
84
- };
85
- export interface R3FRootContextOptions {
86
- /** World id — provenance for every debug-registry registration (matches the
87
- * manifest root id by the same convention the classic adapter follows). */
88
- readonly id: string;
89
- /**
90
- * Input map to load through `Game.loadInputMap` (game-owned, load-once —
91
- * several roots asking for the SAME path share one load; competing paths
92
- * throw there). Defaults to {@link DEFAULT_INPUT_MAP_PATH}, the classic
93
- * adapter's own default. Pass `null` to skip loading (a world with no
94
- * actions). Ignored when no `host.game` is present.
95
- */
96
- readonly inputMapPath?: string | null | undefined;
97
- }
98
- /** What {@link createR3FRootContext} returns — the engine runtime one R3F
99
- * world owns, plus the wiring hooks `createR3FAdapter`'s `mount()` drives. */
100
- export interface R3FRootRuntime {
101
- /** The context this world's components read through `useGameContext()` —
102
- * see {@link R3FGameContext} for exactly what an R3F world provides. */
103
- readonly ctx: R3FGameContext;
104
- /** The world-local phase runner `mount().update(dt)` ticks. */
105
- readonly systems: ReturnType<typeof createSystemRunner>;
106
- /** The mounted world's `SystemAdapters` bag (`mounted.systems`) —
107
- * `ctx.registerSystemAdapter` writes into it; `debug` is pre-seeded here,
108
- * and the R3F mount adds renderer-backed engine capabilities after commit. */
109
- readonly systemAdapters: SystemAdapters;
110
- /** The registry every registration of this world's lands in — the game's
111
- * own when a `Game` hosts it, a private mount-local one otherwise. Exposed
112
- * (rather than re-derived by the caller) because `createR3FAdapter` seeds
113
- * the render-vitals door onto THIS registry after fiber's first commit
114
- * resolves the scene, and two `getDebugRegistry` calls that are "obviously"
115
- * the same object is exactly the second source of truth the classic
116
- * adapter's single `debugRegistry` local avoids. */
117
- readonly debugRegistry: DebugRegistry;
118
- /** Resolves once the input map load settles (immediately when skipped).
119
- * Never rejects — a failed load reports loudly and resolves. */
120
- readonly inputMapReady: Promise<void>;
121
- /** Backfill fiber's real scene/camera onto `ctx` after the first commit
122
- * resolves them — before any tick can run. `camera` is fiber's union
123
- * (perspective OR orthographic) — the ctx surface exposes whatever the
124
- * tree declared, exactly as the mounted world does. */
125
- setSceneCamera(scene: THREE.Scene, camera: THREE.Camera): void;
126
- /** Detach every component and strip this world's debug registrations
127
- * (scoped, like the classic adapter's teardown — sibling roots and
128
- * react-door registrations are untouched when a Game hosts this world). */
129
- dispose(): void;
130
- }
131
- /**
132
- * The GAME-SCOPED input wiring one three root owns — and the half that has
133
- * nothing to do with the vgai runtime context.
134
- *
135
- * Two things happen here, both of them HOST work performed from outside the
136
- * world's own tree:
137
- *
138
- * 1. the project's input map is loaded through `Game.loadInputMap` (game-owned,
139
- * load-once), so the actions the project declared exist;
140
- * 2. this root's seams are registered on the game-scoped debug registry
141
- * (`setVirtualInputTarget`/`setInputActionsSource`/`setInputTraceSource`),
142
- * which is what makes `game.input.hold/tap/...` (the bot/`vgai eval` input
143
- * doctrine) and the built-in `input.actions`/`input.trace` providers
144
- * resolve to this world instead of throwing `DEBUG_INPUT_UNAVAILABLE`.
145
- *
146
- * It is called on BOTH three-root paths, and that is the point: a world that
147
- * mounts as a default-exported component receives no vgai runtime context
148
- * (`engineRuntime: false` — see `r3f-root-factory.tsx`), but "no context in the
149
- * tree" was never meant to say "the host does not wire this game's input". It
150
- * was read that way, and the cost was measured on a fresh scaffold: the
151
- * declared map never loaded and every `game.input.*` door threw, so the QA
152
- * bot, autoplay routes and `vgai eval` could not drive the ONE world shape
153
- * `vgai create` produces. Same split, same reasoning, as `WorldProvider`
154
- * riding `host.game` rather than the runtime (see `r3f-adapter.tsx`).
155
- *
156
- * `optionalInputMap` is the CONVENTIONAL-PATH probe used when the project
157
- * never named a map: an absent file is the ordinary state of a brand-new
158
- * project, so it resolves quietly instead of printing an error on every boot.
159
- * A map that exists and fails to parse is as loud as ever.
160
- *
161
- * LIFETIME, honestly: `registry.strip(id)` runs on the mount's dispose (both
162
- * paths), but it walks providers/commands only — the three seams set here
163
- * survive it, keyed by this world's id, until a remount of the same id
164
- * overwrites them. That residue is inert rather than hidden: every closure
165
- * below reads GAME-scoped state (`game.input`, `game.loop.fixedDt`, the game's
166
- * seed), so a stale entry actuates exactly what a live one would. See the
167
- * dispose comment in `r3f-adapter.tsx` for the full accounting.
168
- */
169
- export declare function wireGameInputSeams(host: ThreeHostContext, registry: DebugRegistry, options: {
170
- readonly id: string;
171
- readonly inputMapPath?: string | null | undefined;
172
- readonly optionalInputMap?: boolean | undefined;
173
- }): Promise<void>;
174
- /**
175
- * Build the engine runtime for one R3F world from a `ThreeHostContext` — the same
176
- * wiring, at the same layer, as the classic `SetupThreeRootAdapter.mount()`
177
- * performs for the subsystems an R3F world shares with it (debug, input,
178
- * random). The ones it does not build are not on
179
- * {@link R3FGameContext} at all.
180
- */
181
- export declare function createR3FRootContext(host: ThreeHostContext, options: R3FRootContextOptions): R3FRootRuntime;
182
- //# sourceMappingURL=world-context.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"world-context.d.ts","sourceRoot":"","sources":["../../src/world3d-react/world-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAmCK;AAEL,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAOhE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAEL,KAAK,aAAa,EAEnB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE1E;iFACiF;AACjF,eAAO,MAAM,sBAAsB,qCAAqC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,MAAM,cAAc,GAAG,oBAAoB,GAC/C,IAAI,CAAC,WAAW,EAAE,OAAO,GAAG,YAAY,GAAG,QAAQ,GAAG,OAAO,CAAC,GAAG;IAC/D,qEAAqE;IACrE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;CACtB,CAAC;AAEJ,MAAM,WAAW,qBAAqB;IACpC;gFAC4E;IAC5E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;CACnD;AAED;+EAC+E;AAC/E,MAAM,WAAW,cAAc;IAC7B;6EACyE;IACzE,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;IACxD;;mFAE+E;IAC/E,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC;;;;;;yDAMqD;IACrD,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC;qEACiE;IACjE,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC;;;4DAGwD;IACxD,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;IAC/D;;gFAE4E;IAC5E,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,gBAAgB,EACtB,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE;IACP,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACjD,GACA,OAAO,CAAC,IAAI,CAAC,CAiDf;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,qBAAqB,GAC7B,cAAc,CAqGhB"}
@@ -1,235 +0,0 @@
1
- /**
2
- * `createR3FRootContext` — the ENGINE runtime an R3F world owns, split out of
3
- * `./r3f-adapter.tsx` so it stays react-free and headlessly unit-testable
4
- * (`test/world3d-react-context-parity.test.tsx`).
5
- *
6
- * Context parity with the classic first-party adapter
7
- * (`../adapter/setup-three-root-adapter.ts`) — closed here after a dogfood
8
- * build had to shim around three silent gaps:
9
- *
10
- * 1. **`ctx.debug` / `ctx.random` / `ctx.game` / `ctx.roots` are wired**
11
- * exactly the way the classic adapter wires them: the ONE game-scoped
12
- * debug registry (`getDebugRegistry(host.game)`) provides
13
- * `ctx.debug = registry.forRoot(id)`, so a component's
14
- * effect can `ctx.debug.registerStateProvider(...)` and be visible
15
- * to `vgai eval`/the editor's debug panels — previously a silent no-op.
16
- * 2. **`ctx.input` is the game-owned `InputManager` when a Game hosts the
17
- * root** (`host.game.input` —
18
- * the same instance the classic adapter uses when hosted), and this
19
- * module registers the SAME per-world debug-registry seams the classic
20
- * adapter registers (`setVirtualInputTarget`/`setInputActionsSource`/
21
- * `setInputTraceSource`), which is what makes `game.input.*` (the bot
22
- * input doctrine) work instead of throwing `DEBUG_INPUT_UNAVAILABLE`.
23
- * A bare/foreign mount receives `null`, visibly in the type, rather than
24
- * a getter that throws later inside a frame callback.
25
- * The project's input map is loaded through `Game.loadInputMap` (the
26
- * game-owned, load-once path every root shares) with the SAME default
27
- * path the classic adapter uses. A missing/unparseable map degrades
28
- * LOUDLY (console.error naming the path and what breaks) without
29
- * failing the mount — an R3F tree with no actions (e.g. a pure
30
- * OrbitControls demo, `examples/r3f-first-party`) is legal.
31
- * 3. **Absent subsystems are absent from the TYPE.** An R3F world builds no
32
- * Rapier world, composer, audio, particles, or debug-draw, and
33
- * {@link R3FGameContext} — the type `useGameContext()` hands back — simply
34
- * does not have those fields. Reaching for one is a compile error at the
35
- * call site, with the sanctioned alternative in that type's doc comment.
36
- * */
37
- import { createSeededRandom, DEFAULT_SEEDED_RANDOM_SEED, getSeededRandom, } from '../core/seeded-random';
38
- import { createSimClock, getSimClock } from '../core/sim-clock';
39
- import { createSystemRunner } from '../core/system-runner';
40
- import { createSceneIndex } from '../ecs/scene-index';
41
- import { createDebugRegistry, getDebugRegistry, } from '../runtime/debug-registry';
42
- import { disposeDebrisSubtree } from '../runtime/game';
43
- /** The classic adapter's default input-map path — the conventional location
44
- * every scaffolded project ships (`public/inputmaps/default.inputmap.json`). */
45
- export const DEFAULT_INPUT_MAP_PATH = '/inputmaps/default.inputmap.json';
46
- /**
47
- * The GAME-SCOPED input wiring one three root owns — and the half that has
48
- * nothing to do with the vgai runtime context.
49
- *
50
- * Two things happen here, both of them HOST work performed from outside the
51
- * world's own tree:
52
- *
53
- * 1. the project's input map is loaded through `Game.loadInputMap` (game-owned,
54
- * load-once), so the actions the project declared exist;
55
- * 2. this root's seams are registered on the game-scoped debug registry
56
- * (`setVirtualInputTarget`/`setInputActionsSource`/`setInputTraceSource`),
57
- * which is what makes `game.input.hold/tap/...` (the bot/`vgai eval` input
58
- * doctrine) and the built-in `input.actions`/`input.trace` providers
59
- * resolve to this world instead of throwing `DEBUG_INPUT_UNAVAILABLE`.
60
- *
61
- * It is called on BOTH three-root paths, and that is the point: a world that
62
- * mounts as a default-exported component receives no vgai runtime context
63
- * (`engineRuntime: false` — see `r3f-root-factory.tsx`), but "no context in the
64
- * tree" was never meant to say "the host does not wire this game's input". It
65
- * was read that way, and the cost was measured on a fresh scaffold: the
66
- * declared map never loaded and every `game.input.*` door threw, so the QA
67
- * bot, autoplay routes and `vgai eval` could not drive the ONE world shape
68
- * `vgai create` produces. Same split, same reasoning, as `WorldProvider`
69
- * riding `host.game` rather than the runtime (see `r3f-adapter.tsx`).
70
- *
71
- * `optionalInputMap` is the CONVENTIONAL-PATH probe used when the project
72
- * never named a map: an absent file is the ordinary state of a brand-new
73
- * project, so it resolves quietly instead of printing an error on every boot.
74
- * A map that exists and fails to parse is as loud as ever.
75
- *
76
- * LIFETIME, honestly: `registry.strip(id)` runs on the mount's dispose (both
77
- * paths), but it walks providers/commands only — the three seams set here
78
- * survive it, keyed by this world's id, until a remount of the same id
79
- * overwrites them. That residue is inert rather than hidden: every closure
80
- * below reads GAME-scoped state (`game.input`, `game.loop.fixedDt`, the game's
81
- * seed), so a stale entry actuates exactly what a live one would. See the
82
- * dispose comment in `r3f-adapter.tsx` for the full accounting.
83
- */
84
- export function wireGameInputSeams(host, registry, options) {
85
- const { id, inputMapPath = DEFAULT_INPUT_MAP_PATH, optionalInputMap = false } = options;
86
- if (!host.game)
87
- return Promise.resolve();
88
- const game = host.game;
89
- const input = game.input;
90
- registry.setInputActionsSource(id, () => input.actionNames().map((name) => ({ name, valueType: input.getActionValueType(name) })));
91
- registry.setInputTraceSource(id, () => {
92
- const raw = input.getInputTrace();
93
- return {
94
- version: raw.version,
95
- seed: getSeededRandom(game)?.seed ?? null,
96
- fixedDt: game.loop.fixedDt,
97
- ticks: raw.ticks,
98
- };
99
- });
100
- registry.setVirtualInputTarget(id, {
101
- setVirtualAction: (action, value) => input.setVirtualAction(action, value),
102
- tapVirtualAction: (action) => input.tapVirtualAction(action),
103
- clearVirtualActions: () => input.clearVirtualActions(),
104
- scheduleActionAtTick: (tick, action, value) => input.scheduleActionAtTick(tick, action, value),
105
- startInputRecording: () => input.startInputRecording(),
106
- stopInputRecording: () => input.stopInputRecording(),
107
- isInputRecording: () => input.isInputRecording(),
108
- injectAxis: (sourceId, value) => input.injectAxis(sourceId, value),
109
- injectVector2: (sourceId, value) => input.injectVector2(sourceId, value),
110
- injectPointerDelta: (sourceId, delta) => input.injectPointerDelta(sourceId, delta),
111
- injectPointerPosition: (sourceId, value) => input.injectPointerPosition(sourceId, value),
112
- });
113
- if (host.headless || inputMapPath === null)
114
- return Promise.resolve();
115
- // Load-once through the game-owned path (competing paths across roots throw
116
- // THERE, loudly). A FAILED load (missing/bad file) must not fail the mount:
117
- // a three world with no declared actions is legal. It degrades loudly
118
- // instead — naming exactly what breaks.
119
- // The non-optional call passes ONE argument, exactly as it always has — a
120
- // trailing `undefined` is a different call to any observer of it.
121
- const load = optionalInputMap
122
- ? game.loadInputMap(inputMapPath, { optional: true })
123
- : game.loadInputMap(inputMapPath);
124
- return load.catch((err) => {
125
- // biome-ignore lint/suspicious/noConsole: deliberate loud degrade — the documented alternative to failing the mount (see comment above)
126
- console.error(`three world "${id}": failed to load input map "${inputMapPath}" — declared input ` +
127
- 'actions and `game.input.*` (bot/virtual input) will not work until a valid map ' +
128
- 'loads. Ship one at the conventional path or pass `inputMapPath` to ' +
129
- `createR3FAdapter (null to opt out). Cause: ${err instanceof Error ? err.message : String(err)}`);
130
- });
131
- }
132
- /**
133
- * Build the engine runtime for one R3F world from a `ThreeHostContext` — the same
134
- * wiring, at the same layer, as the classic `SetupThreeRootAdapter.mount()`
135
- * performs for the subsystems an R3F world shares with it (debug, input,
136
- * random). The ones it does not build are not on
137
- * {@link R3FGameContext} at all.
138
- */
139
- export function createR3FRootContext(host, options) {
140
- const { id, inputMapPath = DEFAULT_INPUT_MAP_PATH } = options;
141
- // Same observer threading as the classic adapter — profiler sees this
142
- // world's phases under the shared game profiler when hosted.
143
- const systems = createSystemRunner(host.game?.profiler.systemObserver, 'three');
144
- // The mounted world's adapter surface (`mounted.systems`) — declared before
145
- // ctx so `ctx.registerSystemAdapter` can close over it (classic parity: a
146
- // game-owned capability like networking registers here from component code).
147
- const systemAdapters = {};
148
- // The world's live scene index — built in `setSceneCamera` (below), because
149
- // fiber resolves the scene after this function returns. Same lifetime as
150
- // `ctx.scene`, which is backfilled at the same moment.
151
- let sceneIndex = null;
152
- const ctxRaw = {
153
- systems,
154
- assets: host.assets,
155
- input: host.game?.input ?? null,
156
- registerSystemAdapter: (kind, adapter) => {
157
- // biome-ignore lint/suspicious/noExplicitAny: same per-key record write the classic adapter performs; correct by construction
158
- systemAdapters[kind] = adapter;
159
- host.game?.notifySystemAdaptersChanged();
160
- },
161
- };
162
- const ctx = ctxRaw;
163
- if (host.game) {
164
- ctx.game = host.game;
165
- ctx.roots = host.game.roots;
166
- ctx.playtest = host.game.playtest ?? null;
167
- }
168
- // --- ctx.debug (gap 1) — the ONE registry per Game root, shared across
169
- // every world mounted onto it; a bare mount with no Game shell gets a
170
- // private, mount-local registry instead (same absence precedent as the
171
- // classic adapter). ---
172
- const debugRegistry = (host.game ? getDebugRegistry(host.game) : null) ??
173
- createDebugRegistry({ getTick: () => 0, getSimT: () => 0 });
174
- systemAdapters.debug = debugRegistry.adapter;
175
- ctx.debug = debugRegistry.forRoot(id);
176
- ctx.random =
177
- (host.game ? getSeededRandom(host.game) : null) ??
178
- createSeededRandom(DEFAULT_SEEDED_RANDOM_SEED);
179
- // --- ctx.clock (P3) — every world has a sim clock, R3F included. Same
180
- // game-scoped reach-in as `ctx.random` above; a bare mount with no Game
181
- // shell gets a private, mount-local clock that nothing flushes (there is no
182
- // fixed loop to bind timers to). The mount-local fallback uses the SAME
183
- // `disposeDebrisSubtree` the hosted path uses — it drops
184
- // the subtree and disposes geometry/materials, with no Rapier owners
185
- // because this ctx carries no `physics`/`rapierWorld` (the
186
- // honest "this world owns no bodies").
187
- const gameClock = host.game ? getSimClock(host.game) : null;
188
- const clock = gameClock ?? createSimClock({ dispose: (obj) => disposeDebrisSubtree(obj, []) });
189
- ctx.clock = clock;
190
- // --- ctx.input + the debug-registry input seams (gap 3) — see
191
- // `wireGameInputSeams` below; `ctx.input` is that same game-owned manager. ---
192
- const inputMapReady = wireGameInputSeams(host, debugRegistry, { id, inputMapPath });
193
- return {
194
- ctx,
195
- systems,
196
- systemAdapters,
197
- debugRegistry,
198
- inputMapReady,
199
- setSceneCamera(scene, camera) {
200
- const sameScene = ctxRaw['scene'] === scene && sceneIndex !== null;
201
- ctxRaw['scene'] = scene;
202
- ctxRaw['camera'] = camera;
203
- // The live scene index (P2 `observe`) is a REAL value here, not a
204
- // throwing getter — every three world has one. It can only be built once
205
- // fiber has resolved the scene, which is the same moment `ctx.scene`
206
- // itself becomes readable, so index and scene share one lifetime. A
207
- // second commit (fiber handing back a different scene) replaces it.
208
- //
209
- // Keyed on the SCENE, so a camera-only backfill (a world swapping the
210
- // default camera after mount) leaves the index — and every `observe`
211
- // subscription hanging off it — alone.
212
- if (sameScene)
213
- return;
214
- sceneIndex?.dispose();
215
- sceneIndex = createSceneIndex(scene);
216
- ctxRaw['sceneIndex'] = sceneIndex;
217
- },
218
- dispose() {
219
- // Same ownership rule as the classic adapter's teardown: dispose ONLY a
220
- // clock this mount created. The game-scoped one belongs to
221
- // `GameInternal.dispose()` — killing it here would freeze sim time and
222
- // cancel every SIBLING world's timers.
223
- if (!gameClock)
224
- clock.dispose();
225
- sceneIndex?.dispose();
226
- sceneIndex = null;
227
- // Scoped strip when a Game hosts this world (sibling roots stay live);
228
- // a bare standalone mount owns its private registry and clears it all.
229
- if (host.game)
230
- debugRegistry.strip(id);
231
- else
232
- debugRegistry.strip();
233
- },
234
- };
235
- }