@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,358 +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
-
38
- import type * as THREE from 'three';
39
- import type { ThreeHostContext } from '../adapter';
40
- import type { SystemAdapters } from '../adapter/system-adapter';
41
- import {
42
- createSeededRandom,
43
- DEFAULT_SEEDED_RANDOM_SEED,
44
- getSeededRandom,
45
- } from '../core/seeded-random';
46
- import { createSimClock, getSimClock, type SimClockInternal } from '../core/sim-clock';
47
- import { createSystemRunner } from '../core/system-runner';
48
- import { createSceneIndex, type SceneIndex } from '../ecs/scene-index';
49
- import {
50
- createDebugRegistry,
51
- type DebugRegistry,
52
- getDebugRegistry,
53
- } from '../runtime/debug-registry';
54
- import { disposeDebrisSubtree } from '../runtime/game';
55
- import type { ComponentContextBase, GameContext } from '../runtime/types';
56
-
57
- /** The classic adapter's default input-map path — the conventional location
58
- * every scaffolded project ships (`public/inputmaps/default.inputmap.json`). */
59
- export const DEFAULT_INPUT_MAP_PATH = '/inputmaps/default.inputmap.json';
60
-
61
- /**
62
- * The engine context an R3F world genuinely provides — what
63
- * `useGameContext()` hands back.
64
- *
65
- * An EXPLICIT `Pick`, never an `Omit`: a field added to `GameContext` later
66
- * must be deliberately admitted here, so this surface can never silently widen
67
- * into something an R3F mount does not build.
68
- *
69
- * **What an R3F world does NOT have, and what to reach for instead.** These
70
- * are not oversights — the mount builds none of them, so they are absent from
71
- * this type and naming one is a compile error rather than a crash mid-render:
72
- *
73
- * - **Physics** (`rapierWorld`, `rapier`, `physics`, `collisions`) — an R3F
74
- * world does not build the first-party Rapier runtime. Use a physics
75
- * solution inside the fiber tree (e.g. `@react-three/rapier`), or an
76
- * imperative three root whose `setup(ctx)` receives the first-party Rapier
77
- * runtime.
78
- * - **`composer`** — R3F renders through react-three-fiber against the host
79
- * renderer; there is no postprocessing `EffectComposer` here. Use
80
- * fiber-native postprocessing inside the tree.
81
- * - **`audio`** — no first-party audio context is built. Own a Web Audio graph
82
- * inside the tree (or use drei's audio helpers) and register it as this
83
- * world's `SystemAdapters.audio` via
84
- * `ctx.registerSystemAdapter('audio', …)`, so host mute/pause can still
85
- * silence it — `examples/third-person/src/lib/audio/sfx.ts` is the worked
86
- * reference.
87
- * - **`particles`** — no three.quarks particle runtime. Drive particles from
88
- * the fiber tree.
89
- * - **`debugDraw`** — no debug-draw helper. Add helper objects to the fiber
90
- * tree directly.
91
- *
92
- * For the full classic runtime, use an imperative three root instead: a module
93
- * exporting `setup(ctx): Promise<GameCleanup>`, which `SetupThreeRootAdapter`
94
- * mounts with the complete {@link GameContext}.
95
- */
96
- export type R3FGameContext = ComponentContextBase &
97
- Pick<GameContext, 'scene' | 'sceneIndex' | 'assets' | 'clock'> & {
98
- /** Fiber may install either a perspective or orthographic camera. */
99
- camera: THREE.Camera;
100
- };
101
-
102
- export interface R3FRootContextOptions {
103
- /** World id — provenance for every debug-registry registration (matches the
104
- * manifest root id by the same convention the classic adapter follows). */
105
- readonly id: string;
106
- /**
107
- * Input map to load through `Game.loadInputMap` (game-owned, load-once —
108
- * several roots asking for the SAME path share one load; competing paths
109
- * throw there). Defaults to {@link DEFAULT_INPUT_MAP_PATH}, the classic
110
- * adapter's own default. Pass `null` to skip loading (a world with no
111
- * actions). Ignored when no `host.game` is present.
112
- */
113
- readonly inputMapPath?: string | null | undefined;
114
- }
115
-
116
- /** What {@link createR3FRootContext} returns — the engine runtime one R3F
117
- * world owns, plus the wiring hooks `createR3FAdapter`'s `mount()` drives. */
118
- export interface R3FRootRuntime {
119
- /** The context this world's components read through `useGameContext()` —
120
- * see {@link R3FGameContext} for exactly what an R3F world provides. */
121
- readonly ctx: R3FGameContext;
122
- /** The world-local phase runner `mount().update(dt)` ticks. */
123
- readonly systems: ReturnType<typeof createSystemRunner>;
124
- /** The mounted world's `SystemAdapters` bag (`mounted.systems`) —
125
- * `ctx.registerSystemAdapter` writes into it; `debug` is pre-seeded here,
126
- * and the R3F mount adds renderer-backed engine capabilities after commit. */
127
- readonly systemAdapters: SystemAdapters;
128
- /** The registry every registration of this world's lands in — the game's
129
- * own when a `Game` hosts it, a private mount-local one otherwise. Exposed
130
- * (rather than re-derived by the caller) because `createR3FAdapter` seeds
131
- * the render-vitals door onto THIS registry after fiber's first commit
132
- * resolves the scene, and two `getDebugRegistry` calls that are "obviously"
133
- * the same object is exactly the second source of truth the classic
134
- * adapter's single `debugRegistry` local avoids. */
135
- readonly debugRegistry: DebugRegistry;
136
- /** Resolves once the input map load settles (immediately when skipped).
137
- * Never rejects — a failed load reports loudly and resolves. */
138
- readonly inputMapReady: Promise<void>;
139
- /** Backfill fiber's real scene/camera onto `ctx` after the first commit
140
- * resolves them — before any tick can run. `camera` is fiber's union
141
- * (perspective OR orthographic) — the ctx surface exposes whatever the
142
- * tree declared, exactly as the mounted world does. */
143
- setSceneCamera(scene: THREE.Scene, camera: THREE.Camera): void;
144
- /** Detach every component and strip this world's debug registrations
145
- * (scoped, like the classic adapter's teardown — sibling roots and
146
- * react-door registrations are untouched when a Game hosts this world). */
147
- dispose(): void;
148
- }
149
-
150
- /**
151
- * The GAME-SCOPED input wiring one three root owns — and the half that has
152
- * nothing to do with the vgai runtime context.
153
- *
154
- * Two things happen here, both of them HOST work performed from outside the
155
- * world's own tree:
156
- *
157
- * 1. the project's input map is loaded through `Game.loadInputMap` (game-owned,
158
- * load-once), so the actions the project declared exist;
159
- * 2. this root's seams are registered on the game-scoped debug registry
160
- * (`setVirtualInputTarget`/`setInputActionsSource`/`setInputTraceSource`),
161
- * which is what makes `game.input.hold/tap/...` (the bot/`vgai eval` input
162
- * doctrine) and the built-in `input.actions`/`input.trace` providers
163
- * resolve to this world instead of throwing `DEBUG_INPUT_UNAVAILABLE`.
164
- *
165
- * It is called on BOTH three-root paths, and that is the point: a world that
166
- * mounts as a default-exported component receives no vgai runtime context
167
- * (`engineRuntime: false` — see `r3f-root-factory.tsx`), but "no context in the
168
- * tree" was never meant to say "the host does not wire this game's input". It
169
- * was read that way, and the cost was measured on a fresh scaffold: the
170
- * declared map never loaded and every `game.input.*` door threw, so the QA
171
- * bot, autoplay routes and `vgai eval` could not drive the ONE world shape
172
- * `vgai create` produces. Same split, same reasoning, as `WorldProvider`
173
- * riding `host.game` rather than the runtime (see `r3f-adapter.tsx`).
174
- *
175
- * `optionalInputMap` is the CONVENTIONAL-PATH probe used when the project
176
- * never named a map: an absent file is the ordinary state of a brand-new
177
- * project, so it resolves quietly instead of printing an error on every boot.
178
- * A map that exists and fails to parse is as loud as ever.
179
- *
180
- * LIFETIME, honestly: `registry.strip(id)` runs on the mount's dispose (both
181
- * paths), but it walks providers/commands only — the three seams set here
182
- * survive it, keyed by this world's id, until a remount of the same id
183
- * overwrites them. That residue is inert rather than hidden: every closure
184
- * below reads GAME-scoped state (`game.input`, `game.loop.fixedDt`, the game's
185
- * seed), so a stale entry actuates exactly what a live one would. See the
186
- * dispose comment in `r3f-adapter.tsx` for the full accounting.
187
- */
188
- export function wireGameInputSeams(
189
- host: ThreeHostContext,
190
- registry: DebugRegistry,
191
- options: {
192
- readonly id: string;
193
- readonly inputMapPath?: string | null | undefined;
194
- readonly optionalInputMap?: boolean | undefined;
195
- },
196
- ): Promise<void> {
197
- const { id, inputMapPath = DEFAULT_INPUT_MAP_PATH, optionalInputMap = false } = options;
198
- if (!host.game) return Promise.resolve();
199
- const game = host.game;
200
- const input = game.input;
201
- registry.setInputActionsSource(id, () =>
202
- input.actionNames().map((name) => ({ name, valueType: input.getActionValueType(name) })),
203
- );
204
- registry.setInputTraceSource(id, () => {
205
- const raw = input.getInputTrace();
206
- return {
207
- version: raw.version,
208
- seed: getSeededRandom(game)?.seed ?? null,
209
- fixedDt: game.loop.fixedDt,
210
- ticks: raw.ticks,
211
- };
212
- });
213
- registry.setVirtualInputTarget(id, {
214
- setVirtualAction: (action, value) => input.setVirtualAction(action, value),
215
- tapVirtualAction: (action) => input.tapVirtualAction(action),
216
- clearVirtualActions: () => input.clearVirtualActions(),
217
- scheduleActionAtTick: (tick, action, value) => input.scheduleActionAtTick(tick, action, value),
218
- startInputRecording: () => input.startInputRecording(),
219
- stopInputRecording: () => input.stopInputRecording(),
220
- isInputRecording: () => input.isInputRecording(),
221
- injectAxis: (sourceId, value) => input.injectAxis(sourceId, value),
222
- injectVector2: (sourceId, value) => input.injectVector2(sourceId, value),
223
- injectPointerDelta: (sourceId, delta) => input.injectPointerDelta(sourceId, delta),
224
- injectPointerPosition: (sourceId, value) => input.injectPointerPosition(sourceId, value),
225
- });
226
- if (host.headless || inputMapPath === null) return Promise.resolve();
227
- // Load-once through the game-owned path (competing paths across roots throw
228
- // THERE, loudly). A FAILED load (missing/bad file) must not fail the mount:
229
- // a three world with no declared actions is legal. It degrades loudly
230
- // instead — naming exactly what breaks.
231
- // The non-optional call passes ONE argument, exactly as it always has — a
232
- // trailing `undefined` is a different call to any observer of it.
233
- const load = optionalInputMap
234
- ? game.loadInputMap(inputMapPath, { optional: true })
235
- : game.loadInputMap(inputMapPath);
236
- return load.catch((err: unknown) => {
237
- // biome-ignore lint/suspicious/noConsole: deliberate loud degrade — the documented alternative to failing the mount (see comment above)
238
- console.error(
239
- `three world "${id}": failed to load input map "${inputMapPath}" — declared input ` +
240
- 'actions and `game.input.*` (bot/virtual input) will not work until a valid map ' +
241
- 'loads. Ship one at the conventional path or pass `inputMapPath` to ' +
242
- `createR3FAdapter (null to opt out). Cause: ${err instanceof Error ? err.message : String(err)}`,
243
- );
244
- });
245
- }
246
-
247
- /**
248
- * Build the engine runtime for one R3F world from a `ThreeHostContext` — the same
249
- * wiring, at the same layer, as the classic `SetupThreeRootAdapter.mount()`
250
- * performs for the subsystems an R3F world shares with it (debug, input,
251
- * random). The ones it does not build are not on
252
- * {@link R3FGameContext} at all.
253
- */
254
- export function createR3FRootContext(
255
- host: ThreeHostContext,
256
- options: R3FRootContextOptions,
257
- ): R3FRootRuntime {
258
- const { id, inputMapPath = DEFAULT_INPUT_MAP_PATH } = options;
259
-
260
- // Same observer threading as the classic adapter — profiler sees this
261
- // world's phases under the shared game profiler when hosted.
262
- const systems = createSystemRunner(host.game?.profiler.systemObserver, 'three');
263
-
264
- // The mounted world's adapter surface (`mounted.systems`) — declared before
265
- // ctx so `ctx.registerSystemAdapter` can close over it (classic parity: a
266
- // game-owned capability like networking registers here from component code).
267
- const systemAdapters: SystemAdapters = {};
268
-
269
- // The world's live scene index — built in `setSceneCamera` (below), because
270
- // fiber resolves the scene after this function returns. Same lifetime as
271
- // `ctx.scene`, which is backfilled at the same moment.
272
- let sceneIndex: SceneIndex | null = null;
273
-
274
- const ctxRaw: Record<string, unknown> = {
275
- systems,
276
- assets: host.assets,
277
- input: host.game?.input ?? null,
278
- registerSystemAdapter: (kind: keyof SystemAdapters, adapter: SystemAdapters[typeof kind]) => {
279
- // biome-ignore lint/suspicious/noExplicitAny: same per-key record write the classic adapter performs; correct by construction
280
- (systemAdapters as any)[kind] = adapter;
281
- host.game?.notifySystemAdaptersChanged();
282
- },
283
- };
284
- const ctx = ctxRaw as unknown as R3FGameContext;
285
- if (host.game) {
286
- ctx.game = host.game;
287
- ctx.roots = host.game.roots;
288
- ctx.playtest = host.game.playtest ?? null;
289
- }
290
- // --- ctx.debug (gap 1) — the ONE registry per Game root, shared across
291
- // every world mounted onto it; a bare mount with no Game shell gets a
292
- // private, mount-local registry instead (same absence precedent as the
293
- // classic adapter). ---
294
- const debugRegistry: DebugRegistry =
295
- (host.game ? getDebugRegistry(host.game) : null) ??
296
- createDebugRegistry({ getTick: () => 0, getSimT: () => 0 });
297
- systemAdapters.debug = debugRegistry.adapter;
298
- ctx.debug = debugRegistry.forRoot(id);
299
- ctx.random =
300
- (host.game ? getSeededRandom(host.game) : null) ??
301
- createSeededRandom(DEFAULT_SEEDED_RANDOM_SEED);
302
-
303
- // --- ctx.clock (P3) — every world has a sim clock, R3F included. Same
304
- // game-scoped reach-in as `ctx.random` above; a bare mount with no Game
305
- // shell gets a private, mount-local clock that nothing flushes (there is no
306
- // fixed loop to bind timers to). The mount-local fallback uses the SAME
307
- // `disposeDebrisSubtree` the hosted path uses — it drops
308
- // the subtree and disposes geometry/materials, with no Rapier owners
309
- // because this ctx carries no `physics`/`rapierWorld` (the
310
- // honest "this world owns no bodies").
311
- const gameClock: SimClockInternal | null = host.game ? getSimClock(host.game) : null;
312
- const clock: SimClockInternal =
313
- gameClock ?? createSimClock({ dispose: (obj) => disposeDebrisSubtree(obj, []) });
314
- ctx.clock = clock;
315
-
316
- // --- ctx.input + the debug-registry input seams (gap 3) — see
317
- // `wireGameInputSeams` below; `ctx.input` is that same game-owned manager. ---
318
- const inputMapReady = wireGameInputSeams(host, debugRegistry, { id, inputMapPath });
319
-
320
- return {
321
- ctx,
322
- systems,
323
- systemAdapters,
324
- debugRegistry,
325
- inputMapReady,
326
- setSceneCamera(scene, camera): void {
327
- const sameScene = ctxRaw['scene'] === scene && sceneIndex !== null;
328
- ctxRaw['scene'] = scene;
329
- ctxRaw['camera'] = camera;
330
- // The live scene index (P2 `observe`) is a REAL value here, not a
331
- // throwing getter — every three world has one. It can only be built once
332
- // fiber has resolved the scene, which is the same moment `ctx.scene`
333
- // itself becomes readable, so index and scene share one lifetime. A
334
- // second commit (fiber handing back a different scene) replaces it.
335
- //
336
- // Keyed on the SCENE, so a camera-only backfill (a world swapping the
337
- // default camera after mount) leaves the index — and every `observe`
338
- // subscription hanging off it — alone.
339
- if (sameScene) return;
340
- sceneIndex?.dispose();
341
- sceneIndex = createSceneIndex(scene);
342
- ctxRaw['sceneIndex'] = sceneIndex;
343
- },
344
- dispose(): void {
345
- // Same ownership rule as the classic adapter's teardown: dispose ONLY a
346
- // clock this mount created. The game-scoped one belongs to
347
- // `GameInternal.dispose()` — killing it here would freeze sim time and
348
- // cancel every SIBLING world's timers.
349
- if (!gameClock) clock.dispose();
350
- sceneIndex?.dispose();
351
- sceneIndex = null;
352
- // Scoped strip when a Game hosts this world (sibling roots stay live);
353
- // a bare standalone mount owns its private registry and clears it all.
354
- if (host.game) debugRegistry.strip(id);
355
- else debugRegistry.strip();
356
- },
357
- };
358
- }