@vgai/engine 0.5.5 → 0.5.7

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 (446) hide show
  1. package/dist/adapter/adapter-surface.d.ts +30 -0
  2. package/dist/adapter/adapter-surface.d.ts.map +1 -0
  3. package/dist/adapter/adapter-surface.js +18 -0
  4. package/dist/adapter/authoring.d.ts +597 -0
  5. package/dist/adapter/authoring.d.ts.map +1 -0
  6. package/dist/adapter/authoring.js +22 -0
  7. package/dist/adapter/colyseus-networking-adapter.d.ts +77 -0
  8. package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -0
  9. package/dist/adapter/colyseus-networking-adapter.js +76 -0
  10. package/dist/adapter/first-party-systems.d.ts +23 -0
  11. package/dist/adapter/first-party-systems.d.ts.map +1 -0
  12. package/dist/adapter/first-party-systems.js +196 -0
  13. package/dist/adapter/host-context.d.ts +194 -0
  14. package/dist/adapter/host-context.d.ts.map +1 -0
  15. package/dist/adapter/host-context.js +80 -0
  16. package/dist/adapter/index.d.ts +33 -0
  17. package/dist/adapter/index.d.ts.map +1 -0
  18. package/dist/adapter/index.js +25 -0
  19. package/dist/adapter/ingest/game-contract.d.ts +53 -0
  20. package/dist/adapter/ingest/game-contract.d.ts.map +1 -0
  21. package/dist/adapter/ingest/game-contract.js +30 -0
  22. package/dist/adapter/ingest/scene-capture.d.ts +121 -0
  23. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -0
  24. package/dist/adapter/ingest/scene-capture.js +270 -0
  25. package/dist/adapter/ingest/structural-ids.d.ts +64 -0
  26. package/dist/adapter/ingest/structural-ids.d.ts.map +1 -0
  27. package/dist/adapter/ingest/structural-ids.js +110 -0
  28. package/dist/adapter/ingest/upstream-pin.d.ts +44 -0
  29. package/dist/adapter/ingest/upstream-pin.d.ts.map +1 -0
  30. package/dist/adapter/ingest/upstream-pin.js +47 -0
  31. package/dist/adapter/loop-gate-report.d.ts +34 -0
  32. package/dist/adapter/loop-gate-report.d.ts.map +1 -0
  33. package/dist/adapter/loop-gate-report.js +37 -0
  34. package/dist/adapter/rapier-physics-adapter.d.ts +69 -0
  35. package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -0
  36. package/dist/adapter/rapier-physics-adapter.js +135 -0
  37. package/dist/adapter/root-adapter.d.ts +195 -0
  38. package/dist/adapter/root-adapter.d.ts.map +1 -0
  39. package/dist/adapter/root-adapter.js +25 -0
  40. package/dist/adapter/setup-three-root-adapter.d.ts +87 -0
  41. package/dist/adapter/setup-three-root-adapter.d.ts.map +1 -0
  42. package/dist/adapter/setup-three-root-adapter.js +791 -0
  43. package/dist/adapter/system-adapter.d.ts +453 -0
  44. package/dist/adapter/system-adapter.d.ts.map +1 -0
  45. package/dist/adapter/system-adapter.js +13 -0
  46. package/dist/adapter/transform.d.ts +18 -0
  47. package/dist/adapter/transform.d.ts.map +1 -0
  48. package/dist/adapter/transform.js +1 -0
  49. package/dist/ai/navigation.d.ts +78 -0
  50. package/dist/ai/navigation.d.ts.map +1 -0
  51. package/dist/ai/navigation.js +152 -0
  52. package/dist/animation/anim-graph-types.d.ts +25 -0
  53. package/dist/animation/anim-graph-types.d.ts.map +1 -0
  54. package/dist/animation/anim-graph-types.js +1 -0
  55. package/dist/animation/animation-clock.d.ts +213 -0
  56. package/dist/animation/animation-clock.d.ts.map +1 -0
  57. package/dist/animation/animation-clock.js +301 -0
  58. package/dist/animation/blend-node.d.ts +22 -0
  59. package/dist/animation/blend-node.d.ts.map +1 -0
  60. package/dist/animation/blend-node.js +97 -0
  61. package/dist/animation/camera-ownership.d.ts +249 -0
  62. package/dist/animation/camera-ownership.d.ts.map +1 -0
  63. package/dist/animation/camera-ownership.js +169 -0
  64. package/dist/animation/cinematic-cues.d.ts +200 -0
  65. package/dist/animation/cinematic-cues.d.ts.map +1 -0
  66. package/dist/animation/cinematic-cues.js +213 -0
  67. package/dist/animation/clip-map.d.ts +12 -0
  68. package/dist/animation/clip-map.d.ts.map +1 -0
  69. package/dist/animation/clip-map.js +31 -0
  70. package/dist/animation/cubic-spline-interpolant.d.ts +54 -0
  71. package/dist/animation/cubic-spline-interpolant.d.ts.map +1 -0
  72. package/dist/animation/cubic-spline-interpolant.js +111 -0
  73. package/dist/animation/theatre-clock-binding.d.ts +89 -0
  74. package/dist/animation/theatre-clock-binding.d.ts.map +1 -0
  75. package/dist/animation/theatre-clock-binding.js +29 -0
  76. package/dist/animation/theatre-director.d.ts +277 -0
  77. package/dist/animation/theatre-director.d.ts.map +1 -0
  78. package/dist/animation/theatre-director.js +248 -0
  79. package/dist/animation/theatre-object-binding.d.ts +305 -0
  80. package/dist/animation/theatre-object-binding.d.ts.map +1 -0
  81. package/dist/animation/theatre-object-binding.js +351 -0
  82. package/dist/animation/xstate-animation-binding.d.ts +119 -0
  83. package/dist/animation/xstate-animation-binding.d.ts.map +1 -0
  84. package/dist/animation/xstate-animation-binding.js +349 -0
  85. package/dist/animation/xstate-animation-meta.d.ts +266 -0
  86. package/dist/animation/xstate-animation-meta.d.ts.map +1 -0
  87. package/dist/animation/xstate-animation-meta.js +252 -0
  88. package/dist/asset-formats/camera.d.ts +15 -0
  89. package/dist/asset-formats/camera.d.ts.map +1 -0
  90. package/dist/asset-formats/camera.js +15 -0
  91. package/dist/asset-formats/collider.d.ts +21 -0
  92. package/dist/asset-formats/collider.d.ts.map +1 -0
  93. package/dist/asset-formats/collider.js +53 -0
  94. package/dist/asset-formats/index.d.ts +46 -0
  95. package/dist/asset-formats/index.d.ts.map +1 -0
  96. package/dist/asset-formats/index.js +36 -0
  97. package/dist/asset-formats/instances.d.ts +17 -0
  98. package/dist/asset-formats/instances.d.ts.map +1 -0
  99. package/dist/asset-formats/instances.js +31 -0
  100. package/dist/asset-formats/light.d.ts +21 -0
  101. package/dist/asset-formats/light.d.ts.map +1 -0
  102. package/dist/asset-formats/light.js +35 -0
  103. package/dist/asset-formats/material.d.ts +129 -0
  104. package/dist/asset-formats/material.d.ts.map +1 -0
  105. package/dist/asset-formats/material.js +88 -0
  106. package/dist/asset-formats/mesh.d.ts +22 -0
  107. package/dist/asset-formats/mesh.d.ts.map +1 -0
  108. package/dist/asset-formats/mesh.js +64 -0
  109. package/dist/asset-formats/parse.d.ts +20 -0
  110. package/dist/asset-formats/parse.d.ts.map +1 -0
  111. package/dist/asset-formats/parse.js +34 -0
  112. package/dist/asset-formats/particles.d.ts +1390 -0
  113. package/dist/asset-formats/particles.d.ts.map +1 -0
  114. package/dist/asset-formats/particles.js +319 -0
  115. package/dist/asset-formats/render-env.d.ts +387 -0
  116. package/dist/asset-formats/render-env.d.ts.map +1 -0
  117. package/dist/asset-formats/render-env.js +300 -0
  118. package/dist/asset-formats/tuples.d.ts +12 -0
  119. package/dist/asset-formats/tuples.d.ts.map +1 -0
  120. package/dist/asset-formats/tuples.js +14 -0
  121. package/dist/asset-loaders.d.ts +97 -0
  122. package/dist/asset-loaders.d.ts.map +1 -0
  123. package/dist/asset-loaders.js +209 -0
  124. package/dist/asset-parse-error.d.ts +24 -0
  125. package/dist/asset-parse-error.d.ts.map +1 -0
  126. package/dist/asset-parse-error.js +29 -0
  127. package/dist/asset-registry.d.ts +38 -0
  128. package/dist/asset-registry.d.ts.map +1 -0
  129. package/dist/asset-registry.js +66 -0
  130. package/dist/assets.d.ts +26 -0
  131. package/dist/assets.d.ts.map +1 -0
  132. package/dist/assets.js +55 -0
  133. package/dist/audio/wav-encode.d.ts +38 -0
  134. package/dist/audio/wav-encode.d.ts.map +1 -0
  135. package/dist/audio/wav-encode.js +97 -0
  136. package/dist/canvas-react/engine-bridge.d.ts +45 -0
  137. package/dist/canvas-react/engine-bridge.d.ts.map +1 -0
  138. package/dist/canvas-react/engine-bridge.js +45 -0
  139. package/dist/canvas-react/index.d.ts +42 -0
  140. package/dist/canvas-react/index.d.ts.map +1 -0
  141. package/dist/canvas-react/index.js +41 -0
  142. package/dist/canvas-react/pixi-primitive.d.ts +85 -0
  143. package/dist/canvas-react/pixi-primitive.d.ts.map +1 -0
  144. package/dist/canvas-react/pixi-primitive.js +181 -0
  145. package/dist/canvas-react/pixi-react-adapter.d.ts +71 -0
  146. package/dist/canvas-react/pixi-react-adapter.d.ts.map +1 -0
  147. package/dist/canvas-react/pixi-react-adapter.js +229 -0
  148. package/dist/canvas-react/pixi-react-root-factory.d.ts +56 -0
  149. package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -0
  150. package/dist/canvas-react/pixi-react-root-factory.js +73 -0
  151. package/dist/canvas-react/world-context.d.ts +88 -0
  152. package/dist/canvas-react/world-context.d.ts.map +1 -0
  153. package/dist/canvas-react/world-context.js +212 -0
  154. package/dist/config.d.ts +9 -0
  155. package/dist/config.d.ts.map +1 -0
  156. package/dist/config.js +8 -0
  157. package/dist/core/frame-pacing.d.ts +90 -0
  158. package/dist/core/frame-pacing.d.ts.map +1 -0
  159. package/dist/core/frame-pacing.js +89 -0
  160. package/dist/core/game-loop.d.ts +62 -0
  161. package/dist/core/game-loop.d.ts.map +1 -0
  162. package/dist/core/game-loop.js +189 -0
  163. package/dist/core/seeded-random.d.ts +69 -0
  164. package/dist/core/seeded-random.d.ts.map +1 -0
  165. package/dist/core/seeded-random.js +121 -0
  166. package/dist/core/sim-clock.d.ts +199 -0
  167. package/dist/core/sim-clock.d.ts.map +1 -0
  168. package/dist/core/sim-clock.js +291 -0
  169. package/dist/core/system-runner.d.ts +96 -0
  170. package/dist/core/system-runner.d.ts.map +1 -0
  171. package/dist/core/system-runner.js +253 -0
  172. package/dist/core/types.d.ts +111 -0
  173. package/dist/core/types.d.ts.map +1 -0
  174. package/dist/core/types.js +25 -0
  175. package/dist/data/data-asset.d.ts +101 -0
  176. package/dist/data/data-asset.d.ts.map +1 -0
  177. package/dist/data/data-asset.js +122 -0
  178. package/dist/data/data-check-core.d.ts +109 -0
  179. package/dist/data/data-check-core.d.ts.map +1 -0
  180. package/dist/data/data-check-core.js +177 -0
  181. package/dist/data/data-ref.d.ts +79 -0
  182. package/dist/data/data-ref.d.ts.map +1 -0
  183. package/dist/data/data-ref.js +130 -0
  184. package/dist/data/vite-plugin-data.d.ts +79 -0
  185. package/dist/data/vite-plugin-data.d.ts.map +1 -0
  186. package/dist/data/vite-plugin-data.js +217 -0
  187. package/dist/defaults.d.ts +233 -0
  188. package/dist/defaults.d.ts.map +1 -0
  189. package/dist/defaults.js +137 -0
  190. package/dist/dev/chrome-trace.d.ts +45 -0
  191. package/dist/dev/chrome-trace.d.ts.map +1 -0
  192. package/dist/dev/chrome-trace.js +114 -0
  193. package/dist/dev/console-bridge.d.ts +22 -0
  194. package/dist/dev/console-bridge.d.ts.map +1 -0
  195. package/dist/dev/console-bridge.js +71 -0
  196. package/dist/dev/debug-draw.d.ts +24 -0
  197. package/dist/dev/debug-draw.d.ts.map +1 -0
  198. package/dist/dev/debug-draw.js +73 -0
  199. package/dist/dev/instruments.d.ts +133 -0
  200. package/dist/dev/instruments.d.ts.map +1 -0
  201. package/dist/dev/instruments.js +231 -0
  202. package/dist/dev/logger.d.ts +30 -0
  203. package/dist/dev/logger.d.ts.map +1 -0
  204. package/dist/dev/logger.js +90 -0
  205. package/dist/dev/performance-profiler.d.ts +111 -0
  206. package/dist/dev/performance-profiler.d.ts.map +1 -0
  207. package/dist/dev/performance-profiler.js +215 -0
  208. package/dist/dev/render-debug-adapter.d.ts +57 -0
  209. package/dist/dev/render-debug-adapter.d.ts.map +1 -0
  210. package/dist/dev/render-debug-adapter.js +141 -0
  211. package/dist/dev/render-memory.d.ts +53 -0
  212. package/dist/dev/render-memory.d.ts.map +1 -0
  213. package/dist/dev/render-memory.js +190 -0
  214. package/dist/dev/webgl-frame-capture.d.ts +134 -0
  215. package/dist/dev/webgl-frame-capture.d.ts.map +1 -0
  216. package/dist/dev/webgl-frame-capture.js +270 -0
  217. package/dist/dev/webgl-gpu-timer.d.ts +8 -0
  218. package/dist/dev/webgl-gpu-timer.d.ts.map +1 -0
  219. package/dist/dev/webgl-gpu-timer.js +52 -0
  220. package/dist/ecs/scene-index.d.ts +179 -0
  221. package/dist/ecs/scene-index.d.ts.map +1 -0
  222. package/dist/ecs/scene-index.js +331 -0
  223. package/dist/ecs/scene-query.d.ts +6 -0
  224. package/dist/ecs/scene-query.d.ts.map +1 -0
  225. package/dist/ecs/scene-query.js +40 -0
  226. package/dist/ecs/user-data.d.ts +193 -0
  227. package/dist/ecs/user-data.d.ts.map +1 -0
  228. package/dist/ecs/user-data.js +145 -0
  229. package/dist/index.d.ts +35 -0
  230. package/dist/index.d.ts.map +1 -0
  231. package/dist/index.js +29 -0
  232. package/dist/input/input-manager.d.ts +944 -0
  233. package/dist/input/input-manager.d.ts.map +1 -0
  234. package/dist/input/input-manager.js +2209 -0
  235. package/dist/input/input-types.d.ts +174 -0
  236. package/dist/input/input-types.d.ts.map +1 -0
  237. package/dist/input/input-types.js +1 -0
  238. package/dist/input/prompt-labels.d.ts +21 -0
  239. package/dist/input/prompt-labels.d.ts.map +1 -0
  240. package/dist/input/prompt-labels.js +121 -0
  241. package/dist/input/rebind-controller.d.ts +59 -0
  242. package/dist/input/rebind-controller.d.ts.map +1 -0
  243. package/dist/input/rebind-controller.js +85 -0
  244. package/dist/input/schema.d.ts +16 -0
  245. package/dist/input/schema.d.ts.map +1 -0
  246. package/dist/input/schema.js +228 -0
  247. package/dist/loader.d.ts +76 -0
  248. package/dist/loader.d.ts.map +1 -0
  249. package/dist/loader.js +114 -0
  250. package/dist/manifest/editor-port.d.ts +53 -0
  251. package/dist/manifest/editor-port.d.ts.map +1 -0
  252. package/dist/manifest/editor-port.js +67 -0
  253. package/dist/manifest/filename.d.ts +14 -0
  254. package/dist/manifest/filename.d.ts.map +1 -0
  255. package/dist/manifest/filename.js +43 -0
  256. package/dist/manifest/index.d.ts +6 -0
  257. package/dist/manifest/index.d.ts.map +1 -0
  258. package/dist/manifest/index.js +9 -0
  259. package/dist/manifest/load-file.d.ts +7 -0
  260. package/dist/manifest/load-file.d.ts.map +1 -0
  261. package/dist/manifest/load-file.js +23 -0
  262. package/dist/manifest/load.d.ts +161 -0
  263. package/dist/manifest/load.d.ts.map +1 -0
  264. package/dist/manifest/load.js +269 -0
  265. package/dist/manifest/locate.d.ts +24 -0
  266. package/dist/manifest/locate.d.ts.map +1 -0
  267. package/dist/manifest/locate.js +48 -0
  268. package/dist/manifest/schema.d.ts +293 -0
  269. package/dist/manifest/schema.d.ts.map +1 -0
  270. package/dist/manifest/schema.js +633 -0
  271. package/dist/physics/collider-dimensions.d.ts +63 -0
  272. package/dist/physics/collider-dimensions.d.ts.map +1 -0
  273. package/dist/physics/collider-dimensions.js +98 -0
  274. package/dist/physics/collision-system.d.ts +41 -0
  275. package/dist/physics/collision-system.d.ts.map +1 -0
  276. package/dist/physics/collision-system.js +64 -0
  277. package/dist/physics/physics-registry.d.ts +45 -0
  278. package/dist/physics/physics-registry.d.ts.map +1 -0
  279. package/dist/physics/physics-registry.js +65 -0
  280. package/dist/physics/transform-writer.d.ts +16 -0
  281. package/dist/physics/transform-writer.d.ts.map +1 -0
  282. package/dist/physics/transform-writer.js +38 -0
  283. package/dist/pixi/authoring.d.ts +93 -0
  284. package/dist/pixi/authoring.d.ts.map +1 -0
  285. package/dist/pixi/authoring.js +207 -0
  286. package/dist/pixi/index.d.ts +16 -0
  287. package/dist/pixi/index.d.ts.map +1 -0
  288. package/dist/pixi/index.js +15 -0
  289. package/dist/pixi/ingest-iframe.d.ts +82 -0
  290. package/dist/pixi/ingest-iframe.d.ts.map +1 -0
  291. package/dist/pixi/ingest-iframe.js +166 -0
  292. package/dist/pixi/ingest.d.ts +97 -0
  293. package/dist/pixi/ingest.d.ts.map +1 -0
  294. package/dist/pixi/ingest.js +40 -0
  295. package/dist/pixi/physics-registry.d.ts +24 -0
  296. package/dist/pixi/physics-registry.d.ts.map +1 -0
  297. package/dist/pixi/physics-registry.js +38 -0
  298. package/dist/pixi/scene-capture.d.ts +43 -0
  299. package/dist/pixi/scene-capture.d.ts.map +1 -0
  300. package/dist/pixi/scene-capture.js +92 -0
  301. package/dist/pixi/system-adapters.d.ts +16 -0
  302. package/dist/pixi/system-adapters.d.ts.map +1 -0
  303. package/dist/pixi/system-adapters.js +37 -0
  304. package/dist/react/unmanaged-root-detector.d.ts +18 -0
  305. package/dist/react/unmanaged-root-detector.d.ts.map +1 -0
  306. package/dist/react/unmanaged-root-detector.js +70 -0
  307. package/dist/react/use-data.d.ts +60 -0
  308. package/dist/react/use-data.d.ts.map +1 -0
  309. package/dist/react/use-data.js +101 -0
  310. package/dist/react/use-selection.d.ts +54 -0
  311. package/dist/react/use-selection.d.ts.map +1 -0
  312. package/dist/react/use-selection.js +71 -0
  313. package/dist/react/world-state.d.ts +154 -0
  314. package/dist/react/world-state.d.ts.map +1 -0
  315. package/dist/react/world-state.js +220 -0
  316. package/dist/render/auto-batcher.d.ts +34 -0
  317. package/dist/render/auto-batcher.d.ts.map +1 -0
  318. package/dist/render/auto-batcher.js +140 -0
  319. package/dist/render/instance-mesh.d.ts +5 -0
  320. package/dist/render/instance-mesh.d.ts.map +1 -0
  321. package/dist/render/instance-mesh.js +19 -0
  322. package/dist/render/light-camera-factory.d.ts +24 -0
  323. package/dist/render/light-camera-factory.d.ts.map +1 -0
  324. package/dist/render/light-camera-factory.js +71 -0
  325. package/dist/render/lod.d.ts +13 -0
  326. package/dist/render/lod.d.ts.map +1 -0
  327. package/dist/render/lod.js +16 -0
  328. package/dist/render/material-factory.d.ts +15 -0
  329. package/dist/render/material-factory.d.ts.map +1 -0
  330. package/dist/render/material-factory.js +186 -0
  331. package/dist/render/mesh-shadow.d.ts +11 -0
  332. package/dist/render/mesh-shadow.d.ts.map +1 -0
  333. package/dist/render/mesh-shadow.js +19 -0
  334. package/dist/render/particles-factory.d.ts +99 -0
  335. package/dist/render/particles-factory.d.ts.map +1 -0
  336. package/dist/render/particles-factory.js +361 -0
  337. package/dist/render/render-batch-system.d.ts +33 -0
  338. package/dist/render/render-batch-system.d.ts.map +1 -0
  339. package/dist/render/render-batch-system.js +223 -0
  340. package/dist/render/render-features.d.ts +47 -0
  341. package/dist/render/render-features.d.ts.map +1 -0
  342. package/dist/render/render-features.js +105 -0
  343. package/dist/render/render-settings.d.ts +35 -0
  344. package/dist/render/render-settings.d.ts.map +1 -0
  345. package/dist/render/render-settings.js +51 -0
  346. package/dist/render/spark-renderer-lifecycle.d.ts +18 -0
  347. package/dist/render/spark-renderer-lifecycle.d.ts.map +1 -0
  348. package/dist/render/spark-renderer-lifecycle.js +57 -0
  349. package/dist/render/viewport-shading.d.ts +17 -0
  350. package/dist/render/viewport-shading.d.ts.map +1 -0
  351. package/dist/render/viewport-shading.js +87 -0
  352. package/dist/runtime/create-runtime.d.ts +259 -0
  353. package/dist/runtime/create-runtime.d.ts.map +1 -0
  354. package/dist/runtime/create-runtime.js +666 -0
  355. package/dist/runtime/debug-bridge.d.ts +288 -0
  356. package/dist/runtime/debug-bridge.d.ts.map +1 -0
  357. package/dist/runtime/debug-bridge.js +277 -0
  358. package/dist/runtime/debug-registry.d.ts +287 -0
  359. package/dist/runtime/debug-registry.d.ts.map +1 -0
  360. package/dist/runtime/debug-registry.js +528 -0
  361. package/dist/runtime/dev-layers.d.ts +34 -0
  362. package/dist/runtime/dev-layers.d.ts.map +1 -0
  363. package/dist/runtime/dev-layers.js +40 -0
  364. package/dist/runtime/frame-selector-cache.d.ts +41 -0
  365. package/dist/runtime/frame-selector-cache.d.ts.map +1 -0
  366. package/dist/runtime/frame-selector-cache.js +65 -0
  367. package/dist/runtime/game.d.ts +576 -0
  368. package/dist/runtime/game.d.ts.map +1 -0
  369. package/dist/runtime/game.js +1003 -0
  370. package/dist/runtime/gameplay-rng-trap.d.ts +47 -0
  371. package/dist/runtime/gameplay-rng-trap.d.ts.map +1 -0
  372. package/dist/runtime/gameplay-rng-trap.js +95 -0
  373. package/dist/runtime/input-router.d.ts +78 -0
  374. package/dist/runtime/input-router.d.ts.map +1 -0
  375. package/dist/runtime/input-router.js +171 -0
  376. package/dist/runtime/mount-game.d.ts +126 -0
  377. package/dist/runtime/mount-game.d.ts.map +1 -0
  378. package/dist/runtime/mount-game.js +159 -0
  379. package/dist/runtime/mount-manifest.d.ts +174 -0
  380. package/dist/runtime/mount-manifest.d.ts.map +1 -0
  381. package/dist/runtime/mount-manifest.js +304 -0
  382. package/dist/runtime/presentation.d.ts +85 -0
  383. package/dist/runtime/presentation.d.ts.map +1 -0
  384. package/dist/runtime/presentation.js +92 -0
  385. package/dist/runtime/render-audio-control.d.ts +126 -0
  386. package/dist/runtime/render-audio-control.d.ts.map +1 -0
  387. package/dist/runtime/render-audio-control.js +116 -0
  388. package/dist/runtime/render-control.d.ts +347 -0
  389. package/dist/runtime/render-control.d.ts.map +1 -0
  390. package/dist/runtime/render-control.js +317 -0
  391. package/dist/runtime/render-seed.d.ts +56 -0
  392. package/dist/runtime/render-seed.d.ts.map +1 -0
  393. package/dist/runtime/render-seed.js +73 -0
  394. package/dist/runtime/state-bridge.d.ts +60 -0
  395. package/dist/runtime/state-bridge.d.ts.map +1 -0
  396. package/dist/runtime/state-bridge.js +62 -0
  397. package/dist/runtime/types.d.ts +285 -0
  398. package/dist/runtime/types.d.ts.map +1 -0
  399. package/dist/runtime/types.js +1 -0
  400. package/dist/runtime/unexported-game-trap.d.ts +36 -0
  401. package/dist/runtime/unexported-game-trap.d.ts.map +1 -0
  402. package/dist/runtime/unexported-game-trap.js +80 -0
  403. package/dist/setup/setup-audio.d.ts +20 -0
  404. package/dist/setup/setup-audio.d.ts.map +1 -0
  405. package/dist/setup/setup-audio.js +41 -0
  406. package/dist/setup/setup-particles.d.ts +17 -0
  407. package/dist/setup/setup-particles.d.ts.map +1 -0
  408. package/dist/setup/setup-particles.js +16 -0
  409. package/dist/setup/setup-physics.d.ts +25 -0
  410. package/dist/setup/setup-physics.d.ts.map +1 -0
  411. package/dist/setup/setup-physics.js +51 -0
  412. package/dist/setup/setup-renderer.d.ts +82 -0
  413. package/dist/setup/setup-renderer.d.ts.map +1 -0
  414. package/dist/setup/setup-renderer.js +381 -0
  415. package/dist/world3d-react/engine-bridge.d.ts +47 -0
  416. package/dist/world3d-react/engine-bridge.d.ts.map +1 -0
  417. package/dist/world3d-react/engine-bridge.js +48 -0
  418. package/dist/world3d-react/index.d.ts +47 -0
  419. package/dist/world3d-react/index.d.ts.map +1 -0
  420. package/dist/world3d-react/index.js +46 -0
  421. package/dist/world3d-react/r3f-adapter.d.ts +51 -0
  422. package/dist/world3d-react/r3f-adapter.d.ts.map +1 -0
  423. package/dist/world3d-react/r3f-adapter.js +368 -0
  424. package/dist/world3d-react/r3f-root-factory.d.ts +62 -0
  425. package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -0
  426. package/dist/world3d-react/r3f-root-factory.js +79 -0
  427. package/dist/world3d-react/rapier-physics-bridge.d.ts +76 -0
  428. package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -0
  429. package/dist/world3d-react/rapier-physics-bridge.js +120 -0
  430. package/dist/world3d-react/renderer-config.d.ts +58 -0
  431. package/dist/world3d-react/renderer-config.d.ts.map +1 -0
  432. package/dist/world3d-react/renderer-config.js +96 -0
  433. package/dist/world3d-react/world-context.d.ts +123 -0
  434. package/dist/world3d-react/world-context.d.ts.map +1 -0
  435. package/dist/world3d-react/world-context.js +204 -0
  436. package/dist-config/config.js +303 -0
  437. package/dist-config/config.js.map +7 -0
  438. package/package.json +14 -3
  439. package/src/adapter/rapier-physics-adapter.ts +77 -34
  440. package/src/config.ts +8 -0
  441. package/src/core/frame-pacing.ts +26 -0
  442. package/src/core/game-loop.ts +4 -3
  443. package/src/dev/instruments.ts +302 -0
  444. package/src/setup/setup-renderer.ts +2 -0
  445. package/src/world3d-react/index.ts +9 -0
  446. package/src/world3d-react/rapier-physics-bridge.tsx +137 -0
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Render-control runtime seam (I2). This is the harness a deterministic-capture host
3
+ * (Playwright/CDP, I0/I3/I4) drives: exact-time seeks against the canonical
4
+ * `AnimationClock` (`animation/animation-clock.ts`), and single `preRender`+`render`
5
+ * phase passes across every world on a `Game` (`runtime/game.ts`) — WITHOUT ever
6
+ * advancing gameplay fixed-step substeps
7
+ * (`input`/`prePhysics`/`physics`/`postPhysics`/`gameLogic`/`animation` are never
8
+ * invoked by this seam; `--simulate` (I5) is a different, later, path that
9
+ * deliberately drives `game.runFrame(fixedDt)` instead).
10
+ *
11
+ * This module does NOT reuse `PlayState.step()` (`Game.play.step()`) — that
12
+ * surface only advances the currently-FROZEN (paused) set of roots and is a
13
+ * whole-call no-op while unpaused (see its doc comment in `runtime/game.ts`);
14
+ * the spec review explicitly flagged it as the wrong seam. `renderOnce`
15
+ * instead walks `game.roots` directly and calls each world's own
16
+ * `RootFrameHooks.runPhase` for exactly two phases.
17
+ *
18
+ * Also does NOT install anything by default — a caller must explicitly call
19
+ * {@link installRenderControlHarness}, and even then it only actually
20
+ * publishes `window.__vgaiRender` when the page's URL opts in via
21
+ * `?vgai-render=1` (AC 4: unavailable/protected in normal production
22
+ * gameplay unless explicitly enabled). See `render-seed.ts` for the
23
+ * matching `Math.random` determinism hardening, which must run BEFORE this
24
+ * module (or any other game module) is even imported.
25
+ *
26
+ * I5 (`--simulate` render, §15 I5) is implemented HERE, alongside `renderOnce`:
27
+ * {@link VgaiRenderHarness.simulateSubsteps} drives `game.runFrame(fixedDt)` a
28
+ * fixed integer number of times — the FULL fixed-step gameplay frame (every
29
+ * phase in `PHASE_ORDER`, including `physics`/`gameLogic`/`animation`), unlike
30
+ * `renderOnce`'s deliberately gameplay-free `preRender`+`render`-only pass.
31
+ * A caller that never invokes `simulateSubsteps` (the default, sequence-
32
+ * controlled render — I4) gets byte-identical behavior to before this method
33
+ * existed: `renderOnce`/`seek`/`seekFrame`/`advanceFrame` are untouched by
34
+ * this addition.
35
+ */
36
+ import type { GameInternal } from './game';
37
+ /** Per-subsystem readiness verdict (I2 AC: "Runtime reports asset, shader,
38
+ * font, Theatre, Tone, and scene readiness"). `not-applicable` is the
39
+ * correct answer for any subsystem genuinely absent from a given fixture
40
+ * (spec note: "If a subsystem isn't present in the fixture, report it as
41
+ * not-applicable rather than failing") — never coerced to `ready`. */
42
+ export type ReadinessStatus = 'ready' | 'not-applicable' | 'error';
43
+ export interface ReadinessEntry {
44
+ readonly status: ReadinessStatus;
45
+ readonly detail?: string;
46
+ }
47
+ /** The full readiness report `ready()` resolves. One entry per subsystem
48
+ * named in the I2 AC, always present (never a sparse/optional object) so a
49
+ * caller can read every key unconditionally. */
50
+ export interface ReadinessReport {
51
+ readonly assets: ReadinessEntry;
52
+ readonly shaders: ReadinessEntry;
53
+ readonly fonts: ReadinessEntry;
54
+ readonly theatre: ReadinessEntry;
55
+ readonly tone: ReadinessEntry;
56
+ readonly scene: ReadinessEntry;
57
+ }
58
+ /**
59
+ * Caller-supplied per-subsystem readiness checks. Each hook is optional:
60
+ * absence means that subsystem genuinely does not exist in this fixture/game
61
+ * (reported `not-applicable`), NOT that it's assumed ready. A hook that
62
+ * resolves is `ready`; one that throws/rejects is `error` with the message
63
+ * captured (never silently swallowed, never escalated to fail the whole
64
+ * `ready()` call — one subsystem's failure must not hide the others' status).
65
+ * `fonts` has no hook slot: it is always checked directly against
66
+ * `document.fonts.ready` (universally meaningful in a browser render page),
67
+ * matching the AC's explicit `document.fonts.ready` callout.
68
+ */
69
+ export interface RenderReadinessHooks {
70
+ assets?(): Promise<void> | void;
71
+ shaders?(): Promise<void> | void;
72
+ theatre?(): Promise<void> | void;
73
+ tone?(): Promise<void> | void;
74
+ scene?(): Promise<void> | void;
75
+ }
76
+ /** The minimal clock surface this seam drives — `AnimationClock`'s `seek`/
77
+ * `seekFrame` (`animation/animation-clock.ts`). Typed narrowly (not
78
+ * imported as a value) so this module never depends on the clock's full
79
+ * surface, just the two methods the render seam actually calls. */
80
+ export interface SeekableClock {
81
+ seek(t: number): unknown;
82
+ seekFrame(n: number, fps: number): unknown;
83
+ }
84
+ /**
85
+ * I8 render-integration seam: a per-frame, fixed-`dt` advance hook — exactly
86
+ * the engine's `SystemFn` shape (`core/types.ts`) — for a
87
+ * cinematic-participating system whose live-gameplay driver `renderOnce()`
88
+ * deliberately bypasses. The motivating case is `bindXStateAnimation`'s
89
+ * `tick` (`animation/xstate-animation-binding.ts`, E2): in live play it
90
+ * rides the fixed-step `'animation'` phase
91
+ * (`ctx.systems.add(SystemPhase.ANIMATION, binding.tick)`), but
92
+ * `renderOnce()` (I2) runs ONLY `preRender`+`render` — the `'animation'`
93
+ * phase, along with every other gameplay phase, is never touched by design
94
+ * (see this module's top doc comment). Without a distinct advance hook, an
95
+ * `AnimationMixer` driven this way would sit frozen for the entire render:
96
+ * nothing would ever call `mixer.update(dt)`. `advanceFrame` (below) is that
97
+ * hook — a small, explicit registry of `FrameAdvancer`s a render-mode page
98
+ * opts into via {@link RenderControlHarnessOptions.frameAdvancers}, invoked
99
+ * in registration order.
100
+ */
101
+ export type FrameAdvancer = (dt: number) => void;
102
+ /** The `window.__vgaiRender` surface (I2 AC, extended by I8's `advanceFrame`).
103
+ * Every method here is synchronous except the two that genuinely need to
104
+ * wait on the browser (composited render completion, async readiness
105
+ * probes). */
106
+ export interface VgaiRenderHarness {
107
+ /** Evaluate Theatre/registered-GSAP/cinematic-participating evaluators at
108
+ * exact time `t` (seconds) via `clock.seek(t)` — never advances gameplay
109
+ * substeps. Synchronous: the clock's subscribed evaluators run inline,
110
+ * before this call returns. */
111
+ seek(t: number): void;
112
+ /** Exact-frame variant — `clock.seekFrame(n, fps)`, i.e. `start + n/fps`,
113
+ * bit-exact and drift-free across repeated calls. Same no-substep
114
+ * semantics as {@link seek}. */
115
+ seekFrame(n: number, fps: number): void;
116
+ /**
117
+ * I8 render-integration seam: advance every registered
118
+ * {@link FrameAdvancer} (e.g. an XState/`AnimationMixer` binding's `tick`)
119
+ * by exactly `dt` seconds, in registration order. A render-mode capture
120
+ * loop calls this ONCE per OUTPUT frame, with `dt = 1 / fps`, BEFORE
121
+ * `seekFrame`/`renderOnce` for that frame — see
122
+ * `packages/vgai-sdk/src/render/render-cinematic.ts`'s `captureFrames`, which
123
+ * drives exactly this sequence. `advanceFrame` is a genuinely
124
+ * FORWARD-ONLY, INCREMENTAL tick (it has no notion of "time" to seek
125
+ * to — it only knows "advance by `dt` more") — unlike {@link seek}/
126
+ * {@link seekFrame}, which are absolute and idempotent, calling
127
+ * `advanceFrame` twice never undoes or re-derives a prior call, it always
128
+ * accumulates. This composes correctly with I4's default sequence-
129
+ * controlled render (and I8's reference cinematic) specifically BECAUSE
130
+ * that capture loop only ever walks output frames monotonically forward
131
+ * (`n = 0, 1, 2, ...`); it is NOT safe to call from a scrubber/editor
132
+ * preview that seeks backward or re-visits a frame — a caller that needs
133
+ * that (a future `--simulate`/preview seam) must not reuse this hook as-is.
134
+ * A no-op when no advancers are registered (the default), so this method
135
+ * is always present and always safe to call even for a fixture with
136
+ * nothing to advance (e.g. the I0 `render-cinematic` fixture).
137
+ */
138
+ advanceFrame(dt: number): void;
139
+ /** Runs exactly one `preRender`+`render` phase pass across every
140
+ * host-driven world on the `Game` (z-order is a DOM/canvas-stacking
141
+ * concern already handled by `create-runtime.ts`'s surface stack — this
142
+ * call does not need to reorder anything to composite correctly), then
143
+ * resolves after a double-`requestAnimationFrame` composition fence. */
144
+ renderOnce(): Promise<void>;
145
+ /** Resolves once every declared subsystem has reported in (see
146
+ * {@link ReadinessReport}). */
147
+ ready(): Promise<ReadinessReport>;
148
+ /**
149
+ * I5 `--simulate` seam: advance the FULL fixed-step gameplay frame
150
+ * (`game.runFrame(fixedDt)` — every phase in `PHASE_ORDER`: input,
151
+ * prePhysics, physics, postPhysics, gameLogic, animation, preRender,
152
+ * render) exactly `steps` times, synchronously, in a tight loop. This is
153
+ * what makes physics/gameplay/AI/particles/participating-audio evolve
154
+ * BETWEEN captured output frames under `--simulate` — every real gameplay
155
+ * phase runs, unlike {@link renderOnce} (which runs ONLY `preRender`+
156
+ * `render`, by design, for the I4 sequence-controlled default) or
157
+ * {@link advanceFrame} (which ticks only registered {@link FrameAdvancer}s,
158
+ * never a gameplay phase).
159
+ *
160
+ * `fixedDt` is fixed for the lifetime of this harness (see
161
+ * {@link RenderControlHarnessOptions.simulateFixedDt}, default `1/60` —
162
+ * the same fixed timestep every real host's `createGameLoop` config
163
+ * uses) — callers choose HOW MANY substeps to run per captured frame
164
+ * (`steps`), not the substep's own `dt`, matching the I5 AC's "a fixed
165
+ * integer number of times per output frame" wording exactly.
166
+ *
167
+ * A capture loop that wants BOTH a warmup period (I5 AC: "simulation
168
+ * state can warm up before the capture range") and per-frame substeps
169
+ * calls this twice: once with the warmup step count before frame 0, then
170
+ * once per captured frame with the per-frame step count — there is no
171
+ * separate "warmup" method, because warmup is just simulateSubsteps called
172
+ * before the capture loop starts (see `render-cinematic.ts`'s
173
+ * `captureFrames`, which drives exactly this sequence for `--simulate`
174
+ * requests).
175
+ */
176
+ simulateSubsteps(steps: number): void;
177
+ /**
178
+ * I5 AC 5 ("the render report names every subsystem excluded from
179
+ * deterministic participation"): every subsystem NOT participating in
180
+ * deterministic capture, computed fresh on every call from two sources —
181
+ * (1) `RenderControlHarnessOptions.excludedFromDeterminism`, a static,
182
+ * page-declared list (e.g. "live Colyseus networking", "non-seeded ambient
183
+ * audio") this fixture/game names about itself, and (2) any world this
184
+ * seam itself could not frame-gate (a `drivesOwnLoop` world, or a
185
+ * host-driven world with no `RootFrameHooks` — the SAME two conditions
186
+ * {@link renderOnce}'s `renderableRoots` skips, described here as policy
187
+ * rather than merely warned about once to the console). Declared entries
188
+ * come first, in declaration order; world-exclusion entries follow, in
189
+ * `game.roots` declaration order.
190
+ */
191
+ excludedFromDeterminism(): string[];
192
+ /**
193
+ * I7 fold-in (I5 nit): the ACTUAL fixed substep `dt` (seconds) this
194
+ * harness passes to every `game.runFrame(fixedDt)` call inside
195
+ * {@link simulateSubsteps} — i.e. `RenderControlHarnessOptions.simulateFixedDt`,
196
+ * resolved with its own `1/60` default already applied. Exists so the
197
+ * render pipeline (`render-cinematic.ts`'s `RenderManifestSimulate
198
+ * .fixedTimestep`) can report the REAL value a fixture is using instead of
199
+ * assuming every fixture matches the harness's own default — a fixture
200
+ * that configures a different `simulateFixedDt` (a different Rapier/
201
+ * physics substep) now shows up correctly in the render manifest rather
202
+ * than a silently wrong hardcoded `1/60`.
203
+ */
204
+ simulateFixedDt(): number;
205
+ /**
206
+ * W3d (F11 perf regression gates) — the `vgai perf` sampling seam. Runs
207
+ * exactly `steps` FULL fixed-step gameplay frames (the same
208
+ * `game.runFrame(fixedDt)` path {@link simulateSubsteps} drives — every
209
+ * phase in `PHASE_ORDER`, including `render`, so `renderer.info`-backed
210
+ * draw/triangle counters populate per frame) with the game's OWN
211
+ * `PerformanceProfiler` (`dev/performance-profiler.ts`) enabled, sampling
212
+ * it after every frame, then restores the profiler's prior enabled state.
213
+ * Returns per-frame CPU/phase timings + render counters, plus a
214
+ * structural node/entity count of every three/canvas world's live scene
215
+ * graph. NOT a parallel instrumentation layer: every number here comes
216
+ * from the existing profiler (timings, render counters) or the live world
217
+ * roots themselves (counts).
218
+ *
219
+ * Honesty notes, recorded here because this seam is what `vgai perf`
220
+ * reports: `gpuMs` is whatever the profiler's render reporter measured —
221
+ * `null` under headless SwiftShader (no usable GPU timer), never a
222
+ * fabricated 0; CPU timings include the profiler's own (small) phase
223
+ * bookkeeping, inherent to profiling; warmup belongs OUTSIDE this call
224
+ * (drive {@link simulateSubsteps} first — profiler disabled, zero
225
+ * overhead).
226
+ */
227
+ perfSample(steps: number): PerfSampleReport;
228
+ }
229
+ /** One measured fixed-step frame from {@link VgaiRenderHarness.perfSample}. */
230
+ export interface PerfFrameSample {
231
+ /** CPU time (ms) for the whole `runFrame` pass (profiler `cpuMs`). */
232
+ readonly cpuMs: number;
233
+ /** Per-phase CPU ms (profiler phase timings), keyed by phase name. */
234
+ readonly phases: Readonly<Record<string, number>>;
235
+ /** Renderer counters reported during this frame's `render` phase
236
+ * (`profiler.reportRender` — renderer.info). All zeros/null for a world
237
+ * whose adapter never reports render stats. */
238
+ readonly render: {
239
+ readonly gpuMs: number | null;
240
+ readonly drawCalls: number;
241
+ readonly triangles: number;
242
+ readonly geometries: number;
243
+ readonly textures: number;
244
+ };
245
+ }
246
+ /** Per-world structural counts from {@link VgaiRenderHarness.perfSample}. */
247
+ export interface PerfRootCount {
248
+ readonly id: string;
249
+ readonly kind: string;
250
+ /** Total scene-graph descendants of the world root (exclusive of the root
251
+ * itself). Exact and deterministic under a seed. */
252
+ readonly nodes: number;
253
+ /** Descendants carrying `userData.entityId` (the loader/editor entity
254
+ * tag). 0 for a hand-built world that tags nothing — an honest count of
255
+ * tagged nodes, not a guess at "entities". */
256
+ readonly entities: number;
257
+ }
258
+ export interface PerfSampleReport {
259
+ readonly frames: readonly PerfFrameSample[];
260
+ /** three/canvas worlds only — a react/DOM world has no scene-graph node
261
+ * count; it is omitted rather than fabricated. */
262
+ readonly worlds: readonly PerfRootCount[];
263
+ }
264
+ export interface RenderControlHarnessOptions {
265
+ /**
266
+ * `GameInternal` (not just the game-facing `Game`) — this harness is
267
+ * itself a host (the render/capture driver), and I5's
268
+ * {@link VgaiRenderHarness.simulateSubsteps} needs the host-only
269
+ * `runFrame` surface (`runtime/game.ts`'s `GameInternal.runFrame` doc
270
+ * comment: "the host loop ... and `GameSession.step()` are the only
271
+ * callers"). Every real call site already holds a `GameInternal` (the
272
+ * direct return of `createGame(...)`), so this is not a new construction
273
+ * requirement, just a narrower declared type than before I5.
274
+ */
275
+ readonly game: GameInternal;
276
+ readonly clock: SeekableClock;
277
+ readonly readiness?: RenderReadinessHooks;
278
+ /** Where to publish the harness. Defaults to the real `window` — override
279
+ * in a unit test to avoid touching the global object. */
280
+ readonly target?: Record<string, unknown>;
281
+ /** Where to read `?vgai-render=1` from. Defaults to `window.location`. */
282
+ readonly location?: {
283
+ readonly search: string;
284
+ };
285
+ /** I8 render-integration seam — see {@link VgaiRenderHarness.advanceFrame}.
286
+ * Zero or more `FrameAdvancer`s invoked, in array order, on every
287
+ * `harness.advanceFrame(dt)` call. Default `[]` (no-op), which is exactly
288
+ * what a fixture with nothing gameplay-phase-driven (e.g. I0's fixture)
289
+ * needs — `advanceFrame` is still present on the harness, it just has
290
+ * nothing to do. */
291
+ readonly frameAdvancers?: readonly FrameAdvancer[];
292
+ /**
293
+ * I5 `--simulate` seam: the fixed substep `dt` (seconds)
294
+ * {@link VgaiRenderHarness.simulateSubsteps} passes to every
295
+ * `game.runFrame(fixedDt)` call. Default `1/60` — the same fixed timestep
296
+ * every real host's `createGameLoop` config uses (`create-runtime.ts`), so
297
+ * a `--simulate` fixture's Rapier world (whose own `integrationParameters.dt`
298
+ * defaults to `1/60`) stays in lockstep with the gameplay substep unless a
299
+ * fixture deliberately configures both to a different, still-matching,
300
+ * value.
301
+ */
302
+ readonly simulateFixedDt?: number;
303
+ /**
304
+ * I5 AC 5 — see {@link VgaiRenderHarness.excludedFromDeterminism}. A
305
+ * static, page-declared list of subsystems this fixture/game has that do
306
+ * NOT participate in deterministic capture (e.g. live networking,
307
+ * non-seeded ambient audio) — named here as an explicit, reviewable
308
+ * policy rather than silently dropped. Default `[]` (nothing to declare —
309
+ * correct for a fixture with no such subsystem).
310
+ */
311
+ readonly excludedFromDeterminism?: readonly string[];
312
+ }
313
+ /** Shared by {@link installRenderControlHarness} and any caller that wants
314
+ * to check the opt-in flag before doing render-mode-only setup work (e.g.
315
+ * `render-seed.ts`'s own gate uses the same query param name). */
316
+ export declare function isRenderModeRequested(location: {
317
+ readonly search: string;
318
+ }): boolean;
319
+ /**
320
+ * Build (and, when render mode is actually requested, publish) the
321
+ * `window.__vgaiRender` harness. AC 4 — production protection — lives HERE,
322
+ * not just at the call site: even if a host application calls this
323
+ * unconditionally on every boot, the harness is only ever constructed *and*
324
+ * attached to `target` when `isRenderModeRequested(location)` is true;
325
+ * otherwise this is a no-op that returns `undefined`. A normal production
326
+ * gameplay page therefore never gets `window.__vgaiRender` no matter how
327
+ * this function is wired into its entry point.
328
+ */
329
+ export declare function installRenderControlHarness(opts: RenderControlHarnessOptions): VgaiRenderHarness | undefined;
330
+ /**
331
+ * Defensive assertion for the I2 AC "Pixi roots are pinned to the WebGL
332
+ * renderer (not WebGPU) in render mode". In THIS engine that invariant
333
+ * already holds unconditionally — no in-repo mount constructs a Pixi
334
+ * `Application` with a WebGPU preference, and `setup/setup-renderer.ts` never
335
+ * constructs a `WebGPURenderer` for three roots either — so there is no
336
+ * live WebGPU code path for a first-party render-mode page to disable.
337
+ * This helper exists for a render-mode installer that mounts a CUSTOM/
338
+ * foreign pixi adapter (`create-runtime.ts`'s `{ module }` pixi path) this
339
+ * engine did not construct itself, to assert the invariant against the
340
+ * live mounted renderer rather than merely trusting it. Duck-typed against
341
+ * pixi.js v8's numeric `RendererType` (`WEBGL = 1`, `WEBGPU = 2`,
342
+ * `CANVAS = 4`) so this file never value-imports `pixi.js` (the same
343
+ * type-only-pixi rule `runtime/game.ts`/`create-runtime.ts` document for
344
+ * themselves).
345
+ */
346
+ export declare function assertPixiRootIsWebGL(rendererType: unknown, worldId: string): void;
347
+ //# sourceMappingURL=render-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-control.d.ts","sourceRoot":"","sources":["../../src/runtime/render-control.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Render-control runtime seam (I2). This is the harness a deterministic-capture host
3
+ * (Playwright/CDP, I0/I3/I4) drives: exact-time seeks against the canonical
4
+ * `AnimationClock` (`animation/animation-clock.ts`), and single `preRender`+`render`
5
+ * phase passes across every world on a `Game` (`runtime/game.ts`) — WITHOUT ever
6
+ * advancing gameplay fixed-step substeps
7
+ * (`input`/`prePhysics`/`physics`/`postPhysics`/`gameLogic`/`animation` are never
8
+ * invoked by this seam; `--simulate` (I5) is a different, later, path that
9
+ * deliberately drives `game.runFrame(fixedDt)` instead).
10
+ *
11
+ * This module does NOT reuse `PlayState.step()` (`Game.play.step()`) — that
12
+ * surface only advances the currently-FROZEN (paused) set of roots and is a
13
+ * whole-call no-op while unpaused (see its doc comment in `runtime/game.ts`);
14
+ * the spec review explicitly flagged it as the wrong seam. `renderOnce`
15
+ * instead walks `game.roots` directly and calls each world's own
16
+ * `RootFrameHooks.runPhase` for exactly two phases.
17
+ *
18
+ * Also does NOT install anything by default — a caller must explicitly call
19
+ * {@link installRenderControlHarness}, and even then it only actually
20
+ * publishes `window.__vgaiRender` when the page's URL opts in via
21
+ * `?vgai-render=1` (AC 4: unavailable/protected in normal production
22
+ * gameplay unless explicitly enabled). See `render-seed.ts` for the
23
+ * matching `Math.random` determinism hardening, which must run BEFORE this
24
+ * module (or any other game module) is even imported.
25
+ *
26
+ * I5 (`--simulate` render, §15 I5) is implemented HERE, alongside `renderOnce`:
27
+ * {@link VgaiRenderHarness.simulateSubsteps} drives `game.runFrame(fixedDt)` a
28
+ * fixed integer number of times — the FULL fixed-step gameplay frame (every
29
+ * phase in `PHASE_ORDER`, including `physics`/`gameLogic`/`animation`), unlike
30
+ * `renderOnce`'s deliberately gameplay-free `preRender`+`render`-only pass.
31
+ * A caller that never invokes `simulateSubsteps` (the default, sequence-
32
+ * controlled render — I4) gets byte-identical behavior to before this method
33
+ * existed: `renderOnce`/`seek`/`seekFrame`/`advanceFrame` are untouched by
34
+ * this addition.
35
+ */
36
+ import { SystemPhase } from '../core/types';
37
+ import { RENDER_MODE_QUERY_PARAM } from './render-seed';
38
+ /** Shared by {@link installRenderControlHarness} and any caller that wants
39
+ * to check the opt-in flag before doing render-mode-only setup work (e.g.
40
+ * `render-seed.ts`'s own gate uses the same query param name). */
41
+ export function isRenderModeRequested(location) {
42
+ return new URLSearchParams(location.search).get(RENDER_MODE_QUERY_PARAM) === '1';
43
+ }
44
+ const RENDER_ONLY_PHASES = [SystemPhase.PRE_RENDER, SystemPhase.RENDER];
45
+ /**
46
+ * Run exactly one `preRender`+`render` phase pass across every host-driven
47
+ * (`!drivesOwnLoop`) world on `game`, then await the GPU/DOM composition
48
+ * fence. This is the ENTIRE frame this call drives — no `input`,
49
+ * `prePhysics`, `physics`, `postPhysics`, `gameLogic`, or `animation` phase
50
+ * is ever touched, which is what makes this safe to call from a paused-or-
51
+ * stopped clock without silently ticking gameplay. Phases are run as global
52
+ * barriers across roots (mirroring `GameInternal.runFrame`'s own phase-then-
53
+ * world nesting in `runtime/game.ts`): every world's `preRender` runs before
54
+ * any world's `render`, in `game.roots` declaration order.
55
+ *
56
+ * Ordering within a phase mirrors `GameInternal.runFrame` EXACTLY (see its
57
+ * doc comment in `runtime/game.ts`): the game-scoped `SystemRunner`
58
+ * (`game.systems`) runs FIRST, before any world's frame hooks. `game.systems`
59
+ * is empty for every game today (nothing registers against it yet), so this
60
+ * is a no-op in practice — but a later slice that registers a Game-scoped
61
+ * `preRender`/`render` system (e.g. a cinematic camera crossfade that must
62
+ * paint once per captured frame) would otherwise be silently MISSED by
63
+ * capture while running fine in live playback, since live playback goes
64
+ * through `runFrame`. Running it here keeps the seam a faithful subset of
65
+ * `runFrame`'s phase pass, not a divergent one.
66
+ *
67
+ * A `drivesOwnLoop` world (self-driven, raw-rAF) has no phase hooks this
68
+ * seam can drive — it is skipped with a loud, once-per-call warning rather
69
+ * than silently omitted, mirroring this file's "fail loudly, never a silent
70
+ * no-op" convention. A host-driven world with no `frame` (an opaque/foreign
71
+ * mount) is skipped the same way — there is no phase hook to call. Both
72
+ * checks happen ONCE, up front (see {@link renderableRoots}), not per
73
+ * phase — a world unrenderable in `preRender` is unrenderable in `render`
74
+ * too, so warning twice would just be noise.
75
+ */
76
+ async function renderOnce(game) {
77
+ const roots = renderableRoots(game);
78
+ for (const phase of RENDER_ONLY_PHASES) {
79
+ // Game-scoped systems first, then per-world frame hooks — the SAME order
80
+ // GameInternal.runFrame uses (runtime/game.ts). See doc comment above.
81
+ game.systems.runPhase(phase, 0);
82
+ for (const world of roots) {
83
+ world.frame.runPhase(phase, 0);
84
+ }
85
+ }
86
+ await compositionFence();
87
+ }
88
+ /** Filter `game.roots` down to the ones {@link renderOnce} can actually
89
+ * drive (host-driven, with real `RootFrameHooks`) — see that function's
90
+ * doc comment for why an excluded world is warned about loudly, not
91
+ * silently dropped. */
92
+ function renderableRoots(game) {
93
+ const result = [];
94
+ for (const world of game.roots) {
95
+ if (world.mounted.drivesOwnLoop) {
96
+ warnUnrenderable(world.id, 'drives its own loop — renderOnce() cannot frame-gate it');
97
+ continue;
98
+ }
99
+ if (!world.frame) {
100
+ warnUnrenderable(world.id, `(kind: ${world.kind}) has no phase hooks — renderOnce() cannot drive it (opaque/foreign ` +
101
+ 'mount with no frame-gated entry point)');
102
+ continue;
103
+ }
104
+ result.push({ id: world.id, frame: world.frame });
105
+ }
106
+ return result;
107
+ }
108
+ function warnUnrenderable(worldLabel, reason) {
109
+ // biome-ignore lint/suspicious/noConsole: structured, greppable — mirrors runtime/game.ts's own direct console.warn idiom for "cannot gate this world" capability shortfalls.
110
+ console.warn(`[render-control] world "${worldLabel}" ${reason}.`);
111
+ }
112
+ /**
113
+ * I5 AC 5 policy text for the two conditions {@link renderableRoots} skips —
114
+ * shared so {@link VgaiRenderHarness.excludedFromDeterminism} names the SAME
115
+ * roots `renderOnce()` warns about, in the SAME words, rather than
116
+ * maintaining a second, driftable description of the same two checks.
117
+ */
118
+ function computeRootExclusionReasons(game) {
119
+ const reasons = [];
120
+ for (const world of game.roots) {
121
+ if (world.mounted.drivesOwnLoop) {
122
+ reasons.push(`world "${world.id}" drives its own loop — cannot be frame-gated for deterministic ` +
123
+ 'capture (the policy is to FAIL LOUDLY, or be excluded only by an explicit declared ' +
124
+ 'policy).');
125
+ continue;
126
+ }
127
+ if (!world.frame) {
128
+ reasons.push(`world "${world.id}" (kind: ${world.kind}) has no phase hooks — an opaque/foreign mount ` +
129
+ 'with no frame-gated entry point cannot participate in deterministic capture.');
130
+ }
131
+ }
132
+ return reasons;
133
+ }
134
+ /** Structural scene-graph walk shared by three (`Object3D`) and canvas
135
+ * (`Container`) roots — both expose a `children` array, and three nodes
136
+ * additionally carry `userData` (where the loader/editor entity tag lives).
137
+ * Deliberately duck-typed so this file keeps its type-only three/pixi rule. */
138
+ function countRootGraph(root) {
139
+ let nodes = 0;
140
+ let entities = 0;
141
+ const stack = [...(root.children ?? [])];
142
+ while (stack.length > 0) {
143
+ const node = stack.pop();
144
+ nodes++;
145
+ if (node.userData?.['entityId'] !== undefined)
146
+ entities++;
147
+ if (node.children)
148
+ stack.push(...node.children);
149
+ }
150
+ return { nodes, entities };
151
+ }
152
+ /** See {@link PerfSampleReport.worlds} — three/canvas worlds only; a
153
+ * react/DOM world has no scene-graph node count and is omitted, never
154
+ * fabricated. */
155
+ function countRoots(game) {
156
+ const counts = [];
157
+ for (const world of game.roots) {
158
+ if (world.kind === 'three') {
159
+ counts.push({ id: world.id, kind: world.kind, ...countRootGraph(world.threeScene()) });
160
+ }
161
+ else if (world.kind === 'canvas') {
162
+ counts.push({ id: world.id, kind: world.kind, ...countRootGraph(world.pixiStage()) });
163
+ }
164
+ }
165
+ return counts;
166
+ }
167
+ function nextAnimationFrame() {
168
+ return new Promise((resolve) => {
169
+ requestAnimationFrame(() => resolve());
170
+ });
171
+ }
172
+ /**
173
+ * GPU/DOM composition fence (I2 AC: "waits for GPU/DOM composition
174
+ * (double-rAF fence ... ) rather than an arbitrary sleep"). Two nested
175
+ * `requestAnimationFrame` callbacks: the first fires once the browser has
176
+ * scheduled a new frame after our `render()` calls above; the second fires
177
+ * once THAT frame has itself been produced and composited — by the time the
178
+ * second callback runs, the compositor has committed the frame our render
179
+ * calls painted, so a screenshot taken immediately after this resolves
180
+ * reflects the seeked state, not a stale one. (The CDP screenshot
181
+ * compositor-commit wait itself is the capture host's job — I0/I3 — this
182
+ * fence is the runtime-side half.)
183
+ */
184
+ async function compositionFence() {
185
+ await nextAnimationFrame();
186
+ await nextAnimationFrame();
187
+ }
188
+ async function resolveHook(hook) {
189
+ if (!hook)
190
+ return { status: 'not-applicable' };
191
+ try {
192
+ await hook();
193
+ return { status: 'ready' };
194
+ }
195
+ catch (err) {
196
+ return { status: 'error', detail: err instanceof Error ? err.message : String(err) };
197
+ }
198
+ }
199
+ async function checkFontsReady() {
200
+ if (typeof document === 'undefined' || !document.fonts)
201
+ return { status: 'not-applicable' };
202
+ try {
203
+ await document.fonts.ready;
204
+ return { status: 'ready' };
205
+ }
206
+ catch (err) {
207
+ return { status: 'error', detail: err instanceof Error ? err.message : String(err) };
208
+ }
209
+ }
210
+ async function buildReadinessReport(hooks) {
211
+ const [assets, shaders, fonts, theatre, tone, scene] = await Promise.all([
212
+ resolveHook(hooks.assets),
213
+ resolveHook(hooks.shaders),
214
+ checkFontsReady(),
215
+ resolveHook(hooks.theatre),
216
+ resolveHook(hooks.tone),
217
+ resolveHook(hooks.scene),
218
+ ]);
219
+ return { assets, shaders, fonts, theatre, tone, scene };
220
+ }
221
+ /**
222
+ * Build (and, when render mode is actually requested, publish) the
223
+ * `window.__vgaiRender` harness. AC 4 — production protection — lives HERE,
224
+ * not just at the call site: even if a host application calls this
225
+ * unconditionally on every boot, the harness is only ever constructed *and*
226
+ * attached to `target` when `isRenderModeRequested(location)` is true;
227
+ * otherwise this is a no-op that returns `undefined`. A normal production
228
+ * gameplay page therefore never gets `window.__vgaiRender` no matter how
229
+ * this function is wired into its entry point.
230
+ */
231
+ export function installRenderControlHarness(opts) {
232
+ const location = opts.location ?? window.location;
233
+ if (!isRenderModeRequested(location))
234
+ return undefined;
235
+ const { game, clock, readiness = {} } = opts;
236
+ const target = opts.target ?? window;
237
+ const frameAdvancers = opts.frameAdvancers ?? [];
238
+ const simulateFixedDt = opts.simulateFixedDt ?? 1 / 60;
239
+ const declaredExclusions = opts.excludedFromDeterminism ?? [];
240
+ const harness = {
241
+ seek(t) {
242
+ clock.seek(t);
243
+ },
244
+ seekFrame(n, fps) {
245
+ clock.seekFrame(n, fps);
246
+ },
247
+ advanceFrame(dt) {
248
+ for (const advancer of frameAdvancers)
249
+ advancer(dt);
250
+ },
251
+ renderOnce() {
252
+ return renderOnce(game);
253
+ },
254
+ ready() {
255
+ return buildReadinessReport(readiness);
256
+ },
257
+ simulateSubsteps(steps) {
258
+ for (let i = 0; i < steps; i++) {
259
+ game.runFrame(simulateFixedDt);
260
+ }
261
+ },
262
+ excludedFromDeterminism() {
263
+ return [...declaredExclusions, ...computeRootExclusionReasons(game)];
264
+ },
265
+ simulateFixedDt() {
266
+ return simulateFixedDt;
267
+ },
268
+ perfSample(steps) {
269
+ const profiler = game.profiler;
270
+ const wasEnabled = profiler.enabled;
271
+ profiler.enabled = true;
272
+ profiler.clear();
273
+ const frames = [];
274
+ try {
275
+ for (let i = 0; i < steps; i++) {
276
+ game.runFrame(simulateFixedDt);
277
+ const snapshot = profiler.getSnapshot();
278
+ const latest = snapshot.frames.at(-1);
279
+ frames.push({
280
+ cpuMs: latest?.cpuMs ?? 0,
281
+ phases: Object.fromEntries((latest?.phases ?? []).map((p) => [p.name, p.ms])),
282
+ render: { ...snapshot.render },
283
+ });
284
+ }
285
+ }
286
+ finally {
287
+ profiler.enabled = wasEnabled;
288
+ }
289
+ return { frames, worlds: countRoots(game) };
290
+ },
291
+ };
292
+ target['__vgaiRender'] = harness;
293
+ return harness;
294
+ }
295
+ /**
296
+ * Defensive assertion for the I2 AC "Pixi roots are pinned to the WebGL
297
+ * renderer (not WebGPU) in render mode". In THIS engine that invariant
298
+ * already holds unconditionally — no in-repo mount constructs a Pixi
299
+ * `Application` with a WebGPU preference, and `setup/setup-renderer.ts` never
300
+ * constructs a `WebGPURenderer` for three roots either — so there is no
301
+ * live WebGPU code path for a first-party render-mode page to disable.
302
+ * This helper exists for a render-mode installer that mounts a CUSTOM/
303
+ * foreign pixi adapter (`create-runtime.ts`'s `{ module }` pixi path) this
304
+ * engine did not construct itself, to assert the invariant against the
305
+ * live mounted renderer rather than merely trusting it. Duck-typed against
306
+ * pixi.js v8's numeric `RendererType` (`WEBGL = 1`, `WEBGPU = 2`,
307
+ * `CANVAS = 4`) so this file never value-imports `pixi.js` (the same
308
+ * type-only-pixi rule `runtime/game.ts`/`create-runtime.ts` document for
309
+ * themselves).
310
+ */
311
+ export function assertPixiRootIsWebGL(rendererType, worldId) {
312
+ const isWebGPU = rendererType === 2 || (typeof rendererType === 'string' && /webgpu/i.test(rendererType));
313
+ if (isWebGPU) {
314
+ throw new Error(`[render-control] world "${worldId}": Pixi renderer is WebGPU in render mode — ` +
315
+ 'render-control requires WebGL.');
316
+ }
317
+ }