@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.
- package/README.md +6 -3
- package/dist/adapter/adapter-module.d.ts.map +1 -1
- package/dist/adapter/adapter-module.js +2 -4
- package/dist/adapter/adapter-surface.d.ts +1 -1
- package/dist/adapter/adapter-surface.js +1 -1
- package/dist/adapter/authoring.d.ts +19 -21
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/authoring.js +2 -2
- package/dist/adapter/binding.d.ts +341 -0
- package/dist/adapter/binding.d.ts.map +1 -0
- package/dist/adapter/binding.js +148 -0
- package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -1
- package/dist/adapter/colyseus-networking-adapter.js +23 -4
- package/dist/adapter/entrypoint-selection-readers.d.ts +24 -0
- package/dist/adapter/entrypoint-selection-readers.d.ts.map +1 -0
- package/dist/adapter/entrypoint-selection-readers.js +63 -0
- package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -1
- package/dist/adapter/entrypoint-selection-source.js +1 -44
- package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
- package/dist/adapter/finders/scenes-from-entrypoint-selection.js +1 -52
- package/dist/adapter/host-context.d.ts +46 -15
- package/dist/adapter/host-context.d.ts.map +1 -1
- package/dist/adapter/host-context.js +2 -9
- package/dist/adapter/index.d.ts +11 -12
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/index.js +9 -10
- package/dist/adapter/ingest/game-contract.d.ts +0 -3
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +0 -3
- package/dist/adapter/ingest/scene-capture.d.ts +19 -23
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +143 -17
- package/dist/adapter/ingest/visible-capture-window.d.ts +34 -0
- package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
- package/dist/adapter/manifest-interpreter.d.ts +59 -0
- package/dist/adapter/manifest-interpreter.d.ts.map +1 -0
- package/dist/adapter/manifest-interpreter.js +50 -0
- package/dist/adapter/native-debug-module.d.ts +71 -3
- package/dist/adapter/native-debug-module.d.ts.map +1 -1
- package/dist/adapter/native-debug-module.js +146 -10
- package/dist/adapter/rapier-physics-adapter.d.ts +31 -1
- package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -1
- package/dist/adapter/rapier-physics-adapter.js +50 -36
- package/dist/adapter/renderer-config.d.ts +3 -3
- package/dist/adapter/renderer-config.js +3 -3
- package/dist/adapter/root-adapter.d.ts +7 -7
- package/dist/adapter/root-adapter.js +4 -4
- package/dist/adapter/system-adapter.d.ts +45 -7
- package/dist/adapter/system-adapter.d.ts.map +1 -1
- package/dist/adapter/system-seam-contract.d.ts +3 -3
- package/dist/adapter/system-seam-contract.js +3 -3
- package/dist/adapter/system-slot.d.ts +168 -0
- package/dist/adapter/system-slot.d.ts.map +1 -0
- package/dist/adapter/system-slot.js +229 -0
- package/dist/adapter/transform.d.ts +11 -1
- package/dist/adapter/transform.d.ts.map +1 -1
- package/dist/ai/navigation.d.ts +17 -1
- package/dist/ai/navigation.d.ts.map +1 -1
- package/dist/ai/navigation.js +26 -5
- package/dist/animation/anim-graph-types.d.ts +11 -3
- package/dist/animation/anim-graph-types.d.ts.map +1 -1
- package/dist/animation/theatre-clock-binding.d.ts +4 -4
- package/dist/animation/theatre-clock-binding.js +4 -4
- package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
- package/dist/animation/xstate-animation-binding.js +2 -2
- package/dist/animation/xstate-animation-meta.d.ts +13 -0
- package/dist/animation/xstate-animation-meta.d.ts.map +1 -1
- package/dist/animation/xstate-animation-meta.js +14 -0
- package/dist/asset-formats/index.d.ts +4 -7
- package/dist/asset-formats/index.d.ts.map +1 -1
- package/dist/asset-formats/index.js +4 -7
- package/dist/asset-formats/render-env.d.ts +5 -8
- package/dist/asset-formats/render-env.d.ts.map +1 -1
- package/dist/asset-formats/render-env.js +3 -6
- package/dist/asset-parse-error.d.ts +2 -5
- package/dist/asset-parse-error.d.ts.map +1 -1
- package/dist/asset-parse-error.js +2 -5
- package/dist/audio/bus-mixer.d.ts +101 -0
- package/dist/audio/bus-mixer.d.ts.map +1 -0
- package/dist/audio/bus-mixer.js +115 -0
- package/dist/canvas-react/index.d.ts +5 -12
- package/dist/canvas-react/index.d.ts.map +1 -1
- package/dist/canvas-react/index.js +5 -12
- package/dist/canvas-react/pixi-react-root-factory.d.ts +34 -21
- package/dist/canvas-react/pixi-react-root-factory.d.ts.map +1 -1
- package/dist/canvas-react/pixi-react-root-factory.js +274 -36
- package/dist/core/countdown-timer.d.ts +118 -0
- package/dist/core/countdown-timer.d.ts.map +1 -0
- package/dist/core/countdown-timer.js +137 -0
- package/dist/core/deferred-commands.d.ts +127 -0
- package/dist/core/deferred-commands.d.ts.map +1 -0
- package/dist/core/deferred-commands.js +132 -0
- package/dist/core/frame-pacing.d.ts +1 -1
- package/dist/core/frame-pacing.js +2 -2
- package/dist/core/game-loop.d.ts +1 -3
- package/dist/core/game-loop.d.ts.map +1 -1
- package/dist/core/game-loop.js +1 -3
- package/dist/core/seeded-random.d.ts +1 -1
- package/dist/core/sim-clock.d.ts +19 -8
- package/dist/core/sim-clock.d.ts.map +1 -1
- package/dist/core/sim-clock.js +18 -7
- package/dist/core/types.d.ts +1 -3
- package/dist/core/types.d.ts.map +1 -1
- package/dist/defaults.d.ts +6 -14
- package/dist/defaults.d.ts.map +1 -1
- package/dist/defaults.js +6 -14
- package/dist/dev/instruments.d.ts +38 -11
- package/dist/dev/instruments.d.ts.map +1 -1
- package/dist/dev/instruments.js +81 -11
- package/dist/dev/register-render-vitals.d.ts +1 -4
- package/dist/dev/register-render-vitals.d.ts.map +1 -1
- package/dist/dev/register-render-vitals.js +1 -4
- package/dist/dev/render-debug-adapter.d.ts +2 -3
- package/dist/dev/render-debug-adapter.d.ts.map +1 -1
- package/dist/dev/render-debug-adapter.js +2 -3
- package/dist/dev/render-vitals.d.ts +3 -3
- package/dist/dev/render-vitals.js +3 -3
- package/dist/dev/webgl-frame-capture.d.ts +1 -1
- package/dist/dev/webgl-frame-capture.js +1 -1
- package/dist/index.d.ts +2 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -4
- package/dist/input/host-pointer.d.ts +122 -0
- package/dist/input/host-pointer.d.ts.map +1 -0
- package/dist/input/host-pointer.js +160 -0
- package/dist/input/input-manager.d.ts +33 -5
- package/dist/input/input-manager.d.ts.map +1 -1
- package/dist/input/input-manager.js +48 -13
- package/dist/input/rebind-controller.d.ts.map +1 -1
- package/dist/input/rebind-controller.js +6 -8
- package/dist/input/schema.d.ts.map +1 -1
- package/dist/input/schema.js +56 -28
- package/dist/manifest/filename.d.ts.map +1 -1
- package/dist/manifest/filename.js +8 -12
- package/dist/manifest/load.d.ts +10 -10
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +9 -6
- package/dist/manifest/schema.d.ts +17 -21
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +45 -35
- package/dist/physics/collider-dimensions.d.ts +8 -12
- package/dist/physics/collider-dimensions.d.ts.map +1 -1
- package/dist/physics/collider-dimensions.js +8 -12
- package/dist/pixi/scene-capture.d.ts +11 -13
- package/dist/pixi/scene-capture.d.ts.map +1 -1
- package/dist/react/world-state.d.ts +1 -1
- package/dist/react/world-state.d.ts.map +1 -1
- package/dist/render/directional-shadow-fit.d.ts +88 -0
- package/dist/render/directional-shadow-fit.d.ts.map +1 -0
- package/dist/render/directional-shadow-fit.js +133 -0
- package/dist/render/environment-capture.d.ts +68 -0
- package/dist/render/environment-capture.d.ts.map +1 -0
- package/dist/render/environment-capture.js +90 -0
- package/dist/render/ibl-override-material.d.ts +97 -0
- package/dist/render/ibl-override-material.d.ts.map +1 -0
- package/dist/render/ibl-override-material.js +114 -0
- package/dist/render/light-camera-factory.d.ts +5 -7
- package/dist/render/light-camera-factory.d.ts.map +1 -1
- package/dist/render/light-camera-factory.js +8 -11
- package/dist/render/material-factory.js +1 -1
- package/dist/render/render-batch-system.d.ts.map +1 -1
- package/dist/render/render-features.d.ts +17 -6
- package/dist/render/render-features.d.ts.map +1 -1
- package/dist/render/render-features.js +0 -28
- package/dist/runtime/create-runtime.d.ts +34 -25
- package/dist/runtime/create-runtime.d.ts.map +1 -1
- package/dist/runtime/create-runtime.js +218 -93
- package/dist/runtime/debug-registry.d.ts +77 -8
- package/dist/runtime/debug-registry.d.ts.map +1 -1
- package/dist/runtime/debug-registry.js +3 -3
- package/dist/runtime/dev-build.d.ts +2 -2
- package/dist/runtime/dev-build.js +2 -2
- package/dist/runtime/game-input-seams.d.ts +51 -0
- package/dist/runtime/game-input-seams.d.ts.map +1 -0
- package/dist/runtime/game-input-seams.js +86 -0
- package/dist/runtime/game.d.ts +104 -40
- package/dist/runtime/game.d.ts.map +1 -1
- package/dist/runtime/game.js +119 -61
- package/dist/runtime/mount-game.d.ts +0 -16
- package/dist/runtime/mount-game.d.ts.map +1 -1
- package/dist/runtime/mount-game.js +26 -41
- package/dist/runtime/mount-manifest.d.ts +22 -14
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +22 -20
- package/dist/runtime/playtest.d.ts +22 -0
- package/dist/runtime/playtest.d.ts.map +1 -0
- package/dist/runtime/playtest.js +12 -0
- package/dist/runtime/state-bridge.d.ts +2 -3
- package/dist/runtime/state-bridge.d.ts.map +1 -1
- package/dist/runtime/state-bridge.js +1 -0
- package/dist/setup/setup-audio.d.ts +12 -0
- package/dist/setup/setup-audio.d.ts.map +1 -1
- package/dist/setup/setup-audio.js +9 -10
- package/dist/setup/setup-renderer.d.ts +2 -25
- package/dist/setup/setup-renderer.d.ts.map +1 -1
- package/dist/setup/setup-renderer.js +2 -31
- package/dist/world3d-react/index.d.ts +15 -25
- package/dist/world3d-react/index.d.ts.map +1 -1
- package/dist/world3d-react/index.js +15 -25
- package/dist/world3d-react/r3f-root-factory.d.ts +28 -34
- package/dist/world3d-react/r3f-root-factory.d.ts.map +1 -1
- package/dist/world3d-react/r3f-root-factory.js +403 -57
- package/dist/world3d-react/rapier-physics-bridge.d.ts +30 -17
- package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
- package/dist/world3d-react/rapier-physics-bridge.js +143 -58
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +16 -14
- package/schemas/vgai-project.schema.json +35 -26
- package/src/adapter/adapter-module.ts +2 -4
- package/src/adapter/adapter-surface.ts +1 -1
- package/src/adapter/authoring.ts +19 -21
- package/src/adapter/binding.ts +468 -0
- package/src/adapter/colyseus-networking-adapter.ts +20 -5
- package/src/adapter/entrypoint-selection-readers.ts +66 -0
- package/src/adapter/entrypoint-selection-source.ts +1 -40
- package/src/adapter/finders/scenes-from-entrypoint-selection.ts +1 -48
- package/src/adapter/host-context.ts +45 -15
- package/src/adapter/index.ts +23 -11
- package/src/adapter/ingest/game-contract.ts +0 -3
- package/src/adapter/ingest/scene-capture.ts +190 -39
- package/src/adapter/ingest/visible-capture-window.ts +35 -0
- package/src/adapter/manifest-interpreter.ts +64 -0
- package/src/adapter/native-debug-module.ts +248 -15
- package/src/adapter/rapier-physics-adapter.ts +76 -20
- package/src/adapter/renderer-config.ts +3 -3
- package/src/adapter/root-adapter.ts +7 -7
- package/src/adapter/system-adapter.ts +43 -8
- package/src/adapter/system-seam-contract.ts +3 -3
- package/src/adapter/system-slot.ts +291 -0
- package/src/adapter/transform.ts +18 -1
- package/src/ai/navigation.ts +30 -3
- package/src/animation/anim-graph-types.ts +11 -3
- package/src/animation/theatre-clock-binding.ts +4 -4
- package/src/animation/xstate-animation-binding.ts +2 -2
- package/src/animation/xstate-animation-meta.ts +16 -0
- package/src/asset-formats/index.ts +4 -7
- package/src/asset-formats/render-env.ts +3 -6
- package/src/asset-parse-error.ts +2 -5
- package/src/audio/bus-mixer.ts +161 -0
- package/src/canvas-react/index.ts +5 -21
- package/src/canvas-react/pixi-react-root-factory.tsx +301 -39
- package/src/core/countdown-timer.ts +188 -0
- package/src/core/deferred-commands.ts +174 -0
- package/src/core/frame-pacing.ts +2 -2
- package/src/core/game-loop.ts +1 -3
- package/src/core/seeded-random.ts +1 -1
- package/src/core/sim-clock.ts +19 -8
- package/src/core/types.ts +1 -3
- package/src/defaults.ts +6 -14
- package/src/dev/instruments.ts +108 -11
- package/src/dev/register-render-vitals.ts +1 -4
- package/src/dev/render-debug-adapter.ts +2 -3
- package/src/dev/render-vitals.ts +3 -3
- package/src/dev/webgl-frame-capture.ts +1 -1
- package/src/index.ts +2 -5
- package/src/input/host-pointer.ts +230 -0
- package/src/input/input-manager.ts +49 -13
- package/src/input/input-types.ts +1 -1
- package/src/input/rebind-controller.ts +6 -8
- package/src/input/schema.ts +216 -188
- package/src/manifest/filename.ts +8 -12
- package/src/manifest/load.ts +17 -13
- package/src/manifest/schema.ts +55 -47
- package/src/physics/collider-dimensions.ts +8 -12
- package/src/pixi/scene-capture.ts +12 -15
- package/src/react/world-state.tsx +1 -1
- package/src/render/directional-shadow-fit.ts +156 -0
- package/src/render/environment-capture.ts +102 -0
- package/src/render/ibl-override-material.ts +170 -0
- package/src/render/light-camera-factory.ts +8 -11
- package/src/render/material-factory.ts +1 -1
- package/src/render/render-batch-system.ts +14 -6
- package/src/render/render-features.ts +17 -35
- package/src/runtime/create-runtime.ts +270 -108
- package/src/runtime/debug-registry.ts +84 -8
- package/src/runtime/dev-build.ts +2 -2
- package/src/runtime/game-input-seams.ts +108 -0
- package/src/runtime/game.ts +231 -85
- package/src/runtime/mount-game.ts +31 -57
- package/src/runtime/mount-manifest.ts +47 -36
- package/src/runtime/playtest.ts +22 -0
- package/src/runtime/state-bridge.ts +3 -3
- package/src/setup/setup-audio.ts +21 -11
- package/src/setup/setup-renderer.ts +2 -61
- package/src/world3d-react/index.ts +15 -31
- package/src/world3d-react/r3f-root-factory.tsx +451 -60
- package/src/world3d-react/rapier-physics-bridge.tsx +154 -58
- package/dist/adapter/setup-three-root-adapter.d.ts +0 -86
- package/dist/adapter/setup-three-root-adapter.d.ts.map +0 -1
- package/dist/adapter/setup-three-root-adapter.js +0 -908
- package/dist/animation/clip-map.d.ts +0 -12
- package/dist/animation/clip-map.d.ts.map +0 -1
- package/dist/animation/clip-map.js +0 -31
- package/dist/asset-registry.d.ts +0 -38
- package/dist/asset-registry.d.ts.map +0 -1
- package/dist/asset-registry.js +0 -66
- package/dist/canvas-react/engine-bridge.d.ts +0 -45
- package/dist/canvas-react/engine-bridge.d.ts.map +0 -1
- package/dist/canvas-react/engine-bridge.js +0 -45
- package/dist/canvas-react/pixi-react-adapter.d.ts +0 -77
- package/dist/canvas-react/pixi-react-adapter.d.ts.map +0 -1
- package/dist/canvas-react/pixi-react-adapter.js +0 -294
- package/dist/canvas-react/world-context.d.ts +0 -98
- package/dist/canvas-react/world-context.d.ts.map +0 -1
- package/dist/canvas-react/world-context.js +0 -173
- package/dist/dev/debug-draw.d.ts +0 -24
- package/dist/dev/debug-draw.d.ts.map +0 -1
- package/dist/dev/debug-draw.js +0 -73
- package/dist/render/auto-batcher.d.ts +0 -34
- package/dist/render/auto-batcher.d.ts.map +0 -1
- package/dist/render/auto-batcher.js +0 -140
- package/dist/render/lod.d.ts +0 -13
- package/dist/render/lod.d.ts.map +0 -1
- package/dist/render/lod.js +0 -16
- package/dist/runtime/types.d.ts +0 -303
- package/dist/runtime/types.d.ts.map +0 -1
- package/dist/runtime/types.js +0 -1
- package/dist/world3d-react/engine-bridge.d.ts +0 -47
- package/dist/world3d-react/engine-bridge.d.ts.map +0 -1
- package/dist/world3d-react/engine-bridge.js +0 -73
- package/dist/world3d-react/r3f-adapter.d.ts +0 -58
- package/dist/world3d-react/r3f-adapter.d.ts.map +0 -1
- package/dist/world3d-react/r3f-adapter.js +0 -634
- package/dist/world3d-react/world-context.d.ts +0 -182
- package/dist/world3d-react/world-context.d.ts.map +0 -1
- package/dist/world3d-react/world-context.js +0 -235
- package/src/adapter/setup-three-root-adapter.ts +0 -1032
- package/src/animation/clip-map.ts +0 -34
- package/src/asset-registry.ts +0 -89
- package/src/canvas-react/engine-bridge.ts +0 -59
- package/src/canvas-react/pixi-react-adapter.tsx +0 -356
- package/src/canvas-react/world-context.ts +0 -253
- package/src/dev/debug-draw.ts +0 -80
- package/src/render/auto-batcher.ts +0 -168
- package/src/render/lod.ts +0 -17
- package/src/runtime/types.ts +0 -328
- package/src/world3d-react/engine-bridge.ts +0 -86
- package/src/world3d-react/r3f-adapter.tsx +0 -717
- package/src/world3d-react/world-context.ts +0 -358
|
@@ -4,11 +4,9 @@
|
|
|
4
4
|
*
|
|
5
5
|
* WHY IT EXISTS. `SystemAdapters.physics` is what the editor's transform
|
|
6
6
|
* coordination (`freeze → apply → unfreeze`) and the universal
|
|
7
|
-
* "Colliders drawn" instrument (`@engine/dev/instruments`) read.
|
|
8
|
-
* first-party
|
|
9
|
-
*
|
|
10
|
-
* Rapier runtime at all (see `R3FGameContext`'s doc comment) — it does physics
|
|
11
|
-
* with `@react-three/rapier` INSIDE the fiber tree, where nothing engine-side
|
|
7
|
+
* "Colliders drawn" instrument (`@engine/dev/instruments`) read. An R3F world
|
|
8
|
+
* builds no first-party Rapier runtime at all — it does physics with
|
|
9
|
+
* `@react-three/rapier` INSIDE the fiber tree, where nothing engine-side
|
|
12
10
|
* can see it. Result, before this: the colliders instrument read `null` (an
|
|
13
11
|
* em-dash) and its toggle threw "no physics adapter" in every shipped example
|
|
14
12
|
* and every scaffolded game.
|
|
@@ -40,18 +38,23 @@
|
|
|
40
38
|
* is how a `SystemAdapters` member says "not supported".
|
|
41
39
|
*
|
|
42
40
|
* ── RESOURCE OWNERSHIP, STATED ONCE ─────────────────────────────────────────
|
|
43
|
-
* OWNER: this component owns
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* a world that unmounts `<Physics>` leaves no stale adapter
|
|
54
|
-
*
|
|
41
|
+
* OWNER: this component owns one physics ADAPTER, published onto the ONE
|
|
42
|
+
* module-scoped slot `rapierPhysicsSystem()` forwards to (GAME-GLOBAL — the
|
|
43
|
+
* entry's static `systems.physics` declaration cannot be per-root). It allocates nothing
|
|
44
|
+
* else — no timers, no subscriptions, no per-frame work (see the phase note
|
|
45
|
+
* below), and no Rapier objects: the bodies it edits belong to `<Physics>`.
|
|
46
|
+
* SHARERS: every reader of `game.systemAdapters.physics` — the colliders
|
|
47
|
+
* instrument, the editor's transform coordination. They read; they never
|
|
48
|
+
* dispose.
|
|
49
|
+
* TEARDOWN: the effect's cleanup, and nothing else — it clears the SAME key
|
|
50
|
+
* and the slot it set (identity-guarded), so a play stop/start, a hot
|
|
51
|
+
* restart, or a world that unmounts `<Physics>` leaves no stale adapter
|
|
52
|
+
* behind. Because the slot is PAGE-global (see its doc below — an engine
|
|
53
|
+
* module is shared across the editor's Play instances), mount exactly ONE
|
|
54
|
+
* bridge per PAGE: with two — a scene that mounts two, or two Play instances
|
|
55
|
+
* of one rapier world — the later mount wins the slot and the later unmount
|
|
56
|
+
* clears it while the earlier bridge is still live. That state is reported
|
|
57
|
+
* loudly the moment it starts (`warnOnContendedMount`).
|
|
55
58
|
*
|
|
56
59
|
* PHASE ORDERING: none to respect — this component registers pull-only
|
|
57
60
|
* callbacks and runs no per-frame work. Every method it exposes is invoked
|
|
@@ -64,6 +67,7 @@ import { useEffect } from 'react';
|
|
|
64
67
|
import * as THREE from 'three';
|
|
65
68
|
import {
|
|
66
69
|
createRapierBodyEditing,
|
|
70
|
+
type RapierBodyLookup,
|
|
67
71
|
type RapierEditableBody,
|
|
68
72
|
} from '../adapter/rapier-physics-adapter';
|
|
69
73
|
import type {
|
|
@@ -73,7 +77,7 @@ import type {
|
|
|
73
77
|
PhysicsJointSnapshot,
|
|
74
78
|
PhysicsJointType,
|
|
75
79
|
} from '../adapter/system-adapter';
|
|
76
|
-
import {
|
|
80
|
+
import { createSystemSlot } from '../adapter/system-slot';
|
|
77
81
|
|
|
78
82
|
export interface RapierPhysicsBridgeProps {
|
|
79
83
|
/**
|
|
@@ -123,57 +127,60 @@ function owningBody(
|
|
|
123
127
|
* `useRapier()`). Renders nothing.
|
|
124
128
|
*/
|
|
125
129
|
export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps): null {
|
|
126
|
-
|
|
130
|
+
// This bridge publishes the module slot below, which the entry's
|
|
131
|
+
// `systems.physics` declaration (`rapierPhysicsSystem()`) forwards to —
|
|
132
|
+
// that declaration is the ONE path the host reads this world's physics
|
|
133
|
+
// through.
|
|
127
134
|
const { colliderStates, rapier, rigidBodyStates, world } = useRapier();
|
|
128
|
-
const registerSystemAdapter = ctx.registerSystemAdapter;
|
|
129
135
|
|
|
130
136
|
useEffect(() => {
|
|
131
|
-
if (!registerSystemAdapter) return;
|
|
132
|
-
|
|
133
137
|
/**
|
|
134
|
-
* node id → the Rapier body driving it,
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* 2. `Object3D` → body, through `rigidBodyStates` — the library's OWN
|
|
142
|
-
* body↔object map, carried on the `RapierContext` `useRapier()`
|
|
143
|
-
* returns. Walking ANCESTORS matters: `<RigidBody>` owns a subtree, so
|
|
144
|
-
* the selected mesh is usually a descendant of the object the state
|
|
145
|
-
* names.
|
|
138
|
+
* node id → the Rapier body driving it, entirely through the library's
|
|
139
|
+
* OWN body↔object map (`rigidBodyStates`, carried on the `RapierContext`
|
|
140
|
+
* `useRapier()` returns): resolve `userData.entityId` — this stack's
|
|
141
|
+
* identity convention, stamped by the editor's R3F authoring adapter —
|
|
142
|
+
* inside the body-owned subtrees, then walk ANCESTORS to the owning body
|
|
143
|
+
* (`<RigidBody>` owns a subtree, so the selected mesh is usually a
|
|
144
|
+
* descendant of the object the state names).
|
|
146
145
|
*
|
|
147
|
-
*
|
|
146
|
+
* The map is live, so this is computed per call rather than cached —
|
|
148
147
|
* calls arrive at gesture rate (a gizmo drag start/end, a dev command),
|
|
149
|
-
* never per frame
|
|
150
|
-
*
|
|
151
|
-
* after this component's first effect (`world-context.ts`'s
|
|
152
|
-
* `setSceneCamera`), so a destructured copy would be permanently absent.
|
|
148
|
+
* never per frame, and only objects under a `<RigidBody>` can have a body
|
|
149
|
+
* at all, so the body subtrees are the complete search space.
|
|
153
150
|
*/
|
|
154
151
|
const bodyFor = (nodeId: string): RapierEditableBody | undefined => {
|
|
155
|
-
const
|
|
156
|
-
const object = indexed ?? lateEntityObject(rigidBodyStates.values(), nodeId);
|
|
157
|
-
if (!indexed && object) {
|
|
158
|
-
// The editor assigns its stable authoring entity ids AFTER Fiber's
|
|
159
|
-
// first commit, while the root's SceneIndex is constructed during
|
|
160
|
-
// that commit. Resolve the one late id inside the library-owned body
|
|
161
|
-
// subtrees and immediately reindex it; subsequent gestures take the
|
|
162
|
-
// normal indexed path. This is the SceneIndex's documented escape
|
|
163
|
-
// hatch for a userData id written after construction, not a parallel
|
|
164
|
-
// scene mirror.
|
|
165
|
-
ctx.sceneIndex?.reindex(object);
|
|
166
|
-
}
|
|
152
|
+
const object = lateEntityObject(rigidBodyStates.values(), nodeId);
|
|
167
153
|
if (!object) return undefined;
|
|
168
154
|
return owningBody(object, rigidBodyStates.values());
|
|
169
155
|
};
|
|
170
156
|
|
|
157
|
+
/**
|
|
158
|
+
* The same question, with the two negative answers KEPT APART for the
|
|
159
|
+
* editing protocol — see {@link RapierBodyLookup}. This world's search
|
|
160
|
+
* space is the `<RigidBody>` subtrees, so "no object here" really is "not a
|
|
161
|
+
* node of mine", not "a node of mine without physics".
|
|
162
|
+
*/
|
|
163
|
+
const bodyLookup = (nodeId: string): RapierBodyLookup => {
|
|
164
|
+
const object = lateEntityObject(rigidBodyStates.values(), nodeId);
|
|
165
|
+
if (!object) return { kind: 'unresolved' };
|
|
166
|
+
const body = owningBody(object, rigidBodyStates.values());
|
|
167
|
+
return body ? { kind: 'body', body } : { kind: 'no-body' };
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
/** Rapier's own name for a shape type, via the enum's reverse mapping —
|
|
171
|
+
* so an unprojected collider reports WHICH shape it is (`TriMesh`,
|
|
172
|
+
* `HeightField`, …) instead of a bare number, and a shape added by a
|
|
173
|
+
* future rapier still names itself. */
|
|
174
|
+
const rapierShapeName = (shapeType: number): string =>
|
|
175
|
+
(rapier.ShapeType as unknown as Record<number, string | undefined>)[shapeType] ??
|
|
176
|
+
`shapeType ${shapeType}`;
|
|
177
|
+
|
|
171
178
|
const colliderShape = (collider: {
|
|
172
179
|
shapeType(): number;
|
|
173
180
|
halfExtents(): { x: number; y: number; z: number };
|
|
174
181
|
radius(): number;
|
|
175
182
|
halfHeight(): number;
|
|
176
|
-
}): PhysicsColliderShape
|
|
183
|
+
}): PhysicsColliderShape => {
|
|
177
184
|
switch (collider.shapeType()) {
|
|
178
185
|
case rapier.ShapeType.Cuboid: {
|
|
179
186
|
const halfExtents = collider.halfExtents();
|
|
@@ -187,8 +194,13 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
|
|
|
187
194
|
halfHeight: collider.halfHeight(),
|
|
188
195
|
radius: collider.radius(),
|
|
189
196
|
};
|
|
197
|
+
// Rapier has many more shapes than this seam projects (TriMesh,
|
|
198
|
+
// HeightField, ConvexPolyhedron, Cylinder, Cone, …). The collider is
|
|
199
|
+
// REAL; only our plain-data projection of it is missing, so it is
|
|
200
|
+
// reported as itself rather than dropped — a body whose only collider
|
|
201
|
+
// is a trimesh used to inspect as a body with no colliders at all.
|
|
190
202
|
default:
|
|
191
|
-
return
|
|
203
|
+
return { type: 'unsupported', kind: rapierShapeName(collider.shapeType()) };
|
|
192
204
|
}
|
|
193
205
|
};
|
|
194
206
|
|
|
@@ -199,7 +211,6 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
|
|
|
199
211
|
for (const state of colliderStates.values()) {
|
|
200
212
|
if (state.collider.parent()?.handle !== body.handle) continue;
|
|
201
213
|
const shape = colliderShape(state.collider);
|
|
202
|
-
if (!shape) continue;
|
|
203
214
|
state.object.updateWorldMatrix(true, false);
|
|
204
215
|
const position = state.collider.translation();
|
|
205
216
|
const rotation = state.collider.rotation();
|
|
@@ -236,6 +247,11 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
|
|
|
236
247
|
collider.setHalfHeight(shape.halfHeight);
|
|
237
248
|
collider.setRadius(shape.radius);
|
|
238
249
|
break;
|
|
250
|
+
// A shape this seam does not project has no dimensions to write back.
|
|
251
|
+
// `colliders()` reports it so the body's physics is not invisible; that
|
|
252
|
+
// is a READ, and it does not make the shape editable.
|
|
253
|
+
case 'unsupported':
|
|
254
|
+
break;
|
|
239
255
|
}
|
|
240
256
|
};
|
|
241
257
|
|
|
@@ -360,16 +376,96 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
|
|
|
360
376
|
};
|
|
361
377
|
|
|
362
378
|
const adapter: PhysicsAdapter = {
|
|
363
|
-
...createRapierBodyEditing(
|
|
379
|
+
...createRapierBodyEditing(bodyLookup),
|
|
364
380
|
setDebugDrawEnabled: (enabled) => onDebugChange(enabled),
|
|
365
381
|
colliders,
|
|
366
382
|
previewCollider,
|
|
367
383
|
joints,
|
|
368
384
|
previewJointAnchor,
|
|
369
385
|
};
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
386
|
+
warnOnContendedMount();
|
|
387
|
+
const detach = physicsSlot.attach(adapter);
|
|
388
|
+
return () => {
|
|
389
|
+
liveBridgeCount -= 1;
|
|
390
|
+
detach();
|
|
391
|
+
};
|
|
392
|
+
}, [colliderStates, onDebugChange, rapier, rigidBodyStates, world]);
|
|
373
393
|
|
|
374
394
|
return null;
|
|
375
395
|
}
|
|
396
|
+
|
|
397
|
+
// ---------------------------------------------------------------------------
|
|
398
|
+
// The entry-declaration half
|
|
399
|
+
// ---------------------------------------------------------------------------
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* The bridge's slot. Module-scoped on purpose: the entry's `systems`
|
|
403
|
+
* declaration is static, the rapier world is not, and this slot is the seam
|
|
404
|
+
* between the two. Module scope here means one per PAGE, not one per game:
|
|
405
|
+
* this file is an ENGINE module, reached by bare specifier, so the editor's
|
|
406
|
+
* multi-instance Play (which stamps mount ids onto PROJECT-owned specifiers
|
|
407
|
+
* only — `server/project-module-instance.ts`) shares this one slot across
|
|
408
|
+
* every mounted instance. Two live instances of a rapier world therefore
|
|
409
|
+
* contend for it: the LAST bridge to mount wins every instance's declared
|
|
410
|
+
* `systems.physics`, and the loser's detach is dropped by the identity guard.
|
|
411
|
+
* {@link warnOnContendedMount} makes that state loud at the moment it
|
|
412
|
+
* happens; making the slot instance-scoped needs a per-mount rendezvous the
|
|
413
|
+
* context-free declaration (`rapierPhysicsSystem()` at project-module scope)
|
|
414
|
+
* does not yet have.
|
|
415
|
+
*
|
|
416
|
+
* Every member is an ACTION: each one either edits the sim or reads a live
|
|
417
|
+
* Rapier handle, and all of them arrive at gesture rate (a gizmo drag, an
|
|
418
|
+
* instrument command), never from a per-frame loop — so refusing by name is
|
|
419
|
+
* always the right answer, and a fabricated empty never is.
|
|
420
|
+
* `debugDraw`/`contactPoints` are OMITTED, not stubbed, for the anti-shim
|
|
421
|
+
* reason stated at the top of this file; a member the config does not list
|
|
422
|
+
* does not exist on the slot.
|
|
423
|
+
*/
|
|
424
|
+
let liveBridgeCount = 0;
|
|
425
|
+
|
|
426
|
+
/** The page-scope contention above, made LOUD instead of silent (never a
|
|
427
|
+
* silent degrade): a second concurrent bridge mount — a scene that mounted
|
|
428
|
+
* two, or a second Play instance of a rapier world — is reported the moment
|
|
429
|
+
* it happens, naming what breaks and until when. */
|
|
430
|
+
function warnOnContendedMount(): void {
|
|
431
|
+
liveBridgeCount += 1;
|
|
432
|
+
if (liveBridgeCount <= 1) return;
|
|
433
|
+
// biome-ignore lint/suspicious/noConsole: the standing-warning channel for a capability the adapter cannot reach — the editor console surfaces it during play
|
|
434
|
+
console.warn(
|
|
435
|
+
`[rapierPhysicsSystem] ${liveBridgeCount} <RapierPhysicsBridge> mounts are live on this ` +
|
|
436
|
+
'page, but the declared physics slot is one per page (engine modules are shared across ' +
|
|
437
|
+
"Play instances). Every instance's `systems.physics` now forwards to the LAST bridge " +
|
|
438
|
+
'mounted, so physics gizmo edits and instruments address that world — not necessarily ' +
|
|
439
|
+
'the one selected — until the extra mounts unmount.',
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const physicsSlot = createSystemSlot<PhysicsAdapter>({
|
|
444
|
+
name: 'rapierPhysicsSystem',
|
|
445
|
+
unattachedMessage:
|
|
446
|
+
'no <RapierPhysicsBridge> is mounted inside a live <Physics> right now — the world is not ' +
|
|
447
|
+
'playing, or the scene never mounted the bridge.',
|
|
448
|
+
members: {
|
|
449
|
+
ownerOf: { kind: 'action' },
|
|
450
|
+
freeze: { kind: 'action' },
|
|
451
|
+
commit: { kind: 'action' },
|
|
452
|
+
unfreeze: { kind: 'action' },
|
|
453
|
+
setDebugDrawEnabled: { kind: 'action' },
|
|
454
|
+
colliders: { kind: 'action' },
|
|
455
|
+
previewCollider: { kind: 'action' },
|
|
456
|
+
joints: { kind: 'action' },
|
|
457
|
+
previewJointAnchor: { kind: 'action' },
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* The `systems.physics` slot for a default-exported world — declare it from
|
|
463
|
+
* the entry (`export const systems = { physics: rapierPhysicsSystem() }`) and
|
|
464
|
+
* keep `<RapierPhysicsBridge />` mounted inside `<Physics>`. Every call
|
|
465
|
+
* forwards to the LIVE bridge adapter, so the static declaration answers with
|
|
466
|
+
* the running world's physics; while none is mounted, every call refuses by
|
|
467
|
+
* name rather than fabricating.
|
|
468
|
+
*/
|
|
469
|
+
export function rapierPhysicsSystem(): PhysicsAdapter {
|
|
470
|
+
return physicsSlot.slot;
|
|
471
|
+
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SetupThreeRootAdapter — the first-party implementer of {@link RootAdapter}
|
|
3
|
-
* that mounts ONE three root from an imperative `setup(ctx)` function.
|
|
4
|
-
*
|
|
5
|
-
* This is where Rapier lives now: the host no longer knows about it.
|
|
6
|
-
* `mount(host)` builds the full first-party runtime (the body
|
|
7
|
-
* that used to live inline in `create-runtime.ts`) from a neutral
|
|
8
|
-
* {@link ThreeHostContext}, runs the game's `setup`, and returns a
|
|
9
|
-
* {@link MountedThreeRoot}. First-party content is now just one implementer of
|
|
10
|
-
* the same interface an unmodified external game implements (Phase B).
|
|
11
|
-
*
|
|
12
|
-
* It was `VgaiSceneGameAdapter` until WO-9 tasks #6/#9. Both halves of that
|
|
13
|
-
* name had stopped being true: it loads no scene (the `.vscn.json` branches
|
|
14
|
-
* went with the format in WO-8 — `setup` is the only mount path left), and what
|
|
15
|
-
* it mounts is one three ROOT, not a game (a game is the manifest's whole set
|
|
16
|
-
* of roots). The R3F/TSX world root is a SEPARATE adapter; this one is
|
|
17
|
-
* specifically the `fromSetup` path.
|
|
18
|
-
*
|
|
19
|
-
* `GameSetupFn` / `GameContext` are imported ONLY here (and in the example
|
|
20
|
-
* `fromSetup` wrappers) — never by the host. That confinement is the inversion.
|
|
21
|
-
*/
|
|
22
|
-
import type { EffectComposer } from 'postprocessing';
|
|
23
|
-
import { type ViewportShadingMode } from '../render/viewport-shading';
|
|
24
|
-
import { type RootFrameHooks } from '../runtime/game';
|
|
25
|
-
import type { EditorPreview, GameContext, GameSetupFn } from '../runtime/types';
|
|
26
|
-
import type { ThreeHostContext } from './host-context';
|
|
27
|
-
import type { MountedThreeRoot, RootAdapter } from './root-adapter';
|
|
28
|
-
/** How a {@link SetupThreeRootAdapter} builds its game (the former RuntimeConfig). */
|
|
29
|
-
export interface SetupThreeRootConfig {
|
|
30
|
-
/** Game setup function — registers systems and loads scenes. */
|
|
31
|
-
setup?: GameSetupFn | undefined;
|
|
32
|
-
/** Editor context — passed to setup() when launched from the editor. */
|
|
33
|
-
editorPreview?: EditorPreview | undefined;
|
|
34
|
-
/** Input map JSON path. Defaults to 'inputmaps/default.inputmap.json'. */
|
|
35
|
-
inputMapPath?: string | undefined;
|
|
36
|
-
/** URL prefix for relative asset paths. Defaults to '/'. */
|
|
37
|
-
assetPrefix?: string | undefined;
|
|
38
|
-
/** Stable adapter id (for registry/conformance). Defaults to 'setup-three'. */
|
|
39
|
-
id?: string | undefined;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* The first-party `MountedThreeRoot`, with concrete extras the editor host uses for
|
|
43
|
-
* first-party features (HMR, physics sync) that are NOT part of the neutral
|
|
44
|
-
* interface. The generic host only touches the `MountedThreeRoot` surface.
|
|
45
|
-
*/
|
|
46
|
-
export interface MountedSetupThreeRoot extends MountedThreeRoot {
|
|
47
|
-
/**
|
|
48
|
-
* First-party brand (checklist item 1 / T7.3 lookahead): a plain `'ctx' in
|
|
49
|
-
* mounted` structural check misfires against any foreign mount that happens
|
|
50
|
-
* to carry an unrelated `ctx` key. This literal-`true`
|
|
51
|
-
* property is what `isFirstPartyMounted` (`runtime/game.ts`) actually
|
|
52
|
-
* checks; `game.ts` reads it via a structural `{ firstParty?: unknown }`
|
|
53
|
-
* probe so it never needs a value import of this module (the existing
|
|
54
|
-
* import is type-only and must stay that way).
|
|
55
|
-
*/
|
|
56
|
-
readonly firstParty: true;
|
|
57
|
-
readonly ctx: GameContext;
|
|
58
|
-
readonly composer: EffectComposer | null;
|
|
59
|
-
/** Render-only developer view; native game materials are restored before the frame returns. */
|
|
60
|
-
setViewportShadingMode(mode: ViewportShadingMode): void;
|
|
61
|
-
/** Warm-restart: dispose the current game, re-run a new setup on the same ctx. */
|
|
62
|
-
hotReload(newSetup: GameSetupFn, editorPreview?: EditorPreview): Promise<void>;
|
|
63
|
-
/**
|
|
64
|
-
* Phase-partitioned frame entry point (T7.1 slice 2) — wired onto this
|
|
65
|
-
* world's `RootInstance` by `registerThreeRoot` (`create-runtime.ts`).
|
|
66
|
-
* `Game.runFrame` is the CANONICAL driver going forward: it calls
|
|
67
|
-
* `frame.runPhase` once per (phase, substep) for every world, then
|
|
68
|
-
* `frame.endFrame` once per substep after all roots finish all phases.
|
|
69
|
-
* `update` is the direct entry point to the same `SystemRunner` for hosts
|
|
70
|
-
* that do not partition phases themselves.
|
|
71
|
-
*/
|
|
72
|
-
readonly frame: RootFrameHooks;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Wrap a first-party `setup` function as a {@link RootAdapter}. This is how every
|
|
76
|
-
* first-party example becomes an implementer of the same interface an external
|
|
77
|
-
* game implements — with no change to the setup's body.
|
|
78
|
-
*/
|
|
79
|
-
export declare function fromSetup(id: string, setup: GameSetupFn): RootAdapter;
|
|
80
|
-
export declare class SetupThreeRootAdapter implements RootAdapter {
|
|
81
|
-
private readonly config;
|
|
82
|
-
readonly id: string;
|
|
83
|
-
constructor(config?: SetupThreeRootConfig);
|
|
84
|
-
mount(host: ThreeHostContext): Promise<MountedSetupThreeRoot>;
|
|
85
|
-
}
|
|
86
|
-
//# sourceMappingURL=setup-three-root-adapter.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"setup-three-root-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/setup-three-root-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAoCrD,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,4BAA4B,CAAC;AAO/F,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAe,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGpE,sFAAsF;AACtF,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,wEAAwE;IACxE,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,+EAA+E;IAC/E,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,+FAA+F;IAC/F,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACxD,kFAAkF;IAClF,SAAS,CAAC,QAAQ,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC;AAeD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,WAAW,CAErE;AAED,qBAAa,qBAAsB,YAAW,WAAW;IAE3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IADnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBACS,MAAM,GAAE,oBAAyB;IAKxD,KAAK,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAq1BpE"}
|