@threenative/core 0.3.2 → 0.3.4
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/capabilities.json +1891 -101
- package/dist/{assets-kyoF7JlJ.d.ts → assets-CqvE429w.d.ts} +52 -3
- package/dist/{audio-BFiGneTL.d.ts → audio-7i3Xl0l3.d.ts} +32 -1
- package/dist/{canvas-layer-BLVijiUJ.d.ts → canvas-layer-DDmC_VVF.d.ts} +1 -1
- package/dist/{game-XGrTzapq.d.ts → game-CljaDv4D.d.ts} +503 -11
- package/dist/{gpu-readback-D2iRvoe9.d.ts → gpu-readback-CqJEfWNQ.d.ts} +16 -6
- package/dist/hot.d.ts +5 -5
- package/dist/hot.js +20 -3
- package/dist/index.d.ts +1496 -43
- package/dist/index.js +8345 -753
- package/dist/playtest.d.ts +11 -5
- package/dist/playtest.js +96 -11
- package/dist/react.d.ts +2 -2
- package/dist/{renderer-C6hqZpoG.d.ts → renderer-CfsS2hxi.d.ts} +436 -14
- package/dist/ui-layer.d.ts +15 -7
- package/dist/ui-layer.js +2 -1
- package/dist/world.d.ts +900 -14
- package/dist/world.js +10876 -126
- package/gpl/fixtures/make_world_fixture.py +184 -0
- package/gpl/recipes/_common.py +11 -0
- package/gpl/recipes/decimate.py +11 -5
- package/gpl/recipes/export_world.py +682 -0
- package/mcp/blender-server.mjs +55 -1
- package/mcp/engine-server.mjs +84 -22
- package/mcp/servers.mjs +3 -3
- package/package.json +6 -6
- package/patches/three@0.185.1.patch +2228 -105
- package/scripts/apply-three-patch.mjs +61 -37
package/dist/playtest.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { JsonValue, JsonPrimitive } from '@threenative/playtest/protocol';
|
|
2
|
-
import { b as IGamePluginHooks } from './game-
|
|
3
|
-
import './assets-
|
|
2
|
+
import { b as IGamePluginHooks } from './game-CljaDv4D.js';
|
|
3
|
+
import './assets-CqvE429w.js';
|
|
4
4
|
import 'three';
|
|
5
|
-
import './renderer-
|
|
5
|
+
import './renderer-CfsS2hxi.js';
|
|
6
6
|
import 'three/webgpu';
|
|
7
|
-
import './canvas-layer-
|
|
7
|
+
import './canvas-layer-DDmC_VVF.js';
|
|
8
8
|
import 'zustand/vanilla';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -31,6 +31,12 @@ declare const PLAYTEST_ATTACH_TIMEOUT_MS = 30000;
|
|
|
31
31
|
*/
|
|
32
32
|
/** The global a playtest runner sets before the page loads, so a game can tell one is coming. */
|
|
33
33
|
declare const PLAYTEST_RUNNER_EXPECTED_GLOBAL = "__THREENATIVE_PLAYTEST_RUNNER_EXPECTED__";
|
|
34
|
+
/**
|
|
35
|
+
* The clock a consumer asks this game to run on, named in the protocol's own vocabulary. Set before
|
|
36
|
+
* the game module evaluates — a production profile does it in the instrumentation it injects ahead
|
|
37
|
+
* of the bundle, on the browser and on native alike.
|
|
38
|
+
*/
|
|
39
|
+
declare const PLAYTEST_CLOCK_GLOBAL = "__THREENATIVE_PLAYTEST_CLOCK__";
|
|
34
40
|
interface IPlaytestOptions {
|
|
35
41
|
readonly events?: () => JsonValue[];
|
|
36
42
|
/**
|
|
@@ -63,4 +69,4 @@ interface IPlaytestTransition {
|
|
|
63
69
|
to: JsonPrimitive;
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
export { type IPlaytestOptions, type IPlaytestTransition, PLAYTEST_ATTACH_TIMEOUT_MS, PLAYTEST_RUNNER_EXPECTED_GLOBAL, PLAYTEST_TRANSITION_LOG_LIMIT, playtest };
|
|
72
|
+
export { type IPlaytestOptions, type IPlaytestTransition, PLAYTEST_ATTACH_TIMEOUT_MS, PLAYTEST_CLOCK_GLOBAL, PLAYTEST_RUNNER_EXPECTED_GLOBAL, PLAYTEST_TRANSITION_LOG_LIMIT, playtest };
|
package/dist/playtest.js
CHANGED
|
@@ -1,27 +1,48 @@
|
|
|
1
1
|
import { assertJsonSafe, PLAYTEST_PROTOCOL_LIMITS } from '@threenative/playtest/protocol';
|
|
2
2
|
import { installThreePlaytestBridge } from '@threenative/playtest/three';
|
|
3
|
-
import { Object3D } from 'three';
|
|
3
|
+
import { Sphere, Vector3, Object3D } from 'three';
|
|
4
4
|
|
|
5
5
|
// src/playtest.ts
|
|
6
|
-
var
|
|
6
|
+
var AUDIO_STATE = /* @__PURE__ */ Symbol.for("threenative.audio.runtime");
|
|
7
|
+
function audioState() {
|
|
8
|
+
const host = globalThis;
|
|
9
|
+
const existing = host[AUDIO_STATE];
|
|
10
|
+
if (existing !== void 0) return existing;
|
|
11
|
+
const created = { buses: /* @__PURE__ */ new Set(), cueCounts: /* @__PURE__ */ new Map(), cueLog: [] };
|
|
12
|
+
host[AUDIO_STATE] = created;
|
|
13
|
+
return created;
|
|
14
|
+
}
|
|
7
15
|
function audioRuntimeSnapshot() {
|
|
8
16
|
let queued = 0;
|
|
9
17
|
let voices = 0;
|
|
10
18
|
let pooled = 0;
|
|
11
19
|
let paused = 0;
|
|
12
20
|
const unsupported = /* @__PURE__ */ new Set();
|
|
13
|
-
|
|
21
|
+
const state = audioState();
|
|
22
|
+
for (const bus of state.buses) {
|
|
14
23
|
queued += bus.queued;
|
|
15
24
|
voices += bus.voices;
|
|
16
25
|
pooled += bus.pooled;
|
|
17
26
|
paused += bus.pausedVoices;
|
|
18
27
|
for (const option of bus.unsupported) unsupported.add(option);
|
|
19
28
|
}
|
|
20
|
-
return {
|
|
29
|
+
return {
|
|
30
|
+
cues: Object.fromEntries(state.cueCounts),
|
|
31
|
+
paused,
|
|
32
|
+
pooled,
|
|
33
|
+
queued,
|
|
34
|
+
recentCues: state.cueLog.map((entry) => ({ ...entry })),
|
|
35
|
+
unsupported: [...unsupported].sort(),
|
|
36
|
+
voices
|
|
37
|
+
};
|
|
21
38
|
}
|
|
39
|
+
var GEOMETRY_CAPTURE_CAPABILITY = "runtime.geometry";
|
|
40
|
+
new Sphere();
|
|
41
|
+
new Vector3();
|
|
42
|
+
new Vector3();
|
|
22
43
|
|
|
23
44
|
// src/version.ts
|
|
24
|
-
var CORE_VERSION = "0.3.
|
|
45
|
+
var CORE_VERSION = "0.3.4";
|
|
25
46
|
|
|
26
47
|
// src/pipeline-census.ts
|
|
27
48
|
var PIPELINE_CENSUS_CAPABILITY = "runtime.pipelineCensus";
|
|
@@ -53,6 +74,7 @@ function playtest(options = {}) {
|
|
|
53
74
|
let attached;
|
|
54
75
|
let startSceneEntered;
|
|
55
76
|
let disposePipelineCensus;
|
|
77
|
+
let disposeGeometryCapture;
|
|
56
78
|
let contactHistory = [];
|
|
57
79
|
const watcher = createTransitionWatcher();
|
|
58
80
|
let readTick;
|
|
@@ -70,6 +92,8 @@ function playtest(options = {}) {
|
|
|
70
92
|
setup: async (ctx, runtime) => {
|
|
71
93
|
readTick = runtime?.tick;
|
|
72
94
|
const seed = runtime?.seed ?? null;
|
|
95
|
+
const clockMode = requestedClockMode();
|
|
96
|
+
const wallClock = clockMode === "wall-clock";
|
|
73
97
|
const replayRuntime = runtime?.seed === null || runtime?.seed === void 0 ? void 0 : {
|
|
74
98
|
agent: currentAgent,
|
|
75
99
|
core: CORE_VERSION,
|
|
@@ -79,9 +103,15 @@ function playtest(options = {}) {
|
|
|
79
103
|
};
|
|
80
104
|
const installation = installThreePlaytestBridge({
|
|
81
105
|
camera: ctx.camera,
|
|
106
|
+
...clockMode === void 0 ? {} : { clockMode },
|
|
82
107
|
entities: () => bridgeEntities(ctx),
|
|
83
108
|
components: () => componentObservations(ctx.entities.snapshot()),
|
|
84
|
-
|
|
109
|
+
// The live clock has to be driven by the host's own frame pump, so a live run's `advance`
|
|
110
|
+
// waits the span of wall time the count names and reports the ticks that pump ran.
|
|
111
|
+
...runtime === void 0 ? {} : {
|
|
112
|
+
fixedStep: wallClock ? wallClockAdvance(runtime) : runtime.fixedStep,
|
|
113
|
+
tick: runtime.tick
|
|
114
|
+
},
|
|
85
115
|
...runtime?.runtimeDiagnosticsSeries === void 0 ? {} : { runtimeDiagnosticsSeries: runtime.runtimeDiagnosticsSeries },
|
|
86
116
|
...options.events === void 0 ? {} : { events: options.events },
|
|
87
117
|
gameplay: () => gameplayObservations(ctx, contactHistory, seed, replayRuntime, watcher),
|
|
@@ -122,14 +152,27 @@ function playtest(options = {}) {
|
|
|
122
152
|
sample: () => ({ pipelineCensus: pipelineCensus() })
|
|
123
153
|
});
|
|
124
154
|
}
|
|
155
|
+
const geometryCapture = runtime?.geometryCapture;
|
|
156
|
+
if (runtime !== void 0 && geometryCapture !== void 0) {
|
|
157
|
+
disposeGeometryCapture = runtime.observations.contribute({
|
|
158
|
+
capabilities: [GEOMETRY_CAPTURE_CAPABILITY],
|
|
159
|
+
// Nothing is armed, walked or hooked unless this request asked for a capture: the
|
|
160
|
+
// capability says the runtime can answer, not that every sample pays for one.
|
|
161
|
+
sample: async (request) => request.geometry === void 0 ? {} : { geometry: await geometryCapture(request.geometry) }
|
|
162
|
+
});
|
|
163
|
+
}
|
|
125
164
|
installRuntimeChannels(installation.bridge, runtime);
|
|
126
|
-
if (
|
|
165
|
+
if (runnerAnnounced()) {
|
|
127
166
|
runtime?.enableRuntimeDiagnostics?.();
|
|
167
|
+
if (!wallClock) runtime?.freezeClock?.();
|
|
168
|
+
}
|
|
128
169
|
dispose = installation.dispose;
|
|
129
170
|
attached = holdUntilAttached(installation.bridge, options, () => startSceneEntered);
|
|
130
171
|
const cleanup = () => {
|
|
131
172
|
disposePipelineCensus?.();
|
|
132
173
|
disposePipelineCensus = void 0;
|
|
174
|
+
disposeGeometryCapture?.();
|
|
175
|
+
disposeGeometryCapture = void 0;
|
|
133
176
|
dispose?.();
|
|
134
177
|
dispose = void 0;
|
|
135
178
|
attached = void 0;
|
|
@@ -155,8 +198,32 @@ function playtest(options = {}) {
|
|
|
155
198
|
}
|
|
156
199
|
var PLAYTEST_ATTACH_TIMEOUT_MS = 3e4;
|
|
157
200
|
var PLAYTEST_RUNNER_EXPECTED_GLOBAL = "__THREENATIVE_PLAYTEST_RUNNER_EXPECTED__";
|
|
201
|
+
var PLAYTEST_CLOCK_GLOBAL = "__THREENATIVE_PLAYTEST_CLOCK__";
|
|
202
|
+
function requestedClockMode() {
|
|
203
|
+
const requested = globalThis[PLAYTEST_CLOCK_GLOBAL];
|
|
204
|
+
if (requested === void 0) return void 0;
|
|
205
|
+
if (requested === "wall-clock") return "wall-clock";
|
|
206
|
+
throw new Error(
|
|
207
|
+
`TN_PLAYTEST_CLOCK_UNSUPPORTED: '${PLAYTEST_CLOCK_GLOBAL}' is ${JSON.stringify(requested)}; this protocol knows 'wall-clock' and nothing else.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
var WALL_CLOCK_PUMP_STEPS = 4;
|
|
211
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
212
|
+
function wallClockAdvance(runtime) {
|
|
213
|
+
const stepMs = runtime.step * 1e3;
|
|
214
|
+
return async (ticks) => {
|
|
215
|
+
const before = runtime.tick();
|
|
216
|
+
await sleep(ticks * stepMs);
|
|
217
|
+
for (let waited = 0; waited < WALL_CLOCK_PUMP_STEPS && runtime.tick() === before; waited += 1)
|
|
218
|
+
await sleep(stepMs / 2);
|
|
219
|
+
return runtime.tick() - before;
|
|
220
|
+
};
|
|
221
|
+
}
|
|
158
222
|
function shouldHoldUntilAttached(options) {
|
|
159
223
|
if (options.holdUntilAttached !== void 0) return options.holdUntilAttached;
|
|
224
|
+
return runnerAnnounced();
|
|
225
|
+
}
|
|
226
|
+
function runnerAnnounced() {
|
|
160
227
|
const host = globalThis;
|
|
161
228
|
return host[PLAYTEST_RUNNER_EXPECTED_GLOBAL] === true || host.TN_PLAYTEST_ENDPOINT !== void 0;
|
|
162
229
|
}
|
|
@@ -227,10 +294,10 @@ function addRuntimeCapabilities(description, contributions) {
|
|
|
227
294
|
}
|
|
228
295
|
return { ...description, capabilities };
|
|
229
296
|
}
|
|
230
|
-
function addRuntimeObservations(snapshot, request, contributions) {
|
|
297
|
+
async function addRuntimeObservations(snapshot, request, contributions) {
|
|
231
298
|
const result = { ...snapshot };
|
|
232
299
|
for (const contribution of contributions) {
|
|
233
|
-
const slice = contribution.sample(request);
|
|
300
|
+
const slice = await contribution.sample(request);
|
|
234
301
|
if (typeof slice !== "object" || slice === null || Array.isArray(slice)) {
|
|
235
302
|
throw new TypeError("A runtime observation contribution must return a top-level object.");
|
|
236
303
|
}
|
|
@@ -434,11 +501,29 @@ function stateResources(ctx) {
|
|
|
434
501
|
// `state` is canonical. Keep `GameState` as a compatibility alias until published
|
|
435
502
|
// scenarios have migrated, then remove the alias in a future breaking release.
|
|
436
503
|
["state", value],
|
|
437
|
-
["GameState", value]
|
|
504
|
+
["GameState", value],
|
|
505
|
+
// Where every asset was actually served from, on every target and not only in a browser
|
|
506
|
+
// console log: a game whose manifest never loaded runs on the uncompiled source directory
|
|
507
|
+
// and looks healthy, and without this nothing observable says so.
|
|
508
|
+
["assets", assetResolutions(ctx.assets)]
|
|
438
509
|
]);
|
|
439
510
|
}
|
|
440
511
|
};
|
|
441
512
|
}
|
|
513
|
+
function assetResolutions(assets) {
|
|
514
|
+
const nested = {};
|
|
515
|
+
for (const [logical, record] of assets.resolved) {
|
|
516
|
+
const parts = logical.split(".");
|
|
517
|
+
let node = nested;
|
|
518
|
+
for (const part of parts.slice(0, -1)) {
|
|
519
|
+
const child = node[part] ?? {};
|
|
520
|
+
node[part] = child;
|
|
521
|
+
node = child;
|
|
522
|
+
}
|
|
523
|
+
node[parts.at(-1) ?? logical] = { url: record.url, via: record.via };
|
|
524
|
+
}
|
|
525
|
+
return nested;
|
|
526
|
+
}
|
|
442
527
|
function cloneJsonValue(value) {
|
|
443
528
|
if (Array.isArray(value)) return value.map((item) => cloneJsonValue(item));
|
|
444
529
|
if (typeof value === "object" && value !== null) {
|
|
@@ -449,4 +534,4 @@ function cloneJsonValue(value) {
|
|
|
449
534
|
return value;
|
|
450
535
|
}
|
|
451
536
|
|
|
452
|
-
export { PLAYTEST_ATTACH_TIMEOUT_MS, PLAYTEST_RUNNER_EXPECTED_GLOBAL, PLAYTEST_TRANSITION_LOG_LIMIT, playtest };
|
|
537
|
+
export { PLAYTEST_ATTACH_TIMEOUT_MS, PLAYTEST_CLOCK_GLOBAL, PLAYTEST_RUNNER_EXPECTED_GLOBAL, PLAYTEST_TRANSITION_LOG_LIMIT, playtest };
|
package/dist/react.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { C as CanvasLayer } from './canvas-layer-
|
|
2
|
+
import { C as CanvasLayer } from './canvas-layer-DDmC_VVF.js';
|
|
3
3
|
import 'three';
|
|
4
|
-
import './renderer-
|
|
4
|
+
import './renderer-CfsS2hxi.js';
|
|
5
5
|
import 'three/webgpu';
|
|
6
6
|
|
|
7
7
|
/**
|