@vgai/engine 0.5.10 → 0.5.12
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 -5
- package/dist/adapter/authoring.d.ts +51 -28
- package/dist/adapter/authoring.d.ts.map +1 -1
- package/dist/adapter/body-marks.d.ts +59 -0
- package/dist/adapter/body-marks.d.ts.map +1 -0
- package/dist/adapter/body-marks.js +62 -0
- package/dist/adapter/constraint.d.ts +41 -0
- package/dist/adapter/constraint.d.ts.map +1 -0
- package/dist/adapter/constraint.js +27 -0
- package/dist/adapter/hierarchy-marks.d.ts +138 -0
- package/dist/adapter/hierarchy-marks.d.ts.map +1 -0
- package/dist/adapter/hierarchy-marks.js +148 -0
- package/dist/adapter/index.d.ts +1 -1
- package/dist/adapter/index.d.ts.map +1 -1
- package/dist/adapter/ingest/contract-system-adapters.d.ts +64 -0
- package/dist/adapter/ingest/contract-system-adapters.d.ts.map +1 -0
- package/dist/adapter/ingest/contract-system-adapters.js +134 -0
- package/dist/adapter/ingest/game-contract.d.ts +89 -5
- package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
- package/dist/adapter/ingest/game-contract.js +11 -3
- package/dist/adapter/ingest/scene-capture.d.ts +31 -4
- package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
- package/dist/adapter/ingest/scene-capture.js +29 -7
- package/dist/adapter/ingest/structural-ids.d.ts +1 -1
- package/dist/adapter/ingest/structural-ids.js +2 -2
- package/dist/adapter/reflection-probe.d.ts +53 -0
- package/dist/adapter/reflection-probe.d.ts.map +1 -0
- package/dist/adapter/reflection-probe.js +18 -0
- package/dist/asset-formats/index.d.ts +4 -10
- package/dist/asset-formats/index.d.ts.map +1 -1
- package/dist/asset-formats/index.js +3 -8
- package/dist/asset-formats/material.d.ts +0 -66
- package/dist/asset-formats/material.d.ts.map +1 -1
- package/dist/asset-formats/material.js +0 -8
- package/dist/asset-formats/mesh.d.ts +1 -2
- package/dist/asset-formats/mesh.d.ts.map +1 -1
- package/dist/asset-formats/mesh.js +3 -13
- package/dist/asset-formats/render-env.d.ts +2 -2
- package/dist/asset-parse-error.d.ts +4 -5
- package/dist/asset-parse-error.d.ts.map +1 -1
- package/dist/asset-parse-error.js +4 -5
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -0
- package/dist/data/curve.d.ts +116 -0
- package/dist/data/curve.d.ts.map +1 -0
- package/dist/data/curve.js +230 -0
- package/dist/data/vite-plugin-data.d.ts +2 -2
- package/dist/data/vite-plugin-data.d.ts.map +1 -1
- package/dist/data/vite-plugin-data.js +24 -15
- package/dist/dev/performance-profiler.d.ts +1 -0
- package/dist/dev/performance-profiler.d.ts.map +1 -1
- package/dist/dev/performance-profiler.js +25 -2
- package/dist/dev/register-render-vitals.d.ts +18 -7
- package/dist/dev/register-render-vitals.d.ts.map +1 -1
- package/dist/dev/register-render-vitals.js +35 -8
- package/dist/ecs/user-data.d.ts +49 -1
- package/dist/ecs/user-data.d.ts.map +1 -1
- package/dist/ecs/user-data.js +34 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/manifest/index.d.ts +3 -3
- package/dist/manifest/index.d.ts.map +1 -1
- package/dist/manifest/index.js +2 -2
- package/dist/manifest/load.d.ts +20 -63
- package/dist/manifest/load.d.ts.map +1 -1
- package/dist/manifest/load.js +12 -103
- package/dist/manifest/schema.d.ts +21 -103
- package/dist/manifest/schema.d.ts.map +1 -1
- package/dist/manifest/schema.js +66 -142
- package/dist/pixi/index.d.ts +2 -3
- package/dist/pixi/index.d.ts.map +1 -1
- package/dist/pixi/index.js +2 -3
- package/dist/pixi/ingest.d.ts +7 -75
- package/dist/pixi/ingest.d.ts.map +1 -1
- package/dist/pixi/ingest.js +6 -14
- package/dist/pixi/scene-capture.d.ts +2 -2
- package/dist/pixi/scene-capture.js +2 -2
- package/dist/react/use-data.d.ts +10 -5
- package/dist/react/use-data.d.ts.map +1 -1
- package/dist/react/use-data.js +15 -15
- package/dist/render/viewport-shading.d.ts +1 -1
- package/dist/render/viewport-shading.d.ts.map +1 -1
- package/dist/render/viewport-shading.js +14 -0
- package/dist/runtime/create-runtime.d.ts +12 -0
- package/dist/runtime/create-runtime.d.ts.map +1 -1
- package/dist/runtime/create-runtime.js +7 -2
- package/dist/runtime/mount-game.d.ts +1 -1
- package/dist/runtime/mount-game.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.d.ts.map +1 -1
- package/dist/runtime/mount-manifest.js +5 -1
- package/dist/world3d-react/r3f-adapter.d.ts.map +1 -1
- package/dist/world3d-react/r3f-adapter.js +129 -6
- package/dist/world3d-react/renderer-config.d.ts +15 -0
- package/dist/world3d-react/renderer-config.d.ts.map +1 -1
- package/dist/world3d-react/renderer-config.js +15 -0
- package/dist/world3d-react/world-context.d.ts +11 -1
- package/dist/world3d-react/world-context.d.ts.map +1 -1
- package/dist/world3d-react/world-context.js +1 -0
- package/dist-config/config.js +140 -20
- package/dist-config/config.js.map +4 -4
- package/package.json +1 -1
- package/schemas/engine-capabilities.json +10 -10
- package/schemas/vgai-project.schema.json +22 -73
- package/src/adapter/authoring.ts +54 -31
- package/src/adapter/body-marks.ts +69 -0
- package/src/adapter/constraint.ts +71 -0
- package/src/adapter/hierarchy-marks.ts +155 -0
- package/src/adapter/index.ts +2 -3
- package/src/adapter/ingest/contract-system-adapters.ts +174 -0
- package/src/adapter/ingest/game-contract.ts +104 -6
- package/src/adapter/ingest/scene-capture.ts +61 -11
- package/src/adapter/ingest/structural-ids.ts +2 -2
- package/src/adapter/reflection-probe.ts +75 -0
- package/src/asset-formats/index.ts +4 -10
- package/src/asset-formats/material.ts +0 -11
- package/src/asset-formats/mesh.ts +3 -16
- package/src/asset-parse-error.ts +4 -5
- package/src/config.ts +13 -0
- package/src/data/curve.ts +263 -0
- package/src/data/vite-plugin-data.ts +48 -14
- package/src/dev/performance-profiler.ts +21 -2
- package/src/dev/register-render-vitals.ts +38 -8
- package/src/ecs/user-data.ts +50 -0
- package/src/index.ts +2 -3
- package/src/manifest/index.ts +3 -15
- package/src/manifest/load.ts +33 -139
- package/src/manifest/schema.ts +72 -181
- package/src/pixi/index.ts +2 -15
- package/src/pixi/ingest.ts +13 -87
- package/src/pixi/scene-capture.ts +2 -2
- package/src/react/use-data.ts +15 -20
- package/src/render/viewport-shading.ts +16 -1
- package/src/runtime/create-runtime.ts +30 -5
- package/src/runtime/mount-game.ts +1 -1
- package/src/runtime/mount-manifest.ts +5 -1
- package/src/world3d-react/r3f-adapter.tsx +142 -6
- package/src/world3d-react/renderer-config.ts +31 -0
- package/src/world3d-react/world-context.ts +11 -1
- package/dist/asset-formats/instances.d.ts +0 -17
- package/dist/asset-formats/instances.d.ts.map +0 -1
- package/dist/asset-formats/instances.js +0 -31
- package/dist/asset-formats/parse.d.ts +0 -20
- package/dist/asset-formats/parse.d.ts.map +0 -1
- package/dist/asset-formats/parse.js +0 -34
- package/dist/pixi/ingest-iframe.d.ts +0 -82
- package/dist/pixi/ingest-iframe.d.ts.map +0 -1
- package/dist/pixi/ingest-iframe.js +0 -166
- package/dist/render/instance-mesh.d.ts +0 -5
- package/dist/render/instance-mesh.d.ts.map +0 -1
- package/dist/render/instance-mesh.js +0 -19
- package/schemas/mat.schema.json +0 -255
- package/src/asset-formats/instances.ts +0 -38
- package/src/asset-formats/parse.ts +0 -39
- package/src/pixi/ingest-iframe.ts +0 -255
- package/src/render/instance-mesh.ts +0 -25
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curve FIELDS in data assets — a response curve a designer shapes, living
|
|
3
|
+
* inside an ordinary `.data.json` like any other tunable value.
|
|
4
|
+
*
|
|
5
|
+
* A curve is a FIELD TYPE, never a file type: there is no `.curve.json`, no
|
|
6
|
+
* curve asset class, no baked LUT, and no document. `curve(axes)` is a real
|
|
7
|
+
* Zod schema you drop into a `*.schema.ts` beside `z.number()`, so the value
|
|
8
|
+
* lives in the same data file, hot-swaps through the same `defineData` handle,
|
|
9
|
+
* and is edited by the same Data panel that edits everything else.
|
|
10
|
+
*
|
|
11
|
+
* The JSON a designer (or an agent) reads and writes:
|
|
12
|
+
*
|
|
13
|
+
* ```json
|
|
14
|
+
* { "interpolation": "smooth",
|
|
15
|
+
* "points": [{ "x": 0, "y": 1 }, { "x": 12, "y": 0.85 }, { "x": 30, "y": 0.2 }] }
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* TWO MODES, NO TANGENTS. `linear` and `smooth`, and nothing else. Per-key
|
|
19
|
+
* tangent handles would double the JSON, make it unreadable to an agent, and
|
|
20
|
+
* hand every author a way to make a curve overshoot its own points. Smoothness
|
|
21
|
+
* is DERIVED instead — Fritsch–Carlson monotone cubic tangents, computed from
|
|
22
|
+
* the points themselves — so the interpolant is guaranteed to stay inside each
|
|
23
|
+
* segment's `[yLeft, yRight]` band. "Shape it by moving points" is the whole
|
|
24
|
+
* authoring model.
|
|
25
|
+
*
|
|
26
|
+
* RUNG AUDIT (recorded decision, 2026-08-14): no npm dependency. The candidate
|
|
27
|
+
* packages (`monotone-cubic-spline` and its siblings) are unmaintained
|
|
28
|
+
* micro-packages, and d3's `curveMonotoneX` is an SVG PATH GENERATOR — it
|
|
29
|
+
* draws, it does not sample `y` at an `x`. What we need is ~40 lines of
|
|
30
|
+
* well-known numerics with no API surface, so it is written here.
|
|
31
|
+
*
|
|
32
|
+
* `sampleCurve` is the entire runtime: a pure function, no class, no cache
|
|
33
|
+
* object, no "curve system". Game code calls it every frame from its own
|
|
34
|
+
* update, the same way it reads any other tuning value.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { z } from 'zod';
|
|
38
|
+
|
|
39
|
+
/** One authored key: a `y` value at an `x` position. No tangents — see above. */
|
|
40
|
+
export interface CurvePoint {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** A curve field's value, exactly as it sits in the `.data.json`. */
|
|
46
|
+
export interface CurveValue {
|
|
47
|
+
interpolation: 'linear' | 'smooth';
|
|
48
|
+
points: CurvePoint[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The curve's DOMAIN, declared by the engineer in the schema (never by the
|
|
53
|
+
* data): what the axes mean and how far they run. The editor draws its rails
|
|
54
|
+
* from this and clamps every drag to it; the schema rejects a point outside
|
|
55
|
+
* it.
|
|
56
|
+
*/
|
|
57
|
+
export interface CurveAxes {
|
|
58
|
+
x: { min: number; max: number; label?: string };
|
|
59
|
+
y: { min: number; max: number; label?: string };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** How the marker rides out to the emitted JSON Schema — the editor's and the dev menu's detection key, the exact shape `dataRef` uses with `x-vgai-ref`. */
|
|
63
|
+
const CURVE_META_KEY = 'x-vgai-curve';
|
|
64
|
+
|
|
65
|
+
function axisLabel(axes: CurveAxes, axis: 'x' | 'y'): string {
|
|
66
|
+
return axes[axis].label ?? axis;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Every out-of-domain / ordering problem in one pass, as teaching issues (`parseDataJson`'s "errors teach" doctrine, §6.5). */
|
|
70
|
+
function checkPoints(value: CurveValue, axes: CurveAxes, ctx: z.RefinementCtx): void {
|
|
71
|
+
const points = value.points;
|
|
72
|
+
for (let i = 0; i < points.length; i++) {
|
|
73
|
+
const point = points[i] as CurvePoint;
|
|
74
|
+
for (const axis of ['x', 'y'] as const) {
|
|
75
|
+
const { min, max } = axes[axis];
|
|
76
|
+
if (point[axis] < min || point[axis] > max) {
|
|
77
|
+
ctx.addIssue({
|
|
78
|
+
code: 'custom',
|
|
79
|
+
path: ['points', i, axis],
|
|
80
|
+
message:
|
|
81
|
+
`${point[axis]} is outside this curve's ${axisLabel(axes, axis)} domain ` +
|
|
82
|
+
`[${min}, ${max}] — move the point inside the domain, or widen the axis in the ` +
|
|
83
|
+
`schema (curve({ ${axis}: { min, max } })).`,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (i === 0) continue;
|
|
88
|
+
const previous = points[i - 1] as CurvePoint;
|
|
89
|
+
if (point.x <= previous.x) {
|
|
90
|
+
ctx.addIssue({
|
|
91
|
+
code: 'custom',
|
|
92
|
+
path: ['points', i, 'x'],
|
|
93
|
+
message:
|
|
94
|
+
`${point.x} must be strictly greater than the previous point's x (${previous.x}) — ` +
|
|
95
|
+
'curve points run left to right along x, with no duplicate x. Sort the points, or ' +
|
|
96
|
+
'nudge this one.',
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* A Zod schema for a data-asset FIELD holding a response curve. Use it in a
|
|
104
|
+
* `.schema.ts` exactly like any other field:
|
|
105
|
+
*
|
|
106
|
+
* ```ts
|
|
107
|
+
* export const TuningSchema = z.object({
|
|
108
|
+
* spawnRate: z.number().min(0).max(10).default(1),
|
|
109
|
+
* difficultyRamp: curve({
|
|
110
|
+
* x: { min: 0, max: 300, label: 'Sim time (s)' },
|
|
111
|
+
* y: { min: 0, max: 2, label: 'Spawn multiplier' },
|
|
112
|
+
* }).describe('How hard the game leans on the player as the round runs.'),
|
|
113
|
+
* });
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* The schema is STRICT (an unrecognized key is an error, never a silent
|
|
117
|
+
* strip), demands at least two points, and enforces strictly-increasing `x`
|
|
118
|
+
* with every point inside the declared axes — each with a message that names
|
|
119
|
+
* the fix rather than the rule.
|
|
120
|
+
*
|
|
121
|
+
* The emitted JSON Schema (`toDataJsonSchema`) carries
|
|
122
|
+
* `"x-vgai-curve": axes` — a Zod `.meta()`, which survives `z.toJSONSchema`
|
|
123
|
+
* including inside a table's `additionalProperties` row schema, exactly the
|
|
124
|
+
* way `dataRef`'s `"x-vgai-ref"` does. That marker is what makes the editor's
|
|
125
|
+
* Data panel render a curve EDITOR instead of a raw-JSON cell, with no emitter
|
|
126
|
+
* changes anywhere.
|
|
127
|
+
*/
|
|
128
|
+
export function curve(axes: CurveAxes): z.ZodType<CurveValue> {
|
|
129
|
+
const point = z.strictObject({
|
|
130
|
+
x: z.number().describe(`Position along ${axisLabel(axes, 'x')}.`),
|
|
131
|
+
y: z.number().describe(`Value at that position, along ${axisLabel(axes, 'y')}.`),
|
|
132
|
+
});
|
|
133
|
+
return z
|
|
134
|
+
.strictObject({
|
|
135
|
+
interpolation: z
|
|
136
|
+
.enum(['linear', 'smooth'])
|
|
137
|
+
.describe(
|
|
138
|
+
'How the value moves between points: "linear" for straight segments, ' +
|
|
139
|
+
'"smooth" for a monotone cubic that never overshoots them.',
|
|
140
|
+
),
|
|
141
|
+
points: z
|
|
142
|
+
.array(point)
|
|
143
|
+
.min(2, {
|
|
144
|
+
message:
|
|
145
|
+
'a curve needs at least two points — add another { "x": …, "y": … } so the ' +
|
|
146
|
+
'value has somewhere to travel between.',
|
|
147
|
+
})
|
|
148
|
+
.describe('Keys in increasing x order; the value is clamped outside the first/last x.'),
|
|
149
|
+
})
|
|
150
|
+
.superRefine((value, ctx) => checkPoints(value, axes, ctx))
|
|
151
|
+
.meta({ [CURVE_META_KEY]: axes }) as unknown as z.ZodType<CurveValue>;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Read a curve field's declared axes back off a LIVE Zod schema (as opposed to
|
|
156
|
+
* the emitted JSON Schema, which the editor's Data panel reads instead).
|
|
157
|
+
*
|
|
158
|
+
* This is the dev-menu capability's detection door: it derives its rows by
|
|
159
|
+
* walking the game's own `TuningSchema` object, so it needs the axes from the
|
|
160
|
+
* schema in hand. Wrappers (`.optional()`, `.default(…)`, `.describe(…)` chains
|
|
161
|
+
* that re-wrap) hide the metadata behind an inner type, so a bounded unwrap
|
|
162
|
+
* walks down to it.
|
|
163
|
+
*/
|
|
164
|
+
export function curveAxesOf(schema: z.ZodType): CurveAxes | undefined {
|
|
165
|
+
let current: unknown = schema;
|
|
166
|
+
for (let depth = 0; depth < 5 && current; depth++) {
|
|
167
|
+
const meta = (current as z.ZodType).meta?.() as Record<string, unknown> | undefined;
|
|
168
|
+
const axes = meta?.[CURVE_META_KEY];
|
|
169
|
+
if (axes && typeof axes === 'object' && 'x' in axes && 'y' in axes) return axes as CurveAxes;
|
|
170
|
+
current = (current as { def?: { innerType?: unknown } }).def?.innerType;
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Fritsch–Carlson tangents: the ordinary cubic-Hermite slopes, then limited so
|
|
177
|
+
* the interpolant cannot overshoot the data. This is what makes "smooth" safe
|
|
178
|
+
* to hand a designer — a curve that dips below its own points would silently
|
|
179
|
+
* feed the game a value nobody authored.
|
|
180
|
+
*/
|
|
181
|
+
function monotoneTangents(points: readonly CurvePoint[]): number[] {
|
|
182
|
+
const n = points.length;
|
|
183
|
+
const secants: number[] = [];
|
|
184
|
+
for (let i = 0; i < n - 1; i++) {
|
|
185
|
+
const left = points[i] as CurvePoint;
|
|
186
|
+
const right = points[i + 1] as CurvePoint;
|
|
187
|
+
secants.push((right.y - left.y) / (right.x - left.x));
|
|
188
|
+
}
|
|
189
|
+
const tangents: number[] = [secants[0] as number];
|
|
190
|
+
for (let i = 1; i < n - 1; i++) {
|
|
191
|
+
tangents.push(((secants[i - 1] as number) + (secants[i] as number)) / 2);
|
|
192
|
+
}
|
|
193
|
+
tangents.push(secants[n - 2] as number);
|
|
194
|
+
|
|
195
|
+
for (let i = 0; i < n - 1; i++) {
|
|
196
|
+
const secant = secants[i] as number;
|
|
197
|
+
if (secant === 0) {
|
|
198
|
+
// A flat segment must stay flat: any slope at either end would bulge.
|
|
199
|
+
tangents[i] = 0;
|
|
200
|
+
tangents[i + 1] = 0;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
let alpha = (tangents[i] as number) / secant;
|
|
204
|
+
let beta = (tangents[i + 1] as number) / secant;
|
|
205
|
+
// A tangent fighting the segment's own direction is a local extremum the
|
|
206
|
+
// data does not have.
|
|
207
|
+
if (alpha < 0) {
|
|
208
|
+
tangents[i] = 0;
|
|
209
|
+
alpha = 0;
|
|
210
|
+
}
|
|
211
|
+
if (beta < 0) {
|
|
212
|
+
tangents[i + 1] = 0;
|
|
213
|
+
beta = 0;
|
|
214
|
+
}
|
|
215
|
+
const radius = alpha * alpha + beta * beta;
|
|
216
|
+
if (radius > 9) {
|
|
217
|
+
const scale = 3 / Math.sqrt(radius);
|
|
218
|
+
tangents[i] = scale * alpha * secant;
|
|
219
|
+
tangents[i + 1] = scale * beta * secant;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return tangents;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* The curve's value at `x` — the whole runtime API.
|
|
227
|
+
*
|
|
228
|
+
* Read it every frame from the game's own update, the way every other data
|
|
229
|
+
* value is read (`tuning.get()` then sample); never precompute a table, and
|
|
230
|
+
* never hold the result across frames — an edit to the `.data.json` hot-swaps
|
|
231
|
+
* underneath you and that is the point.
|
|
232
|
+
*
|
|
233
|
+
* Outside `[first.x, last.x]` the value CLAMPS to the nearest endpoint: a
|
|
234
|
+
* curve declares what it knows about, and extrapolating past it would invent
|
|
235
|
+
* numbers nobody authored.
|
|
236
|
+
*/
|
|
237
|
+
export function sampleCurve(value: CurveValue, x: number): number {
|
|
238
|
+
const points = value.points;
|
|
239
|
+
if (points.length === 0) return 0;
|
|
240
|
+
const first = points[0] as CurvePoint;
|
|
241
|
+
const last = points[points.length - 1] as CurvePoint;
|
|
242
|
+
if (points.length === 1 || x <= first.x) return first.y;
|
|
243
|
+
if (x >= last.x) return last.y;
|
|
244
|
+
|
|
245
|
+
let i = 0;
|
|
246
|
+
while (i < points.length - 2 && x >= (points[i + 1] as CurvePoint).x) i++;
|
|
247
|
+
const left = points[i] as CurvePoint;
|
|
248
|
+
const right = points[i + 1] as CurvePoint;
|
|
249
|
+
const span = right.x - left.x;
|
|
250
|
+
if (span <= 0) return left.y; // duplicate x — the schema rejects it; a hand-built value might not
|
|
251
|
+
const t = (x - left.x) / span;
|
|
252
|
+
if (value.interpolation === 'linear') return left.y + (right.y - left.y) * t;
|
|
253
|
+
|
|
254
|
+
const tangents = monotoneTangents(points);
|
|
255
|
+
const t2 = t * t;
|
|
256
|
+
const t3 = t2 * t;
|
|
257
|
+
return (
|
|
258
|
+
(2 * t3 - 3 * t2 + 1) * left.y +
|
|
259
|
+
(t3 - 2 * t2 + t) * span * (tangents[i] as number) +
|
|
260
|
+
(-2 * t3 + 3 * t2) * right.y +
|
|
261
|
+
(t3 - t2) * span * (tangents[i + 1] as number)
|
|
262
|
+
);
|
|
263
|
+
}
|
|
@@ -38,8 +38,14 @@
|
|
|
38
38
|
* names the registry file to fix.
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
// Node built-ins are imported LAZILY (inside `collectDataCheckProblems`),
|
|
42
|
+
// never statically: this module is re-exported by the `@vgai/engine/config`
|
|
43
|
+
// barrel, which every project `*.schema.ts` reaches through its `data-ref`
|
|
44
|
+
// shim — so this module EVALUATES in the browser graph of any world whose
|
|
45
|
+
// schema the game imports. A static `import { … } from 'node:fs'` dies there
|
|
46
|
+
// ("Module node:fs has been externalized for browser compatibility") before
|
|
47
|
+
// the world can mount. The fs work itself only ever runs in Node (vite build
|
|
48
|
+
// / emit-schemas), where the dynamic import resolves normally.
|
|
43
49
|
import type { Plugin } from 'vite';
|
|
44
50
|
import type { z } from 'zod';
|
|
45
51
|
import { parseDataJson, toDataJsonSchema } from './data-asset';
|
|
@@ -62,18 +68,38 @@ export interface VgaiDataCheckOptions {
|
|
|
62
68
|
readonly root?: string;
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
/** The Node APIs the checks need, resolved lazily — see the import note above. */
|
|
72
|
+
interface NodeIo {
|
|
73
|
+
readdirSync: typeof import('node:fs')['readdirSync'];
|
|
74
|
+
readFileSync: typeof import('node:fs')['readFileSync'];
|
|
75
|
+
statSync: typeof import('node:fs')['statSync'];
|
|
76
|
+
join: typeof import('node:path')['join'];
|
|
77
|
+
relative: typeof import('node:path')['relative'];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function loadNodeIo(): Promise<NodeIo> {
|
|
81
|
+
const [fs, path] = await Promise.all([import('node:fs'), import('node:path')]);
|
|
82
|
+
return {
|
|
83
|
+
readdirSync: fs.readdirSync,
|
|
84
|
+
readFileSync: fs.readFileSync,
|
|
85
|
+
statSync: fs.statSync,
|
|
86
|
+
join: path.join,
|
|
87
|
+
relative: path.relative,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
65
91
|
/** Recursively collect `*.data.json` under `dir` (absent dir → empty — a project with no data assets builds fine). */
|
|
66
|
-
function findDataFiles(dir: string): string[] {
|
|
92
|
+
function findDataFiles(io: NodeIo, dir: string): string[] {
|
|
67
93
|
let entries: string[];
|
|
68
94
|
try {
|
|
69
|
-
entries = readdirSync(dir);
|
|
95
|
+
entries = io.readdirSync(dir) as string[];
|
|
70
96
|
} catch {
|
|
71
97
|
return [];
|
|
72
98
|
}
|
|
73
99
|
const out: string[] = [];
|
|
74
100
|
for (const entry of entries) {
|
|
75
|
-
const full = join(dir, entry);
|
|
76
|
-
if (statSync(full).isDirectory()) out.push(...findDataFiles(full));
|
|
101
|
+
const full = io.join(dir, entry);
|
|
102
|
+
if (io.statSync(full).isDirectory()) out.push(...findDataFiles(io, full));
|
|
77
103
|
else if (entry.endsWith('.data.json')) out.push(full);
|
|
78
104
|
}
|
|
79
105
|
return out;
|
|
@@ -116,6 +142,7 @@ interface ParsedDataFiles {
|
|
|
116
142
|
}
|
|
117
143
|
|
|
118
144
|
function parseAllDataFiles(
|
|
145
|
+
io: NodeIo,
|
|
119
146
|
root: string,
|
|
120
147
|
registered: ReadonlySet<string>,
|
|
121
148
|
errors: string[],
|
|
@@ -123,15 +150,15 @@ function parseAllDataFiles(
|
|
|
123
150
|
): ParsedDataFiles {
|
|
124
151
|
const parsedByStem = new Map<string, unknown>();
|
|
125
152
|
const pathsByStem = new Map<string, string[]>();
|
|
126
|
-
for (const abs of findDataFiles(join(root, 'src', 'data'))) {
|
|
127
|
-
const rel = relative(root, abs).split('\\').join('/');
|
|
153
|
+
for (const abs of findDataFiles(io, io.join(root, 'src', 'data'))) {
|
|
154
|
+
const rel = io.relative(root, abs).split('\\').join('/');
|
|
128
155
|
const stem = assetStem(rel);
|
|
129
156
|
const paths = pathsByStem.get(stem);
|
|
130
157
|
if (paths) paths.push(rel);
|
|
131
158
|
else pathsByStem.set(stem, [rel]);
|
|
132
159
|
|
|
133
160
|
try {
|
|
134
|
-
parsedByStem.set(stem, JSON.parse(readFileSync(abs, 'utf-8')));
|
|
161
|
+
parsedByStem.set(stem, JSON.parse(io.readFileSync(abs, 'utf-8') as string));
|
|
135
162
|
} catch (err) {
|
|
136
163
|
errors.push(
|
|
137
164
|
`"${rel}" is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -167,15 +194,22 @@ function parseAllDataFiles(
|
|
|
167
194
|
* The fs half of checks 1 + 2, extracted from the plugin hook so it is
|
|
168
195
|
* directly unit-testable against a fixture folder (no rollup context needed).
|
|
169
196
|
*/
|
|
170
|
-
export function collectDataCheckProblems(options: VgaiDataCheckOptions): {
|
|
197
|
+
export async function collectDataCheckProblems(options: VgaiDataCheckOptions): Promise<{
|
|
171
198
|
errors: string[];
|
|
172
199
|
warnings: string[];
|
|
173
|
-
} {
|
|
200
|
+
}> {
|
|
201
|
+
const io = await loadNodeIo();
|
|
174
202
|
const root = options.root ?? process.cwd();
|
|
175
203
|
const errors: string[] = [];
|
|
176
204
|
const warnings: string[] = [];
|
|
177
205
|
const registered = new Set(options.assets.map((a) => a.sourcePath.split('\\').join('/')));
|
|
178
|
-
const { parsedByStem, ambiguousStems } = parseAllDataFiles(
|
|
206
|
+
const { parsedByStem, ambiguousStems } = parseAllDataFiles(
|
|
207
|
+
io,
|
|
208
|
+
root,
|
|
209
|
+
registered,
|
|
210
|
+
errors,
|
|
211
|
+
warnings,
|
|
212
|
+
);
|
|
179
213
|
|
|
180
214
|
// Check 1 — registered assets validate through the exact runtime parse.
|
|
181
215
|
for (const asset of options.assets) {
|
|
@@ -256,8 +290,8 @@ export function vgaiDataCheck(options: VgaiDataCheckOptions): Plugin {
|
|
|
256
290
|
return {
|
|
257
291
|
name: 'vgai:data-check',
|
|
258
292
|
apply: 'build',
|
|
259
|
-
buildStart() {
|
|
260
|
-
const { errors, warnings } = collectDataCheckProblems(options);
|
|
293
|
+
async buildStart() {
|
|
294
|
+
const { errors, warnings } = await collectDataCheckProblems(options);
|
|
261
295
|
for (const warning of warnings) this.warn(warning);
|
|
262
296
|
if (errors.length > 0) {
|
|
263
297
|
// ONE error carrying every problem — fail loud with the full picture,
|
|
@@ -109,6 +109,12 @@ export function createPerformanceProfiler(initiallyEnabled = false) {
|
|
|
109
109
|
let frameSystems: PerformanceSpan[] = [];
|
|
110
110
|
let frameComponents: PerformanceSpan[] = [];
|
|
111
111
|
let enabled = initiallyEnabled;
|
|
112
|
+
// The header's always-visible FPS sparkline needs frame INTERVALS without
|
|
113
|
+
// turning on the full profiler. Full profiling measures phases/systems,
|
|
114
|
+
// issues GPU timer queries at render sites, and intentionally warns about
|
|
115
|
+
// overhead; a glanceable heartbeat must not silently opt the user into all
|
|
116
|
+
// of that work. `telemetryEnabled` records only the frame envelope.
|
|
117
|
+
let telemetryEnabled = false;
|
|
112
118
|
let recording = false;
|
|
113
119
|
let frameId = 0;
|
|
114
120
|
let frameStart = 0;
|
|
@@ -177,13 +183,26 @@ export function createPerformanceProfiler(initiallyEnabled = false) {
|
|
|
177
183
|
set enabled(value: boolean) {
|
|
178
184
|
if (enabled === value) return;
|
|
179
185
|
enabled = value;
|
|
186
|
+
if (value && !telemetryEnabled) lastFrameStart = 0;
|
|
187
|
+
publish();
|
|
188
|
+
},
|
|
189
|
+
get telemetryEnabled() {
|
|
190
|
+
return telemetryEnabled;
|
|
191
|
+
},
|
|
192
|
+
set telemetryEnabled(value: boolean) {
|
|
193
|
+
if (telemetryEnabled === value) return;
|
|
194
|
+
const wasInactive = !enabled && !telemetryEnabled;
|
|
195
|
+
telemetryEnabled = value;
|
|
196
|
+
if (value && wasInactive) lastFrameStart = 0;
|
|
180
197
|
publish();
|
|
181
198
|
},
|
|
182
199
|
get recording() {
|
|
183
200
|
return recording;
|
|
184
201
|
},
|
|
185
202
|
startRecording() {
|
|
203
|
+
const wasInactive = !enabled && !telemetryEnabled;
|
|
186
204
|
enabled = true;
|
|
205
|
+
if (wasInactive) lastFrameStart = 0;
|
|
187
206
|
recording = true;
|
|
188
207
|
frames.length = 0;
|
|
189
208
|
publish();
|
|
@@ -197,7 +216,7 @@ export function createPerformanceProfiler(initiallyEnabled = false) {
|
|
|
197
216
|
publish();
|
|
198
217
|
},
|
|
199
218
|
beginFrame() {
|
|
200
|
-
if (!enabled) return;
|
|
219
|
+
if (!enabled && !telemetryEnabled) return;
|
|
201
220
|
currentPhases.clear();
|
|
202
221
|
currentSystems.clear();
|
|
203
222
|
currentComponents.clear();
|
|
@@ -293,7 +312,7 @@ export function createPerformanceProfiler(initiallyEnabled = false) {
|
|
|
293
312
|
};
|
|
294
313
|
},
|
|
295
314
|
endFrame() {
|
|
296
|
-
if (!enabled) return;
|
|
315
|
+
if (!enabled && !telemetryEnabled) return;
|
|
297
316
|
const timestamp = now();
|
|
298
317
|
const intervalMs = lastFrameStart === 0 ? 0 : frameStart - lastFrameStart;
|
|
299
318
|
lastFrameStart = frameStart;
|
|
@@ -6,10 +6,19 @@
|
|
|
6
6
|
* through `vgai eval`, and `game.providers()`/`game.commands()` to discover
|
|
7
7
|
* them without being told they exist.
|
|
8
8
|
*
|
|
9
|
-
* Engine-owned and first-party, seeded by
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
12
|
-
*
|
|
9
|
+
* Engine-owned and first-party, seeded by BOTH three-root mount paths — a game
|
|
10
|
+
* writes nothing to get these, and there is no capability to install:
|
|
11
|
+
* - `adapter/setup-three-root-adapter.ts` (the classic imperative
|
|
12
|
+
* `setup(ctx)` world), at the same spot it seeds
|
|
13
|
+
* `systemAdapters.renderDebug`;
|
|
14
|
+
* - `world3d-react/r3f-adapter.tsx` (a TSX/R3F world — the shape EVERY
|
|
15
|
+
* scaffolded project's three root has), right after fiber's first commit
|
|
16
|
+
* resolves the scene.
|
|
17
|
+
* Both is the whole point and not redundancy: they are two independent mounts
|
|
18
|
+
* onto the same `Game`, and a door wired into only one of them is a door half
|
|
19
|
+
* the engine's worlds never get (issue #1518 — the R3F half was dark
|
|
20
|
+
* everywhere, found in the field on a package-native project).
|
|
21
|
+
* The measurement half is `dev/render-vitals.ts`
|
|
13
22
|
* (derived entirely from profiler frames); the address-book half is
|
|
14
23
|
* `dev/render-census.ts` (pure walks over the live scene). This module is only
|
|
15
24
|
* the wiring between them and the registry.
|
|
@@ -27,9 +36,11 @@
|
|
|
27
36
|
* the drawing buffer halves was never fill-bound.
|
|
28
37
|
*
|
|
29
38
|
* ── GATING ──────────────────────────────────────────────────────────────────
|
|
30
|
-
* The caller gates.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
39
|
+
* The caller gates. Both seeders call this only under `devLayersEnabled()`
|
|
40
|
+
* (`runtime/dev-layers.ts` — the ONE owner of "is this a dev context"), so a
|
|
41
|
+
* ship build registers nothing and pays nothing. Each also requires a `Game`
|
|
42
|
+
* shell (there is no profiler to fold without one) and a non-headless mount
|
|
43
|
+
* (nothing brackets a submission, so the readings could only ever be empty).
|
|
33
44
|
*
|
|
34
45
|
* ── RESOURCE OWNERSHIP ──────────────────────────────────────────────────────
|
|
35
46
|
* OWNER: {@link registerRenderVitals}'s caller. It allocates one
|
|
@@ -112,7 +123,26 @@ export function registerRenderVitals(
|
|
|
112
123
|
const taken = registry.adapter
|
|
113
124
|
.providers()
|
|
114
125
|
.some((provider) => provider.name === RENDER_VITALS_PROVIDER);
|
|
115
|
-
if (taken)
|
|
126
|
+
if (taken) {
|
|
127
|
+
// Loud, by name. This branch used to return `null` in silence, and a
|
|
128
|
+
// silent decline is indistinguishable from a door that was never seeded —
|
|
129
|
+
// which is precisely how issue #1518 stayed a mystery through a field
|
|
130
|
+
// session: every hypothesis about WHY the door was closed had to be
|
|
131
|
+
// guessed, because nothing on any path said anything. It costs one line to
|
|
132
|
+
// make the one legitimate decline explain itself, and the same line names
|
|
133
|
+
// the mount/teardown OVERLAP case (a re-mount seeding before the outgoing
|
|
134
|
+
// mount's `strip()` runs), where the second world would otherwise lose the
|
|
135
|
+
// door and no log would mention it.
|
|
136
|
+
console.warn(
|
|
137
|
+
`[render-vitals] world "${worldId}" did not get the ${RENDER_VITALS_PROVIDER} door: ` +
|
|
138
|
+
'another live world already owns it (the names are fixed, so the first three root to ' +
|
|
139
|
+
'mount wins). Its draws still fold into the readings — the profiler is game-scoped — ' +
|
|
140
|
+
'but render.census/families/toggle address the owning root. If this world IS the ' +
|
|
141
|
+
'current one, the previous mount was torn down AFTER this one seeded and its ' +
|
|
142
|
+
'strip() took the door with it.',
|
|
143
|
+
);
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
116
146
|
|
|
117
147
|
const vitals = createRenderVitals(profiler);
|
|
118
148
|
const debug = registry.forRoot(worldId);
|
package/src/ecs/user-data.ts
CHANGED
|
@@ -76,18 +76,48 @@
|
|
|
76
76
|
* implementation parts without adding wrappers.
|
|
77
77
|
* - `authoringLabel` — authored literal name (or component tag fallback)
|
|
78
78
|
* for that source-backed component instance.
|
|
79
|
+
* - `authoringComponent` — exact source component identity for portable-CSF
|
|
80
|
+
* association; unlike the label, never user-facing copy.
|
|
81
|
+
* - `authoringDocument` — generic reference to the project-tool document
|
|
82
|
+
* that owns this scene instance's authored asset.
|
|
79
83
|
* - `editorHelper` — `true` for editor-only helper objects (gizmos, wireframes).
|
|
84
|
+
*
|
|
85
|
+
* Hierarchy-presentation convention (written by GAME code, read by the editor —
|
|
86
|
+
* see `../adapter/hierarchy-marks.ts`, which is the ONLY place these two are
|
|
87
|
+
* read/written from; the `vgai` prefix marks them as the HOST's namespace on a
|
|
88
|
+
* node a game owns, unlike every other key above, which the engine/editor also
|
|
89
|
+
* write):
|
|
90
|
+
* - `vgaiComponentRoot` — display name of the component instance this subtree
|
|
91
|
+
* IS. The node renders as one collapsed, expandable
|
|
92
|
+
* row named for it.
|
|
93
|
+
* - `vgaiBuiltInternal` — `true` on the ROOT of a subtree runtime code
|
|
94
|
+
* CONSTRUCTED rather than authored (skeleton bones,
|
|
95
|
+
* particle renderers). Subtree-scoped: everything
|
|
96
|
+
* below a marked node is built-internal too.
|
|
97
|
+
* - `vgaiBodyOwner` — the rigid body that OWNS this node's pose, written
|
|
98
|
+
* by the code that attaches the body (see
|
|
99
|
+
* `../adapter/body-marks.ts`, the only reader/writer).
|
|
100
|
+
* A transform edit on such a node must be applied to
|
|
101
|
+
* the BODY, or the next physics step writes the node
|
|
102
|
+
* straight back.
|
|
80
103
|
* - `editorHelperType` — which kind of helper (lights/particles/pivot/navmesh/...).
|
|
81
104
|
* - `editorIcon` — `true` for editor billboard icon sprites.
|
|
82
105
|
* - `skeletonVisible` — per-entity editor preference for its bone overlay.
|
|
83
106
|
* - `skeletonEnabled` — resolved visibility preference on a skeleton helper.
|
|
84
107
|
* - `envObject` — `true` for environment objects (ambient light, etc.).
|
|
108
|
+
* - `reflectionProbe` — live project-owned reflection probe projected from
|
|
109
|
+
* JSX props for renderer/editor integration.
|
|
110
|
+
* - `constraints` — live project-owned spatial constraints projected
|
|
111
|
+
* into the shared Inspector and viewport.
|
|
85
112
|
* - `splineControlPoint` — index of a spline control-point drag handle.
|
|
86
113
|
* - `vcDirIdx` — view-cube face direction index (0=+X,1=-X,2=+Y,...).
|
|
87
114
|
*/
|
|
88
115
|
|
|
89
116
|
import type * as THREE from 'three';
|
|
90
117
|
import type { ParticleSystem } from 'three.quarks';
|
|
118
|
+
import type { ConstraintMark } from '../adapter/constraint';
|
|
119
|
+
import type { RapierEditableBody } from '../adapter/rapier-physics-adapter';
|
|
120
|
+
import type { ReflectionProbeMark } from '../adapter/reflection-probe';
|
|
91
121
|
import type { XStateAnimationBinding } from '../animation/xstate-animation-binding';
|
|
92
122
|
|
|
93
123
|
/** The kinds of editor helper objects tagged via `editorHelperType`. */
|
|
@@ -112,6 +142,8 @@ export type EditorHelperType =
|
|
|
112
142
|
| 'particle-shape'
|
|
113
143
|
| 'pivot'
|
|
114
144
|
| 'navmesh'
|
|
145
|
+
| 'constraints'
|
|
146
|
+
| 'reflection-probes'
|
|
115
147
|
| 'skeletons';
|
|
116
148
|
|
|
117
149
|
/**
|
|
@@ -143,12 +175,23 @@ export interface UserDataSchema {
|
|
|
143
175
|
authoringRoot: boolean;
|
|
144
176
|
authoringInstance: string;
|
|
145
177
|
authoringLabel: string;
|
|
178
|
+
vgaiComponentRoot: string;
|
|
179
|
+
vgaiBuiltInternal: boolean;
|
|
180
|
+
vgaiBodyOwner: RapierEditableBody;
|
|
181
|
+
authoringComponent: string;
|
|
182
|
+
authoringDocument: {
|
|
183
|
+
readonly kind: 'project-tool';
|
|
184
|
+
readonly name: string;
|
|
185
|
+
readonly title: string;
|
|
186
|
+
};
|
|
146
187
|
editorHelper: boolean;
|
|
147
188
|
editorHelperType: EditorHelperType;
|
|
148
189
|
editorIcon: boolean;
|
|
149
190
|
skeletonVisible: boolean;
|
|
150
191
|
skeletonEnabled: boolean;
|
|
151
192
|
envObject: boolean;
|
|
193
|
+
constraints: readonly ConstraintMark[];
|
|
194
|
+
reflectionProbe: ReflectionProbeMark;
|
|
152
195
|
vcDirIdx: number;
|
|
153
196
|
/** W1a navmesh path-probe preview polyline: the routed waypoints, mirrored
|
|
154
197
|
* onto the preview line Object3D so e2e can read the path back from the
|
|
@@ -189,12 +232,19 @@ export const UserDataKeys = {
|
|
|
189
232
|
authoringRoot: 'authoringRoot',
|
|
190
233
|
authoringInstance: 'authoringInstance',
|
|
191
234
|
authoringLabel: 'authoringLabel',
|
|
235
|
+
vgaiComponentRoot: 'vgaiComponentRoot',
|
|
236
|
+
vgaiBuiltInternal: 'vgaiBuiltInternal',
|
|
237
|
+
vgaiBodyOwner: 'vgaiBodyOwner',
|
|
238
|
+
authoringComponent: 'authoringComponent',
|
|
239
|
+
authoringDocument: 'authoringDocument',
|
|
192
240
|
editorHelper: 'editorHelper',
|
|
193
241
|
editorHelperType: 'editorHelperType',
|
|
194
242
|
editorIcon: 'editorIcon',
|
|
195
243
|
skeletonVisible: 'skeletonVisible',
|
|
196
244
|
skeletonEnabled: 'skeletonEnabled',
|
|
197
245
|
envObject: 'envObject',
|
|
246
|
+
constraints: 'constraints',
|
|
247
|
+
reflectionProbe: 'reflectionProbe',
|
|
198
248
|
vcDirIdx: 'vcDirIdx',
|
|
199
249
|
navPathPoints: 'navPathPoints',
|
|
200
250
|
} as const satisfies Record<UserDataKey, string>;
|
package/src/index.ts
CHANGED
|
@@ -20,13 +20,12 @@
|
|
|
20
20
|
* - game setup contract: `GameContext`/`GameCleanup`/`GameSetupFn`, `fromSetup`
|
|
21
21
|
* - surface vocabulary: `AdapterSurface`
|
|
22
22
|
* - manifest-driven mount: `mountManifestRoots` + its option/result types
|
|
23
|
-
* - asset-format entry points: `parseMaterialFile` (.mat.json), `parseInstancesFile`
|
|
24
23
|
* - game manifest (vgai.project.json) entry point: `loadGameManifest`
|
|
25
24
|
*/
|
|
26
25
|
|
|
27
26
|
export type { AdapterSurface } from './adapter/adapter-surface';
|
|
28
27
|
export { fromSetup } from './adapter/setup-three-root-adapter';
|
|
29
|
-
export { AssetParseError
|
|
28
|
+
export { AssetParseError } from './asset-parse-error';
|
|
30
29
|
export type {
|
|
31
30
|
AdapterRoot,
|
|
32
31
|
GameManifest,
|
|
@@ -35,7 +34,7 @@ export type {
|
|
|
35
34
|
ResolvedGameManifest,
|
|
36
35
|
RootAdapter,
|
|
37
36
|
} from './manifest';
|
|
38
|
-
export {
|
|
37
|
+
export { DEFAULT_SERVER_MODULE, loadGameManifest } from './manifest';
|
|
39
38
|
export type { GameSession } from './runtime/create-runtime';
|
|
40
39
|
export type {
|
|
41
40
|
MountEntry,
|
package/src/manifest/index.ts
CHANGED
|
@@ -8,21 +8,9 @@ export {
|
|
|
8
8
|
removedManifestFilenameMessage,
|
|
9
9
|
} from './filename';
|
|
10
10
|
export type { ResolvedAdapter, ResolvedAdapterRoot, ResolvedGameManifest } from './load';
|
|
11
|
-
export {
|
|
12
|
-
export type {
|
|
13
|
-
|
|
14
|
-
GameManifest,
|
|
15
|
-
IngestStrategy,
|
|
16
|
-
RootAdapter,
|
|
17
|
-
Tier,
|
|
18
|
-
} from './schema';
|
|
19
|
-
export {
|
|
20
|
-
AdapterRootSchema,
|
|
21
|
-
GameManifestSchema,
|
|
22
|
-
IngestStrategySchema,
|
|
23
|
-
RootAdapterSchema,
|
|
24
|
-
TierSchema,
|
|
25
|
-
} from './schema';
|
|
11
|
+
export { DEFAULT_SERVER_MODULE, loadGameManifest } from './load';
|
|
12
|
+
export type { AdapterRoot, GameManifest, RootAdapter } from './schema';
|
|
13
|
+
export { AdapterRootSchema, GameManifestSchema, RootAdapterSchema } from './schema';
|
|
26
14
|
|
|
27
15
|
// `loadGameManifestFile` (Node-only, reads `path` via `node:fs`) is
|
|
28
16
|
// deliberately NOT re-exported here — this barrel must stay import-safe for
|