@vgai/engine 0.5.1 → 0.5.3
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 +18 -11
- package/package.json +10 -6
- package/schemas/engine-api.json +1 -68
- package/schemas/engine-api.md +1 -32
- package/schemas/engine-capabilities.json +30 -42
- package/schemas/{vgai-game.schema.json → vgai-project.schema.json} +52 -34
- package/src/adapter/adapter-surface.ts +5 -5
- package/src/adapter/authoring.ts +168 -226
- package/src/adapter/colyseus-networking-adapter.ts +44 -5
- package/src/adapter/first-party-systems.ts +156 -42
- package/src/adapter/host-context.ts +177 -46
- package/src/adapter/index.ts +47 -51
- package/src/adapter/ingest/game-contract.ts +2 -2
- package/src/adapter/ingest/scene-capture.ts +18 -19
- package/src/adapter/ingest/structural-ids.ts +127 -0
- package/src/adapter/ingest/upstream-pin.ts +9 -12
- package/src/adapter/loop-gate-report.ts +11 -11
- package/src/adapter/rapier-physics-adapter.ts +27 -9
- package/src/adapter/root-adapter.ts +217 -0
- package/src/adapter/{vgai-scene-game-adapter.ts → setup-three-root-adapter.ts} +173 -351
- package/src/adapter/system-adapter.ts +80 -63
- package/src/ai/navigation.ts +1 -1
- package/src/animation/animation-clock.ts +1 -1
- package/src/animation/camera-ownership.ts +1 -2
- package/src/animation/cubic-spline-interpolant.ts +132 -0
- package/src/animation/theatre-clock-binding.ts +2 -2
- package/src/animation/theatre-object-binding.ts +4 -4
- package/src/animation/xstate-animation-binding.ts +75 -5
- package/src/{scene/schema → asset-formats}/camera.ts +2 -5
- package/src/{scene/schema → asset-formats}/collider.ts +2 -5
- package/src/asset-formats/index.ts +54 -0
- package/src/{scene/schema → asset-formats}/instances.ts +10 -6
- package/src/{scene/schema → asset-formats}/light.ts +3 -6
- package/src/{scene/schema → asset-formats}/material.ts +4 -7
- package/src/{scene/schema → asset-formats}/mesh.ts +4 -7
- package/src/asset-formats/parse.ts +39 -0
- package/src/{scene/schema → asset-formats}/particles.ts +3 -6
- package/src/{scene/schema/environment.ts → asset-formats/render-env.ts} +23 -86
- package/src/{scene/schema → asset-formats}/tuples.ts +1 -1
- package/src/{scene/asset-loaders.ts → asset-loaders.ts} +8 -9
- package/src/asset-parse-error.ts +33 -0
- package/src/{scene/asset-registry.ts → asset-registry.ts} +1 -1
- package/src/assets.ts +1 -1
- package/src/audio/wav-encode.ts +9 -9
- package/src/canvas-react/engine-bridge.ts +59 -0
- package/src/canvas-react/index.ts +50 -0
- package/src/canvas-react/pixi-primitive.tsx +202 -0
- package/src/canvas-react/pixi-react-adapter.tsx +290 -0
- package/src/canvas-react/pixi-react-root-factory.tsx +88 -0
- package/src/canvas-react/world-context.ts +328 -0
- package/src/core/frame-pacing.ts +100 -0
- package/src/core/game-loop.ts +50 -28
- package/src/core/seeded-random.ts +7 -7
- package/src/core/sim-clock.ts +388 -0
- package/src/core/system-runner.ts +17 -63
- package/src/core/types.ts +34 -15
- package/src/data/data-asset.ts +3 -3
- package/src/data/data-check-core.ts +6 -7
- package/src/data/data-ref.ts +11 -11
- package/src/data/vite-plugin-data.ts +10 -10
- package/src/{scene/defaults.ts → defaults.ts} +18 -40
- package/src/dev/render-debug-adapter.ts +1 -1
- package/src/dev/webgl-frame-capture.ts +1 -1
- package/src/ecs/scene-index.ts +439 -0
- package/src/ecs/scene-query.ts +43 -0
- package/src/{scene → ecs}/user-data.ts +17 -36
- package/src/index.ts +7 -9
- package/src/input/input-manager.ts +29 -32
- package/src/input/input-types.ts +2 -2
- package/src/input/schema.ts +5 -5
- package/src/loader.ts +57 -0
- package/src/manifest/editor-port.ts +69 -0
- package/src/manifest/filename.ts +49 -0
- package/src/manifest/index.ts +8 -2
- package/src/manifest/load-file.ts +11 -0
- package/src/manifest/load.ts +65 -77
- package/src/manifest/locate.ts +55 -0
- package/src/manifest/schema.ts +400 -233
- package/src/{scene → physics}/collider-dimensions.ts +3 -3
- package/src/physics/physics-registry.ts +1 -1
- package/src/{world2d/authoring-2d.ts → pixi/authoring.ts} +24 -11
- package/src/pixi/index.ts +43 -0
- package/src/{world2d/ingest-iframe-2d.ts → pixi/ingest-iframe.ts} +9 -9
- package/src/{world2d/ingest2d.ts → pixi/ingest.ts} +28 -28
- package/src/{world2d/physics2d-registry.ts → pixi/physics-registry.ts} +1 -1
- package/src/{world2d/scene-capture-2d.ts → pixi/scene-capture.ts} +3 -3
- package/src/{world2d/system-adapters-2d.ts → pixi/system-adapters.ts} +2 -2
- package/src/react/unmanaged-root-detector.ts +26 -1
- package/src/react/use-data.ts +5 -5
- package/src/react/use-selection.tsx +15 -42
- package/src/react/{game-state.tsx → world-state.tsx} +44 -46
- package/src/render/auto-batcher.ts +1 -2
- package/src/{scene → render}/instance-mesh.ts +1 -1
- package/src/{scene → render}/light-camera-factory.ts +14 -13
- package/src/render/lod.ts +17 -0
- package/src/{scene → render}/material-factory.ts +8 -7
- package/src/{scene → render}/particles-factory.ts +62 -12
- package/src/render/render-batch-system.ts +14 -41
- package/src/render/render-features.ts +1 -1
- package/src/render/render-settings.ts +1 -2
- package/src/render/spark-renderer-lifecycle.ts +1 -1
- package/src/runtime/create-runtime.ts +352 -463
- package/src/runtime/debug-bridge.ts +148 -93
- package/src/runtime/debug-registry.ts +79 -54
- package/src/runtime/dev-layers.ts +40 -0
- package/src/runtime/frame-selector-cache.ts +4 -4
- package/src/runtime/game.ts +618 -369
- package/src/runtime/gameplay-rng-trap.ts +6 -7
- package/src/runtime/input-router.ts +11 -11
- package/src/runtime/mount-game.ts +54 -55
- package/src/runtime/mount-manifest.ts +154 -150
- package/src/runtime/presentation.ts +141 -0
- package/src/runtime/render-audio-control.ts +64 -53
- package/src/runtime/render-control.ts +45 -51
- package/src/runtime/render-seed.ts +3 -4
- package/src/runtime/state-bridge.ts +17 -18
- package/src/runtime/types.ts +94 -61
- package/src/setup/setup-renderer.ts +11 -6
- package/src/world3d-react/engine-bridge.ts +46 -33
- package/src/world3d-react/index.ts +31 -26
- package/src/world3d-react/r3f-adapter.tsx +211 -96
- package/src/world3d-react/r3f-root-factory.tsx +91 -0
- package/src/world3d-react/renderer-config.ts +137 -0
- package/src/world3d-react/world-context.ts +141 -111
- package/schemas/entity2d.schema.json +0 -468
- package/schemas/prefab.schema.json +0 -9992
- package/schemas/scn2d.schema.json +0 -494
- package/schemas/vscn.schema.json +0 -10851
- package/src/adapter/game-adapter.ts +0 -164
- package/src/adapter/ingest/overlay-applier.ts +0 -207
- package/src/adapter/ingest/overlay-apply.ts +0 -168
- package/src/adapter/ingest/overlay-file.ts +0 -126
- package/src/adapter/ingest/overlay-report.ts +0 -176
- package/src/animation/gsap-registration.ts +0 -184
- package/src/audio/audio-introspection.ts +0 -290
- package/src/audio/index.ts +0 -39
- package/src/audio/tone-clock-binding.ts +0 -98
- package/src/audio/tone-context.ts +0 -175
- package/src/audio/tone-offline-render.ts +0 -167
- package/src/ecs/component-manager.ts +0 -814
- package/src/ecs/game-component.ts +0 -260
- package/src/ecs/hmr-swap-report.ts +0 -65
- package/src/physics/trigger-dispatch.ts +0 -97
- package/src/react/root-adapter.tsx +0 -49
- package/src/scene/asset-paths.ts +0 -121
- package/src/scene/asset-ref-check.ts +0 -248
- package/src/scene/component-registry.ts +0 -51
- package/src/scene/parse.ts +0 -204
- package/src/scene/scene-apply.ts +0 -407
- package/src/scene/scene-diff-schema.ts +0 -115
- package/src/scene/scene-diff-types.ts +0 -29
- package/src/scene/scene-loader.ts +0 -1526
- package/src/scene/scene-query.ts +0 -63
- package/src/scene/scene-types.ts +0 -33
- package/src/scene/scene-version.ts +0 -40
- package/src/scene/schema/animation.ts +0 -46
- package/src/scene/schema/audio.ts +0 -25
- package/src/scene/schema/entity-ref.ts +0 -78
- package/src/scene/schema/entity.ts +0 -189
- package/src/scene/schema/index.ts +0 -51
- package/src/scene/schema/joint.ts +0 -26
- package/src/scene/schema/physics.ts +0 -49
- package/src/scene/schema/scene-file.ts +0 -292
- package/src/scene/schema/shadow.ts +0 -24
- package/src/scene/schema/spline.ts +0 -21
- package/src/world2d/asset-paths2d.ts +0 -44
- package/src/world2d/capture-to-scene2d.ts +0 -52
- package/src/world2d/collision-2d.ts +0 -99
- package/src/world2d/entity2d-asset.ts +0 -22
- package/src/world2d/index.ts +0 -91
- package/src/world2d/physics2d-transform.ts +0 -173
- package/src/world2d/physics2d-units.ts +0 -10
- package/src/world2d/pixi-game-adapter.ts +0 -439
- package/src/world2d/pixi-surface.ts +0 -78
- package/src/world2d/scene2d-identity.ts +0 -49
- package/src/world2d/scene2d-loader.ts +0 -433
- package/src/world2d/schema/entity2d.ts +0 -163
- package/src/world2d/schema/physics2d.ts +0 -64
- package/src/world2d/schema/sprite.ts +0 -99
- package/src/world2d/schema/tilemap.ts +0 -39
- package/src/world2d/schema/tuples2d.ts +0 -25
- package/src/world2d/transform-writer-2d.ts +0 -42
- package/src/world2d/types.ts +0 -74
- package/src/world3d-react/behavior.tsx +0 -146
- /package/src/{scene → render}/mesh-shadow.ts +0 -0
package/src/adapter/authoring.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* interface and the advertised `capabilities` — it never branches on which
|
|
9
9
|
* implementer it is talking to.
|
|
10
10
|
*
|
|
11
|
-
* The interface lives in the engine (not the editor) so `
|
|
11
|
+
* The interface lives in the engine (not the editor) so `MountedThreeRoot.authoring`
|
|
12
12
|
* can reference it without the engine depending on the editor; implementers live
|
|
13
13
|
* in `packages/editor/src/authoring/`.
|
|
14
14
|
*
|
|
@@ -21,39 +21,34 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import type * as THREE from 'three';
|
|
24
|
-
import type {
|
|
25
|
-
import type { Transform, TransformOwner } from './transform';
|
|
24
|
+
import type { Transform } from './transform';
|
|
26
25
|
|
|
27
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Booleans the editor UI gates affordances on (hide what an adapter can't do).
|
|
28
|
+
*
|
|
29
|
+
* Deliberately SMALL: a capability flag earns its place only when the shell
|
|
30
|
+
* actually branches on it. Structural affordances (create/delete/reparent) are
|
|
31
|
+
* gated by `structure` PROVIDER PRESENCE instead — the flag and the provider
|
|
32
|
+
* cannot disagree that way. (WO-2 deleted `material`/`create`/`delete`/
|
|
33
|
+
* `reparent`/`layout`, which no shell code ever read.)
|
|
34
|
+
*/
|
|
28
35
|
export interface AuthoringCapabilities {
|
|
29
36
|
transform: boolean;
|
|
30
|
-
material: boolean;
|
|
31
37
|
inspectorFields: boolean;
|
|
32
|
-
create: boolean;
|
|
33
|
-
delete: boolean;
|
|
34
|
-
reparent: boolean;
|
|
35
38
|
persist: boolean;
|
|
36
|
-
/**
|
|
37
|
-
* UI layout authoring (anchors/offsets/pivot/flex) — distinct from 3D
|
|
38
|
-
* `transform`. Optional so existing 3D adapters need not declare it. A UI
|
|
39
|
-
* adapter sets this true and implements {@link LayoutProvider}; the 3D gizmo /
|
|
40
|
-
* TransformProvider are NOT the UI manipulation path (spec K7).
|
|
41
|
-
*/
|
|
42
|
-
layout?: boolean;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
/**
|
|
46
42
|
* The adapter's own answer to "what is truth behind these rows, and how
|
|
47
|
-
* writable is it" (spec 29 §5
|
|
48
|
-
* 29-hierarchy-philosophy.md). Provenance never varies WITHIN one adapter's
|
|
43
|
+
* writable is it" (spec 29 §5). Provenance never varies WITHIN one adapter's
|
|
49
44
|
* rows — it is a property of the world boundary — so it is declared ONCE per
|
|
50
45
|
* adapter and rendered at the SEAM (the composite's `world:<id>` group row,
|
|
51
46
|
* or the panel header for a bare adapter), never per row. Only the adapter
|
|
52
47
|
* knows its truth: the shell must read this field, never guess from adapter
|
|
53
|
-
* identity (rule zero). Machine-readable operation gates stay in
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
48
|
+
* identity (rule zero). Machine-readable operation gates stay in {@link
|
|
49
|
+
* AuthoringCapabilities}/provider presence — this type carries the HUMAN
|
|
50
|
+
* explanation those gates can point at when an affordance is present but
|
|
51
|
+
* unavailable.
|
|
57
52
|
*/
|
|
58
53
|
export interface AuthoringProvenance {
|
|
59
54
|
/**
|
|
@@ -106,21 +101,16 @@ export interface EditorNode {
|
|
|
106
101
|
* as `<header>` beneath a React component name or a document's file path. */
|
|
107
102
|
secondaryLabel?: string;
|
|
108
103
|
kind: 'mesh' | 'light' | 'camera' | 'group' | 'object' | (string & {});
|
|
104
|
+
/**
|
|
105
|
+
* What to CALL this node's type in the inspector header — the component or
|
|
106
|
+
* class an author thinks of it as, when that differs from the native `kind`
|
|
107
|
+
* the shell uses to pick an icon. An `<Enemy>` renders a `group`, but "group"
|
|
108
|
+
* is a fact about its implementation, not its identity; Godot names the class
|
|
109
|
+
* here and Unity names the prefab. Adapters that omit it keep showing `kind`.
|
|
110
|
+
*/
|
|
111
|
+
typeLabel?: string;
|
|
109
112
|
parentId: string | null;
|
|
110
113
|
childIds: string[];
|
|
111
|
-
flags: {
|
|
112
|
-
runtimeOnly?: boolean;
|
|
113
|
-
locked?: boolean;
|
|
114
|
-
transformOwner?: TransformOwner;
|
|
115
|
-
/** D8 — member of a RootGroup, not the active one; children may be unloaded. */
|
|
116
|
-
inactiveRoot?: boolean;
|
|
117
|
-
/** Design state currently projected into the live authoring surface. */
|
|
118
|
-
active?: boolean;
|
|
119
|
-
/** The adapter resolved this boundary, but it is not editable here. */
|
|
120
|
-
readOnly?: boolean;
|
|
121
|
-
/** The adapter could not resolve the declared authoring boundary. */
|
|
122
|
-
error?: boolean;
|
|
123
|
-
};
|
|
124
114
|
}
|
|
125
115
|
|
|
126
116
|
export interface PropertyDescriptor {
|
|
@@ -130,25 +120,64 @@ export interface PropertyDescriptor {
|
|
|
130
120
|
readonly?: boolean;
|
|
131
121
|
options?: unknown[];
|
|
132
122
|
/**
|
|
133
|
-
* Optional domain-shaped grouping (T3.4 slice 2
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
123
|
+
* Optional domain-shaped grouping (T3.4 slice 2): properties sharing the
|
|
124
|
+
* same `group` label render together under a titled sub-section in the
|
|
125
|
+
* generic inspector, instead of the flat property grid. Backward-compatible
|
|
126
|
+
* — omitted (or two descriptors with different/no `group`) renders exactly
|
|
127
|
+
* as before this field existed. This is the ONLY authoring-richness
|
|
128
|
+
* addition v1 makes; there is no schema and no new PropertyDescriptor
|
|
129
|
+
* `type` — a custom adapter reports its own domain concepts
|
|
130
|
+
* (health/score/an enum, …) through the SAME provider, merely labeled into
|
|
131
|
+
* a named group.
|
|
142
132
|
*/
|
|
143
133
|
group?: string;
|
|
134
|
+
/**
|
|
135
|
+
* The value shown is the DECLARED DEFAULT — the document does not carry this
|
|
136
|
+
* property, the thing that owns it does. Every mainstream inspector
|
|
137
|
+
* distinguishes these (Unity greys an unmodified field and bolds an
|
|
138
|
+
* overridden one) because otherwise you cannot tell what a document actually
|
|
139
|
+
* says from what it merely inherits. Editing a defaulted property writes it
|
|
140
|
+
* into the document for the first time.
|
|
141
|
+
*/
|
|
142
|
+
defaulted?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* This property can be REMOVED from the document, reverting it to the
|
|
145
|
+
* declared default — Unity's revert arrow, Godot's reset arrow. Only set when
|
|
146
|
+
* removal is legal: the owner declares the property optional (or gives it a
|
|
147
|
+
* default), so dropping it leaves a still-valid document. The inverse of
|
|
148
|
+
* editing a `defaulted` property.
|
|
149
|
+
*/
|
|
150
|
+
resettable?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* The SOURCE TEXT of the default a revert returns this property to (`1.55`,
|
|
153
|
+
* `'raider'`), so the revert affordance can name its destination instead of
|
|
154
|
+
* saying "default" and hoping.
|
|
155
|
+
*
|
|
156
|
+
* Set ONLY when the owner actually declares a default the adapter can read.
|
|
157
|
+
* Absence is the honest, common case: an optional property with no declared
|
|
158
|
+
* default is still removable — what takes over is then the owner's own
|
|
159
|
+
* internal fallback or nothing at all, which this side does not know. Never
|
|
160
|
+
* fill it with the property's CURRENT value: that is what the author is
|
|
161
|
+
* discarding, not what they get back.
|
|
162
|
+
*/
|
|
163
|
+
revertsTo?: string;
|
|
144
164
|
}
|
|
145
165
|
|
|
146
166
|
export interface HierarchyProvider {
|
|
147
167
|
roots(): EditorNode[];
|
|
148
168
|
node(id: string): EditorNode | null;
|
|
149
|
-
/**
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
/**
|
|
170
|
+
* OPTIONAL, THREE-SPECIFIC — the live `Object3D` for raycast/gizmo binding
|
|
171
|
+
* (null for a node this adapter has no object for). A Pixi, DOM or React
|
|
172
|
+
* adapter has no `Object3D` at all: it OMITS this method rather than
|
|
173
|
+
* implementing a `() => null` stub that pretends the concept applies. Callers
|
|
174
|
+
* already treat `null` as "no object here", so absence and `null` mean the
|
|
175
|
+
* same thing to them — optional-chain it (`hierarchy.object3D?.(id) ?? null`).
|
|
176
|
+
*/
|
|
177
|
+
object3D?(id: string): THREE.Object3D | null;
|
|
178
|
+
/** OPTIONAL, THREE-SPECIFIC — inverse of {@link object3D}. Same rule: an
|
|
179
|
+
* adapter with no `Object3D` tree omits it. */
|
|
180
|
+
idForObject3D?(o: THREE.Object3D): string | null;
|
|
152
181
|
}
|
|
153
182
|
|
|
154
183
|
export interface SelectionProvider {
|
|
@@ -163,17 +192,13 @@ export interface SelectionProvider {
|
|
|
163
192
|
): SelectionResolution | null;
|
|
164
193
|
}
|
|
165
194
|
|
|
166
|
-
/** Adapter-owned result of semantic selection resolution
|
|
167
|
-
*
|
|
195
|
+
/** Adapter-owned result of semantic selection resolution: the raw native/render
|
|
196
|
+
* hit mapped to the node the author actually means. (The former
|
|
197
|
+
* `inspectorId`/`transformId`/`boundaryId` aliases were never read by the
|
|
198
|
+
* shell — deleted in WO-2.) */
|
|
168
199
|
export interface SelectionResolution {
|
|
169
|
-
/** Hierarchy selection and
|
|
200
|
+
/** Hierarchy selection and Inspector subject. */
|
|
170
201
|
id: string;
|
|
171
|
-
/** Optional alternate Inspector subject. Defaults to {@link id}. */
|
|
172
|
-
inspectorId?: string;
|
|
173
|
-
/** Native transform subject. `null` means the selection is non-spatial. */
|
|
174
|
-
transformId?: string | null;
|
|
175
|
-
/** Nearest closed component/prefab/assembly boundary, when any. */
|
|
176
|
-
boundaryId?: string | null;
|
|
177
202
|
}
|
|
178
203
|
|
|
179
204
|
export type TransformChannel = 'position' | 'rotation' | 'scale';
|
|
@@ -188,7 +213,6 @@ export interface TransformProvider {
|
|
|
188
213
|
/** Honest inspector presentation for the substrate's transform vocabulary. */
|
|
189
214
|
dimensions?(id: string): '2d' | '3d' | null;
|
|
190
215
|
get(id: string): Transform;
|
|
191
|
-
owner(id: string): TransformOwner;
|
|
192
216
|
/** Pre-gesture persistence check. Omitted means the adapter's transform
|
|
193
217
|
* capability applies uniformly. A false result must suppress manipulation
|
|
194
218
|
* before the live object is moved, never discover refusal at drag end. */
|
|
@@ -199,6 +223,17 @@ export interface TransformProvider {
|
|
|
199
223
|
endEdit(id: string): void;
|
|
200
224
|
}
|
|
201
225
|
|
|
226
|
+
/**
|
|
227
|
+
* One transform gesture event (see {@link AuthoringAdapter.observeTransforms}).
|
|
228
|
+
* `live` is a mid-gesture preview pose (the gizmo's live drag path); `commit`
|
|
229
|
+
* closes the gesture.
|
|
230
|
+
*/
|
|
231
|
+
export interface TransformObservation {
|
|
232
|
+
readonly id: string;
|
|
233
|
+
readonly transform: Transform;
|
|
234
|
+
readonly phase: 'live' | 'commit';
|
|
235
|
+
}
|
|
236
|
+
|
|
202
237
|
export interface InspectorProvider {
|
|
203
238
|
/** Schema-driven — NOT fixed to `SceneEntity`. */
|
|
204
239
|
properties(id: string): PropertyDescriptor[];
|
|
@@ -217,21 +252,33 @@ export interface InspectorProvider {
|
|
|
217
252
|
}
|
|
218
253
|
|
|
219
254
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
255
|
+
* An authored node that is itself an asset, even when its bytes are embedded
|
|
256
|
+
* in another source document instead of living at a standalone project path.
|
|
257
|
+
*
|
|
258
|
+
* The first concrete case is inline SVG in a DOM/React root: its `<path>` and
|
|
259
|
+
* `<g>` implementation is no more part of the authoring hierarchy than the
|
|
260
|
+
* meshes inside an imported model. The adapter owns the native-to-asset
|
|
261
|
+
* conversion; the shell only opens the returned image in its ordinary Asset
|
|
262
|
+
* Editor document.
|
|
225
263
|
*/
|
|
226
|
-
export interface
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
/**
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
264
|
+
export interface AuthoringAssetSubject {
|
|
265
|
+
readonly kind: 'image';
|
|
266
|
+
readonly name: string;
|
|
267
|
+
readonly mediaType: 'image/svg+xml';
|
|
268
|
+
readonly text: string;
|
|
269
|
+
/** Project-root-relative source definition, when the adapter can prove it. */
|
|
270
|
+
readonly sourcePath?: string;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export interface AssetSubjectProvider {
|
|
274
|
+
get(id: string): AuthoringAssetSubject | null;
|
|
275
|
+
/**
|
|
276
|
+
* Assets known to this authoring document even when they are not in its
|
|
277
|
+
* currently focused hierarchy projection. React story boards use this to
|
|
278
|
+
* expose SVGs mounted in inactive story frames without flattening those
|
|
279
|
+
* frames into the active hierarchy.
|
|
280
|
+
*/
|
|
281
|
+
entries?(): ReadonlyArray<{ id: string; subject: AuthoringAssetSubject }>;
|
|
235
282
|
}
|
|
236
283
|
|
|
237
284
|
export interface StructureProvider {
|
|
@@ -300,21 +347,18 @@ export interface StructureProvider {
|
|
|
300
347
|
}
|
|
301
348
|
|
|
302
349
|
/**
|
|
303
|
-
* PersistenceProvider — whether and where an adapter's edits persist (design
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
350
|
+
* PersistenceProvider — whether and where an adapter's edits persist (design).
|
|
351
|
+
* The ACTIVE adapter's provider is the ONE place this is decided — the host
|
|
352
|
+
* (editor) never hard-codes a destination or guards on session flags; it asks the
|
|
353
|
+
* provider.
|
|
307
354
|
*/
|
|
308
355
|
export interface PersistenceProvider {
|
|
309
356
|
isDirty(): boolean;
|
|
310
357
|
/** Save to the adapter's OWN source of truth (.vscn / overlay file / …). */
|
|
311
358
|
save(): Promise<void>;
|
|
312
|
-
serialize(): unknown;
|
|
313
|
-
/** Migration aid (first-party direction): pull a subtree into `.vscn` entities. */
|
|
314
|
-
captureToVscn?(ids: string[]): SceneEntity[];
|
|
315
359
|
/**
|
|
316
360
|
* Human/agent-readable destination this provider persists to — e.g.
|
|
317
|
-
* `"scenes/main.vscn.json"`, `"
|
|
361
|
+
* `"scenes/main.vscn.json"`, `"live-only (not saved)"`, or `"ephemeral
|
|
318
362
|
* (discarded on stop)"`. Drives the save-status UI and makes routing
|
|
319
363
|
* inspectable (design §2). Implementers whose destination can change during
|
|
320
364
|
* the session (e.g. the first-party provider tracks the loaded `.vscn` path)
|
|
@@ -378,43 +422,21 @@ export interface StoriesProvider {
|
|
|
378
422
|
isolate?(nodeId: string | null, storyId?: string): void;
|
|
379
423
|
}
|
|
380
424
|
|
|
381
|
-
/**
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
* types; this reads one type's per-entity data), or `null` when the entity
|
|
391
|
-
* or component doesn't exist. Returns a shallow copy — callers must write
|
|
392
|
-
* through `InspectorProvider.set('components.<Type>.<field>', v)`, never by
|
|
393
|
-
* mutating this. Absent ⇒ config is not readable through this provider
|
|
394
|
-
* (tools degrade to `config: null`).
|
|
395
|
-
*/
|
|
396
|
-
config?(nodeId: string, type: string): Record<string, unknown> | null;
|
|
425
|
+
/**
|
|
426
|
+
* Where a drop landed, when the shell knows. The VIEWPORT knows a world point
|
|
427
|
+
* (its ground-plane raycast under the cursor); the hierarchy panel does not,
|
|
428
|
+
* and passes nothing. Optional both ways: an adapter with no placement concept
|
|
429
|
+
* ignores it, and a caller with no point omits it — so neither side has to
|
|
430
|
+
* pretend. Coordinates are world-space, in the adapter's own units.
|
|
431
|
+
*/
|
|
432
|
+
export interface AssetDropContext {
|
|
433
|
+
readonly position?: readonly [number, number, number];
|
|
397
434
|
}
|
|
398
435
|
|
|
399
436
|
/** Asset drop (hierarchy + viewport). */
|
|
400
437
|
export interface AssetDropProvider {
|
|
401
438
|
accepts(nodeId: string, assetPath: string): boolean;
|
|
402
|
-
drop(nodeId: string, assetPath: string): void | Promise<void>;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/** One adapter-owned reusable-entity format the shared hierarchy can save.
|
|
406
|
-
* The adapter owns the bytes and semantics; the shell owns only the filename
|
|
407
|
-
* dialog. This keeps native 2D entity assets out of Three prefab code while
|
|
408
|
-
* preserving one editor language for the operation. */
|
|
409
|
-
export interface EntityAssetSaveDescriptor {
|
|
410
|
-
defaultFolder: string;
|
|
411
|
-
extension: string;
|
|
412
|
-
label: string;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
export interface EntityAssetProvider {
|
|
416
|
-
describeSave(nodeId: string): EntityAssetSaveDescriptor | null;
|
|
417
|
-
save(nodeId: string, path: string): Promise<boolean>;
|
|
439
|
+
drop(nodeId: string, assetPath: string, context?: AssetDropContext): void | Promise<void>;
|
|
418
440
|
}
|
|
419
441
|
|
|
420
442
|
/** Plain-object rect shape shared by {@link RectProvider} — the same fields a real
|
|
@@ -488,106 +510,6 @@ export interface BoxEditProvider {
|
|
|
488
510
|
referencePoint?(id: string): BoxEditReferencePoint | null;
|
|
489
511
|
}
|
|
490
512
|
|
|
491
|
-
export interface TilemapCell {
|
|
492
|
-
column: number;
|
|
493
|
-
row: number;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
/** One bounded native tile grid projected into the shared world overlay.
|
|
497
|
-
* Geometry is affine and host-relative: `origin` is local cell corner [0,0],
|
|
498
|
-
* while `columnStep`/`rowStep` are the transformed one-cell basis vectors.
|
|
499
|
-
* This compact shape represents nested rotation, non-uniform scale, reflection,
|
|
500
|
-
* and zoom without fabricating axis-aligned cell polygons. */
|
|
501
|
-
export interface TilemapEditGrid {
|
|
502
|
-
columns: number;
|
|
503
|
-
columnStep: { x: number; y: number };
|
|
504
|
-
origin: { x: number; y: number };
|
|
505
|
-
palette: ReadonlyArray<{ frame: string; index: number }>;
|
|
506
|
-
rows: number;
|
|
507
|
-
rowStep: { x: number; y: number };
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/** Native tile-cell authoring. Palette selection is editor-session state;
|
|
511
|
-
* source truth receives only atlas-frame indices, with -1 meaning an empty cell.
|
|
512
|
-
* begin/apply/end bracket one exact project-history transaction. */
|
|
513
|
-
export interface TilemapEditProvider {
|
|
514
|
-
apply(id: string, cell: TilemapCell): void;
|
|
515
|
-
begin(id: string, tileIndex: number): void;
|
|
516
|
-
/** Resolve a host-relative pointer through the adapter's native transform. */
|
|
517
|
-
cellAt(id: string, point: { x: number; y: number }): TilemapCell | null;
|
|
518
|
-
end(id: string): void;
|
|
519
|
-
grid(id: string): TilemapEditGrid | null;
|
|
520
|
-
/** Load the native Pixi atlas used to render palette thumbnails. */
|
|
521
|
-
loadTileset(id: string): Promise<SpriteAtlas2D>;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
export type PhysicsShape2DHandle = 'half-height' | 'half-width' | 'radius';
|
|
525
|
-
|
|
526
|
-
/** One native 2D physics shape projected into host-relative viewport pixels.
|
|
527
|
-
* `origin` is collider-local [0,0]; `xAxis`/`yAxis` are the transformed
|
|
528
|
-
* one-pixel basis vectors. Keeping the complete affine basis preserves nested
|
|
529
|
-
* rotation, non-uniform scale, reflection, centered canvas placement, and
|
|
530
|
-
* viewport zoom without teaching the shared shell about Pixi or Rapier. */
|
|
531
|
-
export interface PhysicsShape2DProjection {
|
|
532
|
-
collider:
|
|
533
|
-
| { shape: 'cuboid'; halfExtents: readonly [number, number] }
|
|
534
|
-
| { shape: 'ball'; radius: number }
|
|
535
|
-
| { shape: 'capsule'; halfHeight: number; radius: number };
|
|
536
|
-
editable: boolean;
|
|
537
|
-
id: string;
|
|
538
|
-
origin: { x: number; y: number };
|
|
539
|
-
sensor: boolean;
|
|
540
|
-
xAxis: { x: number; y: number };
|
|
541
|
-
yAxis: { x: number; y: number };
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
/** Adapter-owned native collider inspection and spatial editing. Shapes are
|
|
545
|
-
* readable independently of editability so locked/read-only source remains
|
|
546
|
-
* inspectable. begin/apply/end bracket one exact project-history transaction. */
|
|
547
|
-
export interface PhysicsShape2DProvider {
|
|
548
|
-
apply(id: string, handle: PhysicsShape2DHandle, point: { x: number; y: number }): void;
|
|
549
|
-
begin(id: string, handle: PhysicsShape2DHandle): void;
|
|
550
|
-
end(id: string): void;
|
|
551
|
-
shapes(): readonly PhysicsShape2DProjection[];
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/** One named sub-texture in a Pixi-compatible spritesheet atlas. `x`/`y` are
|
|
555
|
-
* source-image pixels; width/height are the runtime-facing dimensions Pixi
|
|
556
|
-
* swaps when `rotated` reports packed 90-degree storage. */
|
|
557
|
-
export interface SpriteAtlasFrame2D {
|
|
558
|
-
height: number;
|
|
559
|
-
name: string;
|
|
560
|
-
rotated: boolean;
|
|
561
|
-
width: number;
|
|
562
|
-
x: number;
|
|
563
|
-
y: number;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
export interface SpriteAtlas2D {
|
|
567
|
-
atlasPath: string;
|
|
568
|
-
frames: readonly SpriteAtlasFrame2D[];
|
|
569
|
-
/** Project-relative image source named by the atlas. */
|
|
570
|
-
imagePath: string;
|
|
571
|
-
/** Image bytes loaded through the owning adapter's storage backend. */
|
|
572
|
-
imageBytes: Uint8Array;
|
|
573
|
-
imageMediaType: string;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
export interface SpriteFrames2DDescriptor {
|
|
577
|
-
atlasPath: string;
|
|
578
|
-
editable: boolean;
|
|
579
|
-
selectedFrame: string | null;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
/** Source-backed Pixi spritesheet discovery and frame selection. Atlas parsing
|
|
583
|
-
* remains adapter-owned; the universal Inspector only renders this projection.
|
|
584
|
-
* `select` writes source truth and creates one project-history transaction. */
|
|
585
|
-
export interface SpriteFrames2DProvider {
|
|
586
|
-
describe(id: string): SpriteFrames2DDescriptor | null;
|
|
587
|
-
load(id: string): Promise<SpriteAtlas2D>;
|
|
588
|
-
select(id: string, frame: string | null): void;
|
|
589
|
-
}
|
|
590
|
-
|
|
591
513
|
/** Text-content editing — currently OFF-contract (the react adapter has an `editText(id)`
|
|
592
514
|
* method, react-world-authoring-adapter.ts:728, not reachable via the contract). Bring it
|
|
593
515
|
* on so the overlay's double-click-to-edit-text (D3) is contract-driven. */
|
|
@@ -616,6 +538,27 @@ export interface ColorSampleProvider {
|
|
|
616
538
|
backgroundChainAt(clientX: number, clientY: number): string[] | null;
|
|
617
539
|
}
|
|
618
540
|
|
|
541
|
+
/**
|
|
542
|
+
* WO-IDX1 — where a node's live object was CREATED in the game's own source.
|
|
543
|
+
*
|
|
544
|
+
* `display` is the `file:line` the editor shows verbatim. The unanchored shape
|
|
545
|
+
* carries a `reason` rather than a blank, because "we don't know" and "a
|
|
546
|
+
* library built it" are different facts and neither may present as silence
|
|
547
|
+
* (the honest-floor rule).
|
|
548
|
+
*/
|
|
549
|
+
export type NodeCreationSite =
|
|
550
|
+
| { anchored: true; file: string; line: number; col: number; display: string }
|
|
551
|
+
| { anchored: false; reason: string };
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Absent ⇒ this adapter's substrate has no creation-site index at all (a
|
|
555
|
+
* document-backed world's truth is the document, not a construction site).
|
|
556
|
+
* Present ⇒ every id gets an answer, anchored or reasoned.
|
|
557
|
+
*/
|
|
558
|
+
export interface CreationSiteProvider {
|
|
559
|
+
anchor(id: string): NodeCreationSite;
|
|
560
|
+
}
|
|
561
|
+
|
|
619
562
|
export interface AuthoringAdapter {
|
|
620
563
|
readonly capabilities: AuthoringCapabilities;
|
|
621
564
|
/** Spec 29 §5 — seam-level provenance ("what is truth behind these rows").
|
|
@@ -626,8 +569,8 @@ export interface AuthoringAdapter {
|
|
|
626
569
|
readonly selection?: SelectionProvider;
|
|
627
570
|
readonly transforms?: TransformProvider;
|
|
628
571
|
readonly inspector?: InspectorProvider;
|
|
629
|
-
/**
|
|
630
|
-
readonly
|
|
572
|
+
/** The selected node is an atomic asset that can open in the Asset Editor. */
|
|
573
|
+
readonly assetSubject?: AssetSubjectProvider;
|
|
631
574
|
readonly structure?: StructureProvider;
|
|
632
575
|
readonly persistence?: PersistenceProvider;
|
|
633
576
|
/** D8 — mutually-exclusive root groups (radio roots). Absent ⇒ no grouped roots. */
|
|
@@ -642,12 +585,6 @@ export interface AuthoringAdapter {
|
|
|
642
585
|
/** T0 (spec 27 §2) — spatial drag-resize/move → source/data write for non-Object3D
|
|
643
586
|
* (DOM) nodes. Absent ⇒ no box-edit gesture for this adapter. */
|
|
644
587
|
readonly boxEdit?: BoxEditProvider;
|
|
645
|
-
/** Native 2D physics debug geometry and shape handles. */
|
|
646
|
-
readonly physicsShapes2D?: PhysicsShape2DProvider;
|
|
647
|
-
/** Native Pixi spritesheet frame discovery/selection. */
|
|
648
|
-
readonly spriteFrames2D?: SpriteFrames2DProvider;
|
|
649
|
-
/** Native bounded tile-cell painting. Absent means no viewport tile editor. */
|
|
650
|
-
readonly tilemapEdit?: TilemapEditProvider;
|
|
651
588
|
/** T0 (spec 27 §2) — double-click-to-edit-text on the contract (wires the react
|
|
652
589
|
* adapter's existing off-contract `editText`). Absent ⇒ no in-place text edit. */
|
|
653
590
|
readonly text?: TextProvider;
|
|
@@ -656,16 +593,21 @@ export interface AuthoringAdapter {
|
|
|
656
593
|
readonly colorSample?: ColorSampleProvider;
|
|
657
594
|
/** D4 — storybook stories. Absent ⇒ no stories for any node in this adapter. */
|
|
658
595
|
readonly stories?: StoriesProvider;
|
|
659
|
-
/**
|
|
660
|
-
|
|
596
|
+
/** WO-IDX1 — the source location that constructed a node's live object.
|
|
597
|
+
* Absent ⇒ this adapter indexes no creation sites. */
|
|
598
|
+
readonly creationSite?: CreationSiteProvider;
|
|
661
599
|
/** Asset drop (hierarchy + viewport). Absent ⇒ this adapter accepts no drops. */
|
|
662
600
|
readonly assetDrop?: AssetDropProvider;
|
|
663
|
-
/** Save a native entity subtree as an adapter-owned reusable asset. */
|
|
664
|
-
readonly entityAssets?: EntityAssetProvider;
|
|
665
|
-
/** Undo/redo for this adapter's edits (first-party delegates to the store; an
|
|
666
|
-
* ingest adapter keeps its own overlay-backed stack). Absent ⇒ host falls back. */
|
|
667
|
-
undo?(): void;
|
|
668
|
-
redo?(): void;
|
|
669
601
|
/** Change notification → UI refresh. */
|
|
670
602
|
subscribe?(listener: () => void): () => void;
|
|
603
|
+
/**
|
|
604
|
+
* Observe transform GESTURES on this adapter's nodes — the one seam the
|
|
605
|
+
* animation sequencer needs to record a drag as keyframes and to refresh its
|
|
606
|
+
* design baselines. Deliberately narrow (one event shape, one consumer, no
|
|
607
|
+
* general event bus): before it existed, the sequencer could only observe
|
|
608
|
+
* the first-party store's own `.vscn` transform path, so a live or
|
|
609
|
+
* source-backed world's gestures were invisible to it. Absent ⇒ this
|
|
610
|
+
* adapter reports no gestures.
|
|
611
|
+
*/
|
|
612
|
+
observeTransforms?(listener: (event: TransformObservation) => void): () => void;
|
|
671
613
|
}
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
NetConditioning,
|
|
20
20
|
NetMessageEvent,
|
|
21
21
|
NetPeer,
|
|
22
|
+
NetPlayerIdentity,
|
|
22
23
|
NetRates,
|
|
23
24
|
NetworkingAdapter,
|
|
24
25
|
ReplicationStats,
|
|
@@ -31,6 +32,16 @@ export interface ColyseusNetworkingConfig {
|
|
|
31
32
|
localSessionId(): string | null;
|
|
32
33
|
/** Peers currently in the room. */
|
|
33
34
|
peers(): NetPeer[];
|
|
35
|
+
/**
|
|
36
|
+
* The game's own `id → Object3D` map (P-4). The SEAM
|
|
37
|
+
* (`NetworkingAdapter`) is keyed by node id so a non-three world can
|
|
38
|
+
* implement it at all; the accessors below stay `Object3D`-keyed because a
|
|
39
|
+
* three game already thinks in objects. This one member is the bridge, and
|
|
40
|
+
* it is the game's to declare — the engine never guesses an identity
|
|
41
|
+
* convention on a game's behalf. Returning `null` means "no such node here",
|
|
42
|
+
* and every accessor degrades exactly as it does for an unreplicated object.
|
|
43
|
+
*/
|
|
44
|
+
resolve(nodeId: string): THREE.Object3D | null;
|
|
34
45
|
/** Network id for an object, or null if it is not replicated (→ local authored). */
|
|
35
46
|
networkId(o: THREE.Object3D): string | null;
|
|
36
47
|
/** Owning peer/session id for a replicated object (null = server-owned). */
|
|
@@ -65,19 +76,36 @@ export interface ColyseusNetworkingConfig {
|
|
|
65
76
|
* must be supplied together for the inspector to show the controls. */
|
|
66
77
|
getConditioning?(): NetConditioning;
|
|
67
78
|
setConditioning?(c: NetConditioning): void;
|
|
79
|
+
/** Optional capability: read the local player's identity off the game's own
|
|
80
|
+
* room state (→ adapter `getPlayerIdentity`, a seat label). Independent of
|
|
81
|
+
* the setter below — a game can expose a read-only identity. */
|
|
82
|
+
playerIdentity?(): NetPlayerIdentity | undefined;
|
|
83
|
+
/** Optional capability: set the local player's identity through the game's own
|
|
84
|
+
* mechanism (→ adapter `setPlayerIdentity`, an editable name field). Present
|
|
85
|
+
* only when the game supports a settable name; drives the game's real path
|
|
86
|
+
* (a join option the server honours, a rename message, …). */
|
|
87
|
+
setPlayerIdentity?(identity: NetPlayerIdentity): void | Promise<void>;
|
|
68
88
|
}
|
|
69
89
|
|
|
70
90
|
export function createColyseusNetworkingAdapter(cfg: ColyseusNetworkingConfig): NetworkingAdapter {
|
|
71
91
|
const adapter: NetworkingAdapter = {
|
|
72
92
|
peers: () => cfg.peers(),
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
93
|
+
// The seam speaks node ids; `cfg.resolve` is the one crossing into Three's
|
|
94
|
+
// vocabulary. An unresolvable id behaves exactly like an unreplicated
|
|
95
|
+
// object — local authority, editable — rather than throwing or fabricating.
|
|
96
|
+
networkId: (nodeId) => {
|
|
97
|
+
const o = cfg.resolve(nodeId);
|
|
98
|
+
return o ? cfg.networkId(o) : null;
|
|
99
|
+
},
|
|
100
|
+
authority: (nodeId) => {
|
|
101
|
+
const o = cfg.resolve(nodeId);
|
|
102
|
+
if (!o || cfg.networkId(o) == null) return 'local'; // not replicated → local authored
|
|
76
103
|
if (cfg.serverAuthoritative?.(o)) return 'server';
|
|
77
104
|
return cfg.ownerId(o) === cfg.localSessionId() ? 'local' : 'remote';
|
|
78
105
|
},
|
|
79
|
-
editable: (
|
|
80
|
-
|
|
106
|
+
editable: (nodeId) => {
|
|
107
|
+
const o = cfg.resolve(nodeId);
|
|
108
|
+
if (!o || cfg.networkId(o) == null) return true; // local authored object
|
|
81
109
|
if (cfg.serverAuthoritative?.(o)) return false; // server owns it → inspect-only
|
|
82
110
|
return cfg.ownerId(o) === cfg.localSessionId(); // only your own peer's objects
|
|
83
111
|
},
|
|
@@ -102,5 +130,16 @@ export function createColyseusNetworkingAdapter(cfg: ColyseusNetworkingConfig):
|
|
|
102
130
|
adapter.getConditioning = () => getConditioning();
|
|
103
131
|
adapter.setConditioning = (c) => setConditioning(c);
|
|
104
132
|
}
|
|
133
|
+
// Identity read and write are INDEPENDENTLY optional: a game may expose a
|
|
134
|
+
// read-only seat label (identity, no setter) or a settable name (both). The
|
|
135
|
+
// editor shows an editable field only when `setPlayerIdentity` is present.
|
|
136
|
+
const playerIdentity = cfg.playerIdentity?.bind(cfg);
|
|
137
|
+
if (playerIdentity) {
|
|
138
|
+
adapter.getPlayerIdentity = () => playerIdentity() ?? {};
|
|
139
|
+
}
|
|
140
|
+
const setPlayerIdentity = cfg.setPlayerIdentity?.bind(cfg);
|
|
141
|
+
if (setPlayerIdentity) {
|
|
142
|
+
adapter.setPlayerIdentity = (id) => setPlayerIdentity(id);
|
|
143
|
+
}
|
|
105
144
|
return adapter;
|
|
106
145
|
}
|