@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
@@ -13,17 +13,36 @@
13
13
  * copy of its stores.
14
14
  */
15
15
  import type { Game } from '../runtime/game';
16
+ import { type ContractSurface, type ContractSystemEmptySlot } from './ingest/contract-system-adapters';
17
+ import type { SystemAdapters } from './system-adapter';
16
18
  export declare const NATIVE_INPUT_VALUE_TYPES: readonly ["digital", "scalar", "vector2", "pointerDelta", "pointerPosition"];
17
19
  export type NativeInputValueType = (typeof NATIVE_INPUT_VALUE_TYPES)[number];
18
20
  export type NativeInputValue = boolean | number | {
19
21
  readonly x: number;
20
22
  readonly y: number;
21
23
  };
24
+ /** A table may be a THUNK, evaluated once at install (after every project
25
+ * module has run), so a game whose declarations accrete during module
26
+ * evaluation — the dev-tools registry's `stat()`/`cheat()` lines beside each
27
+ * mechanic — hands a complete snapshot without ordering its own imports
28
+ * around the debugger. */
29
+ type NativeTable<T> = Readonly<Record<string, T>> | (() => Readonly<Record<string, T>>);
30
+ /** A command is a plain function, or `{ description, run }` when it wants the
31
+ * session's `game.commands()` listing to say what it does. Argument
32
+ * validation stays inside the function — the game's words at the game's
33
+ * door. */
34
+ export type NativeCommandEntry = ((...args: unknown[]) => unknown) | {
35
+ readonly description?: string;
36
+ readonly run: (...args: unknown[]) => unknown;
37
+ };
22
38
  export interface NativeDebugModule {
23
- readonly commands?: Readonly<Record<string, (...args: unknown[]) => unknown>>;
24
- readonly state?: Readonly<Record<string, () => unknown>>;
39
+ readonly commands?: NativeTable<NativeCommandEntry>;
40
+ readonly state?: NativeTable<() => unknown>;
25
41
  readonly input?: {
26
- readonly actions: Readonly<Record<string, NativeInputValueType>>;
42
+ /** May be a thunk, read LIVE on every use — an app whose action set loads
43
+ * asynchronously (an input map fetched at boot) answers with what it has
44
+ * NOW rather than freezing the empty pre-load set at install. */
45
+ readonly actions: Readonly<Record<string, NativeInputValueType>> | (() => Readonly<Record<string, NativeInputValueType>>);
27
46
  readonly set: (action: string, value: NativeInputValue) => void;
28
47
  readonly clear: () => void;
29
48
  /** Optional native-scheduler operation. The adapter never synthesizes one. */
@@ -31,6 +50,12 @@ export interface NativeDebugModule {
31
50
  /** Optional native-scheduler operation. The adapter never adds a host phase. */
32
51
  readonly scheduleAtTick?: (tick: number, action: string, value: NativeInputValue) => void;
33
52
  };
53
+ /** The app's own EVENT STREAM, bridged into the session's tick-stamped
54
+ * event log at install — the entry-export replacement for the react
55
+ * emit hook. The app owns the emitter; the host only forwards. */
56
+ readonly events?: {
57
+ readonly subscribe: (listener: (event: string, detail?: unknown) => void) => () => void;
58
+ };
34
59
  }
35
60
  export interface NativeDebugBinding {
36
61
  readonly rootId: string;
@@ -40,4 +65,47 @@ export interface NativeDebugBinding {
40
65
  export declare function nativeDebugBindingFromEntryModule(rootId: string, entryModule: unknown): NativeDebugBinding | null;
41
66
  /** Project native registries onto the existing session debugger after mount. */
42
67
  export declare function installNativeDebugBindings(game: Game, bindings: readonly NativeDebugBinding[]): void;
68
+ /** One root's statically declared, already-validated system-adapter slots. */
69
+ export interface NativeSystemsBinding {
70
+ readonly rootId: string;
71
+ readonly slots: Readonly<Partial<SystemAdapters>>;
72
+ /**
73
+ * The slots this root's `systems` table answered with `absent(reason)` — the
74
+ * positive absences, in the game's own words.
75
+ *
76
+ * They are carried rather than dropped because they are the ONLY thing that
77
+ * can tell a coverage reader "this game has no networking" apart from "nobody
78
+ * ever looked". Nothing is installed for them (that would be the stub adapter
79
+ * `absent()`'s own comment forbids); they exist to be READ — through
80
+ * `binding.observation.entrySystems`, which is the product door the coverage
81
+ * table renders from.
82
+ */
83
+ readonly absent: readonly ContractSystemEmptySlot[];
84
+ }
85
+ /**
86
+ * Read the single native `systems` export from an already-loaded root module.
87
+ * `surface` is the root's own mount surface when the caller knows it — it
88
+ * feeds the physics keying check (`PHYSICS_KEYING_BY_SURFACE`).
89
+ */
90
+ export declare function nativeSystemsBindingFromEntryModule(rootId: string, entryModule: unknown, surface?: ContractSurface | undefined): NativeSystemsBinding | null;
91
+ /**
92
+ * Install every declared binding onto the mounted Game's game-scoped slot
93
+ * table (`Game.installDeclaredSystemAdapters`), where the ordinary
94
+ * `game.systemAdapters` merge picks them up ahead of any lingering component
95
+ * registration for the same root.
96
+ *
97
+ * A root's ABSENCES travel the same call and install NOTHING — they are
98
+ * recorded, never bound, because the whole point of `absent()` is that no
99
+ * adapter exists to bind (see its comment: a marker, never a stub). They are
100
+ * recorded HERE rather than left on the binding alone so the answer is
101
+ * game-scoped, matching the registry every editor panel already reads: a
102
+ * `SystemAdapters` slot is filled by whichever root builds it, so "does this
103
+ * GAME have physics" cannot be answered one root at a time.
104
+ *
105
+ * A root that declares only absences therefore still calls through — skipping
106
+ * it on an empty `slots` map is what would drop exactly the games whose whole
107
+ * declaration is "I have none of these".
108
+ */
109
+ export declare function installNativeSystemsBindings(game: Game, bindings: readonly NativeSystemsBinding[]): void;
110
+ export {};
43
111
  //# sourceMappingURL=native-debug-module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"native-debug-module.d.ts","sourceRoot":"","sources":["../../src/adapter/native-debug-module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,eAAO,MAAM,wBAAwB,8EAM3B,CAAC;AACX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7E,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,GAAG;IAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC;IAC9E,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE;QACf,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;QACjE,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;QAChE,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;QAC3B,8EAA8E;QAC9E,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;QACxC,gFAAgF;QAChF,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;KAC3F,CAAC;CACH;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;CACnC;AA0ED,gFAAgF;AAChF,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,OAAO,GACnB,kBAAkB,GAAG,IAAI,CAwB3B;AAkID,gFAAgF;AAChF,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,SAAS,kBAAkB,EAAE,GACtC,IAAI,CAkBN"}
1
+ {"version":3,"file":"native-debug-module.d.ts","sourceRoot":"","sources":["../../src/adapter/native-debug-module.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,7 @@
13
13
  * copy of its stores.
14
14
  */
15
15
  import { DebugError, getDebugRegistry, } from '../runtime/debug-registry';
16
+ import { CONTRACT_SYSTEM_SLOTS, projectContractSystemAdapters, } from './ingest/contract-system-adapters';
16
17
  export const NATIVE_INPUT_VALUE_TYPES = [
17
18
  'digital',
18
19
  'scalar',
@@ -61,6 +62,17 @@ function parseInput(value) {
61
62
  if (!isRecord(value))
62
63
  throw new Error('debug.input must be an object.');
63
64
  rejectUnknownKeys(value, ['actions', 'set', 'clear', 'tap', 'scheduleAtTick'], 'debug.input');
65
+ if (typeof value['actions'] === 'function') {
66
+ // A live table: validated per read (requireAction/the actions source), not
67
+ // here — the whole point is that its content changes after install.
68
+ const set = value['set'];
69
+ const clear = value['clear'];
70
+ if (typeof set !== 'function')
71
+ throw new Error('debug.input.set must be a function.');
72
+ if (typeof clear !== 'function')
73
+ throw new Error('debug.input.clear must be a function.');
74
+ return value;
75
+ }
64
76
  const set = value['set'];
65
77
  const clear = value['clear'];
66
78
  const tap = value['tap'];
@@ -95,11 +107,32 @@ export function nativeDebugBindingFromEntryModule(rootId, entryModule) {
95
107
  throw new Error(`Root "${rootId}" exports \`debug\`, but it is not an object.`);
96
108
  }
97
109
  const raw = entryModule['debug'];
98
- rejectUnknownKeys(raw, ['commands', 'state', 'input'], `Root "${rootId}" debug export`);
99
- const commands = raw['commands'] === undefined ? undefined : functionRecord(raw['commands'], 'debug.commands');
100
- const state = raw['state'] === undefined ? undefined : functionRecord(raw['state'], 'debug.state');
110
+ rejectUnknownKeys(raw, ['commands', 'state', 'input', 'events'], `Root "${rootId}" debug export`);
111
+ // A thunk table defers to install time (see {@link NativeTable}); a plain
112
+ // record validates here, where the error can still name the export.
113
+ const parseTable = (value, at) => {
114
+ if (value === undefined)
115
+ return undefined;
116
+ if (typeof value === 'function')
117
+ return value;
118
+ return functionRecord(value, at);
119
+ };
120
+ // Commands allow the `{ description?, run }` entry shape; full validation
121
+ // happens at install (`resolveCommandTable`), where thunks resolve too.
122
+ const parseCommands = (value, at) => {
123
+ if (value === undefined)
124
+ return undefined;
125
+ if (typeof value === 'function')
126
+ return value;
127
+ if (!isRecord(value))
128
+ throw new Error(`${at} must be an object of named commands.`);
129
+ return value;
130
+ };
131
+ const commands = parseCommands(raw['commands'], 'debug.commands');
132
+ const state = parseTable(raw['state'], 'debug.state');
101
133
  const input = raw['input'] === undefined ? undefined : parseInput(raw['input']);
102
- if (!commands && !state && !input) {
134
+ const events = raw['events'] === undefined ? undefined : parseEvents(raw['events']);
135
+ if (!commands && !state && !input && !events) {
103
136
  throw new Error(`Root "${rootId}" exports an empty \`debug\` object.`);
104
137
  }
105
138
  return {
@@ -108,9 +141,42 @@ export function nativeDebugBindingFromEntryModule(rootId, entryModule) {
108
141
  ...(commands ? { commands } : {}),
109
142
  ...(state ? { state } : {}),
110
143
  ...(input ? { input } : {}),
144
+ ...(events ? { events } : {}),
111
145
  },
112
146
  };
113
147
  }
148
+ function parseEvents(value) {
149
+ if (!isRecord(value) || typeof value['subscribe'] !== 'function') {
150
+ throw new Error('debug.events must be an object with a subscribe(listener) function.');
151
+ }
152
+ return value;
153
+ }
154
+ /** Resolve a possibly-thunk COMMAND table, validating each entry's shape. */
155
+ function resolveCommandTable(table, at) {
156
+ if (table === undefined)
157
+ return {};
158
+ const raw = typeof table === 'function' ? table() : table;
159
+ if (!isRecord(raw))
160
+ throw new Error(`${at} must be an object of named commands.`);
161
+ for (const [name, entry] of Object.entries(raw)) {
162
+ if (!name)
163
+ throw new Error(`${at} contains an empty name.`);
164
+ const ok = typeof entry === 'function' ||
165
+ (isRecord(entry) && typeof entry.run === 'function');
166
+ if (!ok)
167
+ throw new Error(`${at}.${name} must be a function or { description?, run }.`);
168
+ }
169
+ return raw;
170
+ }
171
+ /** Resolve a possibly-thunk table at install time, validating the result. */
172
+ function resolveTable(table, at) {
173
+ if (table === undefined)
174
+ return {};
175
+ if (typeof table === 'function') {
176
+ return functionRecord(table(), at);
177
+ }
178
+ return table;
179
+ }
114
180
  function valueMatches(type, value) {
115
181
  if (type === 'digital')
116
182
  return typeof value === 'boolean';
@@ -126,11 +192,14 @@ function valueMatches(type, value) {
126
192
  function installInput(game, registry, binding, input) {
127
193
  const trace = [];
128
194
  let recording = false;
195
+ // LIVE read on every use — see the `actions` field's own comment.
196
+ const liveActions = () => typeof input.actions === 'function' ? input.actions() : input.actions;
129
197
  const requireAction = (action, value) => {
130
- const type = input.actions[action];
198
+ const actions = liveActions();
199
+ const type = actions[action];
131
200
  if (!type) {
132
201
  throw new DebugError('INPUT_ACTION_NOT_FOUND', `Native input has no action named "${action}".`, {
133
- registered: Object.keys(input.actions),
202
+ registered: Object.keys(actions),
134
203
  });
135
204
  }
136
205
  if (!valueMatches(type, value)) {
@@ -197,7 +266,7 @@ function installInput(game, registry, binding, input) {
197
266
  unsupportedSource('injectPointerPosition');
198
267
  },
199
268
  };
200
- registry.setInputActionsSource(binding.rootId, () => Object.entries(input.actions).map(([name, valueType]) => ({ name, valueType })));
269
+ registry.setInputActionsSource(binding.rootId, () => Object.entries(liveActions()).map(([name, valueType]) => ({ name, valueType })));
201
270
  registry.setInputTraceSource(binding.rootId, () => ({
202
271
  version: 1,
203
272
  seed: null,
@@ -218,13 +287,80 @@ export function installNativeDebugBindings(game, bindings) {
218
287
  throw new Error(`Native debug binding names unmounted root "${binding.rootId}".`);
219
288
  }
220
289
  const root = registry.forRoot(binding.rootId);
221
- for (const [name, read] of Object.entries(binding.debug.state ?? {})) {
290
+ const state = resolveTable(binding.debug.state, `Root "${binding.rootId}" debug.state`);
291
+ for (const [name, read] of Object.entries(state)) {
222
292
  root.registerStateProvider(name, read, { tier: 'assisted' });
223
293
  }
224
- for (const [name, command] of Object.entries(binding.debug.commands ?? {})) {
225
- root.registerCommand(name, { locus: 'client' }, command);
294
+ const commands = resolveCommandTable(binding.debug.commands, `Root "${binding.rootId}" debug.commands`);
295
+ for (const [name, entry] of Object.entries(commands)) {
296
+ const run = typeof entry === 'function' ? entry : entry.run;
297
+ const description = typeof entry === 'function' ? undefined : entry.description;
298
+ root.registerCommand(name, { locus: 'client', ...(description === undefined ? {} : { description }) }, run);
226
299
  }
227
300
  if (binding.debug.input)
228
301
  installInput(game, registry, binding, binding.debug.input);
302
+ // The app's emitter → the session's tick-stamped event log. The
303
+ // subscription's disposer is deliberately dropped: it lives exactly as
304
+ // long as this Game's registry does.
305
+ binding.debug.events?.subscribe((event, detail) => root.emit(event, detail));
306
+ }
307
+ }
308
+ /**
309
+ * Read the single native `systems` export from an already-loaded root module.
310
+ * `surface` is the root's own mount surface when the caller knows it — it
311
+ * feeds the physics keying check (`PHYSICS_KEYING_BY_SURFACE`).
312
+ */
313
+ export function nativeSystemsBindingFromEntryModule(rootId, entryModule, surface) {
314
+ if (!isRecord(entryModule) || entryModule['systems'] === undefined)
315
+ return null;
316
+ const raw = entryModule['systems'];
317
+ if (!isRecord(raw)) {
318
+ throw new Error(`Root "${rootId}" exports \`systems\`, but it is not an object.`);
319
+ }
320
+ rejectUnknownKeys(raw, CONTRACT_SYSTEM_SLOTS, `Root "${rootId}" systems export`);
321
+ const projection = projectContractSystemAdapters({ systemAdapters: raw }, surface);
322
+ if (projection.malformed.length > 0) {
323
+ throw new Error(`Root "${rootId}" systems export is malformed: ` +
324
+ projection.malformed.map((slot) => `${slot.slot} — ${slot.reason}`).join('; '));
325
+ }
326
+ if (Object.keys(projection.bound).length === 0 && projection.empty.length === 0) {
327
+ throw new Error(`Root "${rootId}" exports an empty \`systems\` object.`);
328
+ }
329
+ return { rootId, slots: projection.bound, absent: projection.empty };
330
+ }
331
+ /**
332
+ * Install every declared binding onto the mounted Game's game-scoped slot
333
+ * table (`Game.installDeclaredSystemAdapters`), where the ordinary
334
+ * `game.systemAdapters` merge picks them up ahead of any lingering component
335
+ * registration for the same root.
336
+ *
337
+ * A root's ABSENCES travel the same call and install NOTHING — they are
338
+ * recorded, never bound, because the whole point of `absent()` is that no
339
+ * adapter exists to bind (see its comment: a marker, never a stub). They are
340
+ * recorded HERE rather than left on the binding alone so the answer is
341
+ * game-scoped, matching the registry every editor panel already reads: a
342
+ * `SystemAdapters` slot is filled by whichever root builds it, so "does this
343
+ * GAME have physics" cannot be answered one root at a time.
344
+ *
345
+ * A root that declares only absences therefore still calls through — skipping
346
+ * it on an empty `slots` map is what would drop exactly the games whose whole
347
+ * declaration is "I have none of these".
348
+ */
349
+ export function installNativeSystemsBindings(game, bindings) {
350
+ if (bindings.length === 0)
351
+ return;
352
+ const install = game.installDeclaredSystemAdapters;
353
+ if (!install) {
354
+ throw new Error('Cannot install native systems bindings: this Game does not implement ' +
355
+ 'installDeclaredSystemAdapters.');
356
+ }
357
+ for (const binding of bindings) {
358
+ if (Object.keys(binding.slots).length === 0 && binding.absent.length === 0)
359
+ continue;
360
+ install.call(game, binding.rootId, binding.slots, binding.absent.map((slot) => ({
361
+ rootId: binding.rootId,
362
+ slot: slot.slot,
363
+ reason: slot.evidence,
364
+ })));
229
365
  }
230
366
  }
@@ -25,6 +25,15 @@ import type { PhysicsAdapter } from './system-adapter';
25
25
  * `@react-three/rapier` pins for R3F worlds (0.19). Their `RigidBody` classes
26
26
  * are unrelated NOMINAL types, so a shared helper that named either one could
27
27
  * not serve both callers.
28
+ *
29
+ * FLIP CONDITION: `@react-three/rapier` pins its rapier EXACTLY (`0.19.2` as
30
+ * of its 2.2.0 — check `npm view @react-three/rapier dependencies`), so an
31
+ * override or engine bump to any OTHER rapier version forks the wasm instance
32
+ * that library initializes against and is refused. This structural split
33
+ * collapses only when the engine's own `@dimforge/rapier3d-compat` and
34
+ * `@react-three/rapier`'s exact pin agree on one version — at that point the
35
+ * two `RigidBody` types are one nominal type again and this interface can
36
+ * name it directly.
28
37
  */
29
38
  export interface RapierEditableBody {
30
39
  isFixed(): boolean;
@@ -57,7 +66,28 @@ export interface RapierEditableBody {
57
66
  * answered differently by each Rapier owner, and a caller that cannot answer
58
67
  * one honestly omits it.
59
68
  */
60
- export declare function createRapierBodyEditing(bodyFor: (nodeId: string) => RapierEditableBody | null | undefined): Pick<PhysicsAdapter, 'ownerOf' | 'freeze' | 'commit' | 'unfreeze'>;
69
+ /**
70
+ * What asking this Rapier owner about a node id actually yields.
71
+ *
72
+ * THE TWO NEGATIVE ANSWERS ARE DIFFERENT FACTS, and collapsing them into one
73
+ * `undefined` is what let `ownerOf` answer `'editor'` — "the editor drives this
74
+ * node's transform" — for an id this world has never heard of. `'no-body'` is a
75
+ * real measurement about a real node; `'unresolved'` is this implementer saying
76
+ * it cannot answer at all.
77
+ */
78
+ export type RapierBodyLookup = {
79
+ readonly kind: 'body';
80
+ readonly body: RapierEditableBody;
81
+ }
82
+ /** The node exists here and simply has no physics body. */
83
+ | {
84
+ readonly kind: 'no-body';
85
+ }
86
+ /** This world does not know this node id. */
87
+ | {
88
+ readonly kind: 'unresolved';
89
+ };
90
+ export declare function createRapierBodyEditing(lookup: (nodeId: string) => RapierBodyLookup): Pick<PhysicsAdapter, 'ownerOf' | 'freeze' | 'commit' | 'unfreeze'>;
61
91
  export declare function createRapierPhysicsAdapter(registry: PhysicsRegistry, physics: PhysicsContext | null | undefined,
62
92
  /**
63
93
  * The world's own `id → Object3D` map. Injected rather than derived here so
@@ -1 +1 @@
1
- {"version":3,"file":"rapier-physics-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/rapier-physics-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,IAAI,OAAO,CAAC;IACnB,QAAQ,IAAI,MAAM,CAAC;IACnB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACjD,cAAc,CAAC,WAAW,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACxF,WAAW,CAAC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9F;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,IAAI,GAAG,SAAS,GACjE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC,CAqCpE;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS;AAC1C;;;;;GAKG;AACH,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC,QAAQ,GAAG,IAAI,GACjD,cAAc,CA+DhB"}
1
+ {"version":3,"file":"rapier-physics-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/rapier-physics-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,IAAI,OAAO,CAAC;IACnB,QAAQ,IAAI,MAAM,CAAC;IACnB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACjD,cAAc,CAAC,WAAW,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IACxF,WAAW,CAAC,QAAQ,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CAC9F;AAED;;;;;;;;;;;;;;GAcG;AACH;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAA;CAAE;AAC9D,2DAA2D;GACzD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE;AAC9B,6CAA6C;GAC3C;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;CAAE,CAAC;AAYpC,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,gBAAgB,GAC3C,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC,CAiDpE;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,SAAS;AAC1C;;;;;GAKG;AACH,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,KAAK,CAAC,QAAQ,GAAG,IAAI,GACjD,cAAc,CAwEhB"}
@@ -10,34 +10,39 @@
10
10
  * through the `resolve` function its constructor is handed. Nothing above this
11
11
  * line — not the editor, not the seam — needs to know an `Object3D` exists.
12
12
  */
13
- /**
14
- * The four REQUIRED `PhysicsAdapter` members, over nothing but "which Rapier
15
- * body drives this node" — the `freeze → apply → unfreeze` protocol itself,
16
- * with no opinion about where the body came from.
17
- *
18
- * Split out of {@link createRapierPhysicsAdapter} (it is that function's own
19
- * `ownerOf`/`freeze`/`commit`/`unfreeze`, unchanged) because the FIRST-PARTY
20
- * mount is no longer the only Rapier in this engine: an R3F world does its
21
- * physics with `@react-three/rapier` inside the fiber tree, which owns its own
22
- * bodies and its own `Object3D` mapping, and reaches these same four verbs
23
- * through `world3d-react/rapier-physics-bridge.tsx`. The optional members
24
- * (`debugDraw`, `setDebugDrawEnabled`, `contactPoints`) are NOT here: each is
25
- * answered differently by each Rapier owner, and a caller that cannot answer
26
- * one honestly omits it.
27
- */
28
- export function createRapierBodyEditing(bodyFor) {
13
+ /** The refusal sentence for a protocol verb aimed at a node this world has
14
+ * never heard of named once so every verb says the same thing. */
15
+ function unresolvedNodeRefusal(operation, nodeId) {
16
+ return (`[RapierPhysicsAdapter] ${operation}: this world does not know node id "${nodeId}", so ` +
17
+ 'there is no body to drive. Refusing rather than reporting a completed write — gate on ' +
18
+ "ownerOf(id) !== 'unresolved' before driving physics from a game-scoped adapter.");
19
+ }
20
+ export function createRapierBodyEditing(lookup) {
29
21
  /** Saved body types while frozen, so unfreeze can restore them. */
30
22
  const frozen = new Map();
23
+ /** The body for a verb that must actually reach one, or `null` when this node
24
+ * genuinely has no physics. THROWS for an unknown id: the three verbs below
25
+ * return `void`, so a silent return is indistinguishable from a completed
26
+ * write, and `commit` in particular is a WRITE the caller believes landed. */
27
+ const bodyForWrite = (operation, nodeId) => {
28
+ const answer = lookup(nodeId);
29
+ if (answer.kind === 'unresolved')
30
+ throw new Error(unresolvedNodeRefusal(operation, nodeId));
31
+ return answer.kind === 'body' ? answer.body : null;
32
+ };
31
33
  return {
32
34
  ownerOf(nodeId) {
33
- const body = bodyFor(nodeId);
34
- if (!body)
35
+ const answer = lookup(nodeId);
36
+ if (answer.kind === 'unresolved')
37
+ return 'unresolved';
38
+ // A node with no body, and a FIXED body (which never moves on its own),
39
+ // are both transforms the editor drives.
40
+ if (answer.kind === 'no-body')
35
41
  return 'editor';
36
- // A fixed body never moves on its own → the editor owns its transform.
37
- return body.isFixed() ? 'editor' : 'physics';
42
+ return answer.body.isFixed() ? 'editor' : 'physics';
38
43
  },
39
44
  freeze(nodeId) {
40
- const body = bodyFor(nodeId);
45
+ const body = bodyForWrite('freeze', nodeId);
41
46
  if (!body || frozen.has(body))
42
47
  return;
43
48
  frozen.set(body, body.bodyType());
@@ -45,14 +50,14 @@ export function createRapierBodyEditing(bodyFor) {
45
50
  body.setBodyType(2 /* KinematicPositionBased */, true);
46
51
  },
47
52
  commit(nodeId, t) {
48
- const body = bodyFor(nodeId);
53
+ const body = bodyForWrite('commit', nodeId);
49
54
  if (!body)
50
55
  return;
51
56
  body.setTranslation({ x: t.position[0], y: t.position[1], z: t.position[2] }, true);
52
57
  body.setRotation({ x: t.rotation[0], y: t.rotation[1], z: t.rotation[2], w: t.rotation[3] }, true);
53
58
  },
54
59
  unfreeze(nodeId) {
55
- const body = bodyFor(nodeId);
60
+ const body = bodyForWrite('unfreeze', nodeId);
56
61
  if (!body)
57
62
  return;
58
63
  const prev = frozen.get(body);
@@ -114,22 +119,31 @@ resolve) {
114
119
  }
115
120
  return contactBuf.subarray(0, contactCount * 3);
116
121
  };
117
- /** The one place this adapter crosses from node id into Three's vocabulary. */
118
- const bodyFor = (nodeId) => {
119
- const o = resolve(nodeId);
120
- return o ? registry.get(o)?.body : undefined;
122
+ /** The one place this adapter crosses from node id into Three's vocabulary
123
+ * and the one place that can tell an UNKNOWN node from one with no body. */
124
+ const lookup = (nodeId) => {
125
+ const object = resolve(nodeId);
126
+ if (!object)
127
+ return { kind: 'unresolved' };
128
+ const body = registry.get(object)?.body;
129
+ return body ? { kind: 'body', body } : { kind: 'no-body' };
121
130
  };
122
- return {
123
- ...createRapierBodyEditing(bodyFor),
124
- debugDraw: () => physics?.debugMesh ?? null,
125
- setDebugDrawEnabled(enabled) {
126
- if (!physics)
127
- return;
131
+ const adapter = { ...createRapierBodyEditing(lookup) };
132
+ // The debug-draw and contact capabilities exist only where a physics CONTEXT
133
+ // does. They used to be attached unconditionally and then no-op internally:
134
+ // `setDebugDrawEnabled(true)` returned as if it had enabled something, and
135
+ // `contactPoints()` returned an empty view — "we looked and there are no
136
+ // contacts" — for a world with no simulation at all. Presence mirrors the
137
+ // capability, so `physics-debug.ts`'s existing `?.()` calls now see absence.
138
+ if (physics) {
139
+ adapter.debugDraw = () => physics.debugMesh;
140
+ adapter.setDebugDrawEnabled = (enabled) => {
128
141
  // Same two fields the in-game KeyP toggle flips — the adapter's render
129
142
  // system reads `debugEnabled` and feeds `debugRender()` into the mesh.
130
143
  physics.debugEnabled = enabled;
131
144
  physics.debugMesh.visible = enabled;
132
- },
133
- contactPoints: collectContactPoints,
134
- };
145
+ };
146
+ adapter.contactPoints = collectContactPoints;
147
+ }
148
+ return adapter;
135
149
  }
@@ -4,7 +4,7 @@
4
4
  * It lives in the ADAPTER SEAM, not in a surface entry, because `WorldRendererConfig` IS part of
5
5
  * the root contract: `MountedThreeRoot.rendererConfig` reports it (`./root-adapter.ts`) and
6
6
  * `./root-seam-contract.ts` already lists it as a contract field. The seam defines the shape; the
7
- * surface entries and the editor CONFORM to it — `world3d-react/r3f-adapter.tsx` is the declarer,
7
+ * surface entries and the editor CONFORM to it — `world3d-react/r3f-root-factory.tsx` is the declarer,
8
8
  * `runtime/create-runtime.ts` the host, `editor/src/components/ViewportPanel.tsx` the editor's
9
9
  * applier. Do NOT move it back under `world3d-react/`: nothing here touches react, and a core
10
10
  * adapter file reaching into a react entry is exactly what `react-core-import-ban.test.ts` forbids
@@ -17,7 +17,7 @@
17
17
  * engine's pipeline — an imported Godot 3 GLES2 game does gamma-space lighting with no tonemapper
18
18
  * at all, so ACES quietly desaturates and darkens every colour its author picked.
19
19
  *
20
- * So a world may DECLARE the pipeline it was authored for, and `createR3FAdapter` applies it to the
20
+ * So a mount may DECLARE the pipeline its world was authored for, and the mounting adapter applies it to the
21
21
  * host's renderer for the life of the mount, restoring what it found on dispose. Three properties
22
22
  * of that shape are load-bearing:
23
23
  *
@@ -77,7 +77,7 @@ export interface WorldRendererConfig {
77
77
  * Apply `config` to `renderer`, returning the restore function that puts back what was there.
78
78
  *
79
79
  * `three` is passed in rather than imported for values so the enum constants come from the HOST's
80
- * three instance — the same identity rule `r3f-adapter.tsx` follows for the scene and camera.
80
+ * three instance — the same identity rule `r3f-root-factory.tsx` follows for the scene and camera.
81
81
  */
82
82
  export declare function applyWorldRendererConfig(three: typeof THREE, renderer: THREE.WebGLRenderer, config: WorldRendererConfig): () => void;
83
83
  //# sourceMappingURL=renderer-config.d.ts.map
@@ -4,7 +4,7 @@
4
4
  * It lives in the ADAPTER SEAM, not in a surface entry, because `WorldRendererConfig` IS part of
5
5
  * the root contract: `MountedThreeRoot.rendererConfig` reports it (`./root-adapter.ts`) and
6
6
  * `./root-seam-contract.ts` already lists it as a contract field. The seam defines the shape; the
7
- * surface entries and the editor CONFORM to it — `world3d-react/r3f-adapter.tsx` is the declarer,
7
+ * surface entries and the editor CONFORM to it — `world3d-react/r3f-root-factory.tsx` is the declarer,
8
8
  * `runtime/create-runtime.ts` the host, `editor/src/components/ViewportPanel.tsx` the editor's
9
9
  * applier. Do NOT move it back under `world3d-react/`: nothing here touches react, and a core
10
10
  * adapter file reaching into a react entry is exactly what `react-core-import-ban.test.ts` forbids
@@ -17,7 +17,7 @@
17
17
  * engine's pipeline — an imported Godot 3 GLES2 game does gamma-space lighting with no tonemapper
18
18
  * at all, so ACES quietly desaturates and darkens every colour its author picked.
19
19
  *
20
- * So a world may DECLARE the pipeline it was authored for, and `createR3FAdapter` applies it to the
20
+ * So a mount may DECLARE the pipeline its world was authored for, and the mounting adapter applies it to the
21
21
  * host's renderer for the life of the mount, restoring what it found on dispose. Three properties
22
22
  * of that shape are load-bearing:
23
23
  *
@@ -39,7 +39,7 @@
39
39
  * Apply `config` to `renderer`, returning the restore function that puts back what was there.
40
40
  *
41
41
  * `three` is passed in rather than imported for values so the enum constants come from the HOST's
42
- * three instance — the same identity rule `r3f-adapter.tsx` follows for the scene and camera.
42
+ * three instance — the same identity rule `r3f-root-factory.tsx` follows for the scene and camera.
43
43
  */
44
44
  export function applyWorldRendererConfig(three, renderer, config) {
45
45
  // A host that mounts a world WITHOUT rasterizing it hands the adapter a duck-typed renderer —
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * RootAdapter — the host ⇄ runtime contract. A ROOT is *anything that mounts*.
3
3
  *
4
- * This is the interface the host depends on. `GameSetupFn` is private to one
5
- * implementer (`SetupThreeRootAdapter`); first-party content and unmodified
6
- * external games are peer root adapters. A game is the manifest's complete
7
- * roots array, while each adapter owns exactly one root.
4
+ * This is the interface the host depends on. First-party content and
5
+ * unmodified external games are peer root adapters. A game is the
6
+ * manifest's complete roots array, while each adapter owns exactly one
7
+ * root.
8
8
  */
9
9
  import type { Container } from 'pixi.js';
10
10
  import type * as THREE from 'three';
@@ -125,8 +125,8 @@ export interface MountedReactRoot extends MountedRootBase {
125
125
  }
126
126
  /** Every kind of live, mounted world. */
127
127
  export type MountedRoot = MountedThreeRoot | MountedPixiRoot | MountedReactRoot;
128
- /** Map a {@link AdapterSurface} to its mounted-world shape (mirrors `NodeOf`/
129
- * `BodyOf`/`ColliderOf` in `ecs/behavior.ts`) — lets generic code over
128
+ /** Map a {@link AdapterSurface} to its mounted-world shape lets generic
129
+ * code over
130
130
  * `K extends AdapterSurface` name the right surface without a manual union. */
131
131
  export type MountedRootFor<K extends AdapterSurface> = K extends 'three' ? MountedThreeRoot : K extends 'canvas' ? MountedPixiRoot : K extends 'dom' ? MountedReactRoot : never;
132
132
  /**
@@ -155,7 +155,7 @@ export interface RootAdapter<K extends AdapterSurface = 'three'> {
155
155
  mount(host: HostContextFor<K>): Promise<MountedRootFor<K>>;
156
156
  }
157
157
  /**
158
- * One surface tag paired with an adapter typed FOR that surface (WO-34).
158
+ * One surface tag paired with an adapter typed FOR that surface.
159
159
  *
160
160
  * A resolver that turns a manifest root into a mountable
161
161
  * adapter handles every surface in one function, so its return type has to
@@ -1,9 +1,9 @@
1
1
  /**
2
2
  * RootAdapter — the host ⇄ runtime contract. A ROOT is *anything that mounts*.
3
3
  *
4
- * This is the interface the host depends on. `GameSetupFn` is private to one
5
- * implementer (`SetupThreeRootAdapter`); first-party content and unmodified
6
- * external games are peer root adapters. A game is the manifest's complete
7
- * roots array, while each adapter owns exactly one root.
4
+ * This is the interface the host depends on. First-party content and
5
+ * unmodified external games are peer root adapters. A game is the
6
+ * manifest's complete roots array, while each adapter owns exactly one
7
+ * root.
8
8
  */
9
9
  export {};