incanto 0.51.0 → 0.53.0
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/dist/2d.d.ts +86 -3
- package/dist/2d.js +3 -3
- package/dist/3d.d.ts +9 -3
- package/dist/3d.js +4 -4
- package/dist/{behavior-rZNfzVbH.d.ts → behavior-Do0Da56m.d.ts} +108 -0
- package/dist/{create-game-CkzKZ4v5.js → create-game-9D87XaiX.js} +112 -15
- package/dist/{create-game-BNaOC3Px.js → create-game-CDJ1lVqK.js} +21 -13
- package/dist/debug.d.ts +1 -1
- package/dist/debug.js +1 -1
- package/dist/{duplicate-EybyYeyL.js → duplicate-MNLMAcbz.js} +1 -1
- package/dist/editor.js +1931 -1579
- package/dist/{environment-presets-C08pOC6H.js → environment-presets-SkGanr2s.js} +3 -3
- package/dist/{gameplay-bStgtZBV.js → gameplay-CRYw_Q-T.js} +6 -4
- package/dist/gameplay.d.ts +1 -1
- package/dist/gameplay.js +1 -1
- package/dist/index.d.ts +237 -5
- package/dist/index.js +9 -9
- package/dist/{loader-B2asghWa.js → loader-BTkHYrQn.js} +86 -50
- package/dist/{loader-Dkbn56KC.d.ts → loader-Cu_7kJDy.d.ts} +1 -1
- package/dist/{log-report-lxrQY9cH.js → log-report-CPFm4OXf.js} +0 -0
- package/dist/net.d.ts +15 -410
- package/dist/net.js +1 -822
- package/dist/{pathfinding-Bz34pvQD.d.ts → pathfinding-HNGFqGUZ.d.ts} +24 -1
- package/dist/{physics-2d-Ccq2L9R0.js → physics-2d-BhZZ-HAp.js} +2 -2
- package/dist/{physics-3d-B2c5KNYh.js → physics-3d-DmyO2oaN.js} +3 -3
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/{register-CGq-Hee8.js → register-BTomIiYG.js} +305 -25
- package/dist/{register-B_BaaGx-.js → register-DROK2l7J.js} +3 -3
- package/dist/{registry-IyWCGe4q.js → registry-C7u42TID.js} +23 -1
- package/dist/{replay-DIP2_as4.d.ts → replay-9Fy6C10F.d.ts} +1 -1
- package/dist/{replay-BgKxGcXH.js → replay-DYNUL4BU.js} +195 -2
- package/dist/split-screen-CaF9hO7g.js +1267 -0
- package/dist/split-screen-DNmcX1Pz.d.ts +441 -0
- package/dist/{src-C3UwzYXl.js → src-C9xyZW7M.js} +1 -1
- package/dist/{teardown-BKTCzLek.js → teardown-Bw2aeyGI.js} +32 -2
- package/dist/{test-D2gRpk5V.js → test-CJRsciFk.js} +119 -22
- package/dist/test.d.ts +60 -5
- package/dist/test.js +3 -3
- package/dist/{touch-BoNg_MnF.js → touch-BnMyy9tr.js} +4 -4
- package/dist/vite.js +2 -2
- package/editor/assets/{agent8-DbX_msaO.js → agent8-PlFHzJsh.js} +1 -1
- package/editor/assets/{debug-C-kMxdl1.js → debug-CebV7CDW.js} +1 -1
- package/editor/assets/index-CdbsY31G.js +10958 -0
- package/editor/index.html +1 -1
- package/package.json +1 -1
- package/schemas/scene.schema.json +213 -0
- package/skills/incanto-assets.md +4 -1
- package/skills/incanto-audio.md +97 -10
- package/skills/incanto-building-2d-games.md +20 -3
- package/skills/incanto-editor.md +48 -11
- package/skills/incanto-gameplay-behaviors.md +14 -5
- package/skills/incanto-hud.md +7 -0
- package/skills/incanto-multiplayer.md +39 -3
- package/skills/incanto-node-reference.md +34 -0
- package/skills/incanto-physics-and-input.md +38 -0
- package/skills/incanto-verifying-your-game.md +75 -1
- package/skills/incanto-web-integration.md +1 -0
- package/templates/agent8-server.ts +79 -2
- package/templates-app/beacon-isle-3d/index.html +3 -3
- package/templates-app/beacon-isle-3d/package.json +1 -1
- package/templates-app/tps-3d/index.html +3 -3
- package/templates-app/tps-3d/package.json +1 -1
- package/templates-app/village-quest-3d/index.html +3 -3
- package/templates-app/village-quest-3d/package.json +1 -1
- package/dist/register-CLVhzWcI.js +0 -374
- package/editor/assets/index-CUc1U7wm.js +0 -10951
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { A as registerBehavior, n as loadScene, s as resolveViewport } from "./loader-
|
|
2
|
-
import {
|
|
1
|
+
import { A as registerBehavior, n as loadScene, s as resolveViewport } from "./loader-BTkHYrQn.js";
|
|
2
|
+
import { x as Engine } from "./register-BTomIiYG.js";
|
|
3
3
|
import { t as IncantoError } from "./errors-BpWbnbb_.js";
|
|
4
|
-
import { n as startRecording } from "./replay-BgKxGcXH.js";
|
|
5
4
|
import { n as jsonEquals, t as jsonClone } from "./json-BLk7H2Qa.js";
|
|
6
|
-
import { i as getNodeSchema, s as mergeStaticProps } from "./registry-
|
|
7
|
-
import { n as
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { t as
|
|
5
|
+
import { i as getNodeSchema, s as mergeStaticProps } from "./registry-C7u42TID.js";
|
|
6
|
+
import { n as startRecording } from "./replay-DYNUL4BU.js";
|
|
7
|
+
import { n as registerGameplayBehaviors } from "./gameplay-CRYw_Q-T.js";
|
|
8
|
+
import { t as registerNodes2D } from "./register-DROK2l7J.js";
|
|
9
|
+
import { n as registerNodes3D, t as resolveEnvironmentHdri } from "./environment-presets-SkGanr2s.js";
|
|
10
|
+
import { n as registerNodesNet, t as createSplitScreen } from "./split-screen-CaF9hO7g.js";
|
|
11
11
|
import { Box3, Euler, Matrix4, PerspectiveCamera, Quaternion, Vector3 } from "three";
|
|
12
12
|
//#region src/test/framing.ts
|
|
13
13
|
/**
|
|
@@ -1045,12 +1045,12 @@ async function makeProbe(json, opts) {
|
|
|
1045
1045
|
};
|
|
1046
1046
|
}
|
|
1047
1047
|
/** Let the character settle onto whatever it is standing on. */
|
|
1048
|
-
function settle(probe) {
|
|
1048
|
+
function settle$1(probe) {
|
|
1049
1049
|
probe.step(60);
|
|
1050
1050
|
}
|
|
1051
1051
|
/** Hold one direction and learn what this character's plateau actually is. */
|
|
1052
1052
|
function measureAccel(probe, move) {
|
|
1053
|
-
settle(probe);
|
|
1053
|
+
settle$1(probe);
|
|
1054
1054
|
const before = probe.pos();
|
|
1055
1055
|
probe.engine.input.setActionVector(move, 1, 0);
|
|
1056
1056
|
let latency = null;
|
|
@@ -1077,7 +1077,7 @@ function measureAccel(probe, move) {
|
|
|
1077
1077
|
* pass would have to guess the target.
|
|
1078
1078
|
*/
|
|
1079
1079
|
function timeToFullSpeed(probe, move, top) {
|
|
1080
|
-
settle(probe);
|
|
1080
|
+
settle$1(probe);
|
|
1081
1081
|
const target = top * .95;
|
|
1082
1082
|
probe.engine.input.setActionVector(move, 1, 0);
|
|
1083
1083
|
for (let f = 1; f <= 600; f++) {
|
|
@@ -1091,7 +1091,7 @@ function timeToFullSpeed(probe, move, top) {
|
|
|
1091
1091
|
return null;
|
|
1092
1092
|
}
|
|
1093
1093
|
function timeToStop(probe, move, top) {
|
|
1094
|
-
settle(probe);
|
|
1094
|
+
settle$1(probe);
|
|
1095
1095
|
probe.engine.input.setActionVector(move, 1, 0);
|
|
1096
1096
|
for (let f = 0; f < 600 && probe.speed() < top * .9; f++) probe.step();
|
|
1097
1097
|
probe.engine.input.setActionVector(move, 0, 0);
|
|
@@ -1102,7 +1102,7 @@ function timeToStop(probe, move, top) {
|
|
|
1102
1102
|
return null;
|
|
1103
1103
|
}
|
|
1104
1104
|
function turnaround(probe, move, top) {
|
|
1105
|
-
settle(probe);
|
|
1105
|
+
settle$1(probe);
|
|
1106
1106
|
probe.engine.input.setActionVector(move, 1, 0);
|
|
1107
1107
|
for (let f = 0; f < 600 && probe.speed() < top * .9; f++) probe.step();
|
|
1108
1108
|
probe.engine.input.setActionVector(move, -1, 0);
|
|
@@ -1114,7 +1114,7 @@ function turnaround(probe, move, top) {
|
|
|
1114
1114
|
return null;
|
|
1115
1115
|
}
|
|
1116
1116
|
function jumpShape(probe, jump) {
|
|
1117
|
-
settle(probe);
|
|
1117
|
+
settle$1(probe);
|
|
1118
1118
|
const floor = probe.pos()[1];
|
|
1119
1119
|
probe.engine.input.pressAction(jump);
|
|
1120
1120
|
probe.step();
|
|
@@ -1158,7 +1158,7 @@ async function measureCoyote(json, opts, jump) {
|
|
|
1158
1158
|
const fired = async (delayFrames) => {
|
|
1159
1159
|
const { probe } = await makeProbe(json, opts);
|
|
1160
1160
|
if (!probe) return false;
|
|
1161
|
-
settle(probe);
|
|
1161
|
+
settle$1(probe);
|
|
1162
1162
|
const p = probe.pos();
|
|
1163
1163
|
probe.player.position = [
|
|
1164
1164
|
p[0],
|
|
@@ -1194,7 +1194,7 @@ async function measureBuffer(json, opts, jump) {
|
|
|
1194
1194
|
const survived = async (earlyFrames) => {
|
|
1195
1195
|
const { probe } = await makeProbe(json, opts);
|
|
1196
1196
|
if (!probe) return false;
|
|
1197
|
-
settle(probe);
|
|
1197
|
+
settle$1(probe);
|
|
1198
1198
|
const floor = probe.pos()[1];
|
|
1199
1199
|
probe.player.position = [
|
|
1200
1200
|
probe.pos()[0],
|
|
@@ -1211,7 +1211,7 @@ async function measureBuffer(json, opts, jump) {
|
|
|
1211
1211
|
const second = await makeProbe(json, opts);
|
|
1212
1212
|
if (!second.probe) return false;
|
|
1213
1213
|
const p2 = second.probe;
|
|
1214
|
-
settle(p2);
|
|
1214
|
+
settle$1(p2);
|
|
1215
1215
|
p2.player.position = [
|
|
1216
1216
|
p2.pos()[0],
|
|
1217
1217
|
floor + DROP,
|
|
@@ -1353,6 +1353,103 @@ function feelText(report) {
|
|
|
1353
1353
|
return lines.join("\n");
|
|
1354
1354
|
}
|
|
1355
1355
|
//#endregion
|
|
1356
|
+
//#region src/test/multiplay.ts
|
|
1357
|
+
/** Run a whole match headlessly and report what the clients ended up sharing. */
|
|
1358
|
+
async function playMultiplayer(opts) {
|
|
1359
|
+
const count = opts.players ?? 2;
|
|
1360
|
+
const seconds = opts.seconds ?? 5;
|
|
1361
|
+
const stepMs = opts.stepMs ?? 1e3 / 60;
|
|
1362
|
+
const accounts = Array.from({ length: count }, (_, i) => `p${i + 1}`);
|
|
1363
|
+
const { players, dispose } = await createSplitScreen({
|
|
1364
|
+
scene: opts.scene,
|
|
1365
|
+
...opts.server ? { server: opts.server } : {},
|
|
1366
|
+
...opts.scenes ? { scenes: opts.scenes } : {},
|
|
1367
|
+
accounts,
|
|
1368
|
+
setup: () => {}
|
|
1369
|
+
});
|
|
1370
|
+
await opts.start?.(players);
|
|
1371
|
+
await settle();
|
|
1372
|
+
const frames = Math.max(1, Math.round(seconds * 1e3 / stepMs));
|
|
1373
|
+
let now = 0;
|
|
1374
|
+
for (let frame = 0; frame < frames; frame++) {
|
|
1375
|
+
now += stepMs;
|
|
1376
|
+
for (const player of players) {
|
|
1377
|
+
await opts.drive?.(player, frame, now / 1e3);
|
|
1378
|
+
player.engine.tick(now);
|
|
1379
|
+
}
|
|
1380
|
+
if (frame % 6 === 0) await settle();
|
|
1381
|
+
}
|
|
1382
|
+
await settle();
|
|
1383
|
+
const report = summarize(players, seconds, frames);
|
|
1384
|
+
dispose();
|
|
1385
|
+
return report;
|
|
1386
|
+
}
|
|
1387
|
+
/** Give the server's promise queue a turn. */
|
|
1388
|
+
function settle() {
|
|
1389
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
1390
|
+
}
|
|
1391
|
+
function summarize(players, seconds, frames) {
|
|
1392
|
+
const rooms = players.map((p) => p.manager.roomId);
|
|
1393
|
+
const detail = players.map((p) => ({
|
|
1394
|
+
account: p.account,
|
|
1395
|
+
room: p.manager.roomId,
|
|
1396
|
+
spawned: spawnerCounts(p.engine),
|
|
1397
|
+
sawAccounts: Object.keys(p.manager.latestUserStates ?? {}).filter((a) => a !== p.account),
|
|
1398
|
+
errors: p.engine.stats().errors
|
|
1399
|
+
}));
|
|
1400
|
+
const problems = multiplayProblems(detail, rooms);
|
|
1401
|
+
return {
|
|
1402
|
+
ok: problems.length === 0,
|
|
1403
|
+
seconds,
|
|
1404
|
+
frames,
|
|
1405
|
+
rooms,
|
|
1406
|
+
players: detail,
|
|
1407
|
+
roomState: players[0]?.manager.latestRoomState ?? {},
|
|
1408
|
+
problems
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
/**
|
|
1412
|
+
* What is wrong with a finished match, in the order it matters.
|
|
1413
|
+
*
|
|
1414
|
+
* Pure, and exported, because every one of these is a SILENT failure — the test
|
|
1415
|
+
* for "the clients ended up in different rooms" must not depend on being able
|
|
1416
|
+
* to reproduce different rooms.
|
|
1417
|
+
*/
|
|
1418
|
+
function multiplayProblems(detail, rooms) {
|
|
1419
|
+
const problems = [];
|
|
1420
|
+
if (new Set(rooms).size > 1) problems.push(`the clients joined ${new Set(rooms).size} different rooms (${rooms.join(", ")}) — nobody can see anybody. A scene's \`multiplayer.room\` of "auto" is SERVER-ASSIGNED; name a room, or let the harness pin one.`);
|
|
1421
|
+
else if (detail.length > 1) {
|
|
1422
|
+
const blind = detail.filter((d) => d.sawAccounts.length === 0);
|
|
1423
|
+
if (blind.length > 0) problems.push(`${blind.map((d) => d.account).join(", ")} never saw another account's state — an owner node replicates only what its \`network.sync\` lists, and only when it changes.`);
|
|
1424
|
+
const empty = detail.filter((d) => Object.keys(d.spawned).length > 0 && Object.values(d.spawned).every((n) => n === 0));
|
|
1425
|
+
if (empty.length > 0 && blind.length === 0) problems.push(`${empty.map((d) => d.account).join(", ")} has a NetworkSpawner that materialised nothing — check its \`scene\` key is registered and its \`source\` names a real collection.`);
|
|
1426
|
+
}
|
|
1427
|
+
for (const d of detail) if (d.errors > 0) problems.push(`${d.account}: ${d.errors} frame error(s) — read engine.log.`);
|
|
1428
|
+
return problems;
|
|
1429
|
+
}
|
|
1430
|
+
function spawnerCounts(engine) {
|
|
1431
|
+
const out = {};
|
|
1432
|
+
const root = engine.scene?.tree.root;
|
|
1433
|
+
if (!root) return out;
|
|
1434
|
+
const walk = (node) => {
|
|
1435
|
+
if (node.constructor.name === "NetworkSpawner") out[node.name] = node.children.length;
|
|
1436
|
+
for (const child of node.children) walk(child);
|
|
1437
|
+
};
|
|
1438
|
+
walk(root);
|
|
1439
|
+
return out;
|
|
1440
|
+
}
|
|
1441
|
+
/** The report as the one paragraph a reader needs. */
|
|
1442
|
+
function multiplayText(report) {
|
|
1443
|
+
const lines = [`multiplayer: ${report.players.length} clients, ${report.seconds}s, room ${[...new Set(report.rooms)].join(" + ")}`];
|
|
1444
|
+
for (const p of report.players) {
|
|
1445
|
+
const spawned = Object.entries(p.spawned).map(([name, n]) => `${name}=${n}`).join(" ");
|
|
1446
|
+
lines.push(` ${p.account}: sees [${p.sawAccounts.join(", ") || "—"}]` + (spawned ? ` spawned ${spawned}` : "") + (p.errors ? ` · ${p.errors} error(s)` : ""));
|
|
1447
|
+
}
|
|
1448
|
+
for (const problem of report.problems) lines.push(` ✗ ${problem}`);
|
|
1449
|
+
if (report.ok) lines.push(" ✓ every client in one room, seeing the others");
|
|
1450
|
+
return lines.join("\n");
|
|
1451
|
+
}
|
|
1452
|
+
//#endregion
|
|
1356
1453
|
//#region src/test/verify-ladder.ts
|
|
1357
1454
|
const MARK = {
|
|
1358
1455
|
pass: "✓",
|
|
@@ -1621,10 +1718,10 @@ async function runScript(json, opts) {
|
|
|
1621
1718
|
engine.setScene(scene);
|
|
1622
1719
|
const physics = opts.physics ?? "auto";
|
|
1623
1720
|
if (physics === "2d" || physics === "auto" && scene.dimension === "2d") {
|
|
1624
|
-
const { enablePhysics2D } = await import("./physics-2d-
|
|
1721
|
+
const { enablePhysics2D } = await import("./physics-2d-BhZZ-HAp.js").then((n) => n.r);
|
|
1625
1722
|
await enablePhysics2D(engine);
|
|
1626
1723
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
1627
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
1724
|
+
const { enablePhysics3D } = await import("./physics-3d-DmyO2oaN.js").then((n) => n.r);
|
|
1628
1725
|
await enablePhysics3D(engine);
|
|
1629
1726
|
}
|
|
1630
1727
|
const failures = [];
|
|
@@ -1737,10 +1834,10 @@ async function createPlaySession(json, opts = {}) {
|
|
|
1737
1834
|
engine.setScene(scene);
|
|
1738
1835
|
const physics = opts.physics ?? "auto";
|
|
1739
1836
|
if (physics === "2d" || physics === "auto" && scene.dimension === "2d") {
|
|
1740
|
-
const { enablePhysics2D } = await import("./physics-2d-
|
|
1837
|
+
const { enablePhysics2D } = await import("./physics-2d-BhZZ-HAp.js").then((n) => n.r);
|
|
1741
1838
|
await enablePhysics2D(engine);
|
|
1742
1839
|
} else if (physics === "3d" || physics === "auto" && scene.dimension === "3d") {
|
|
1743
|
-
const { enablePhysics3D } = await import("./physics-3d-
|
|
1840
|
+
const { enablePhysics3D } = await import("./physics-3d-DmyO2oaN.js").then((n) => n.r);
|
|
1744
1841
|
await enablePhysics3D(engine);
|
|
1745
1842
|
}
|
|
1746
1843
|
const stepMs = 1e3 / (opts.fixedHz ?? 60);
|
|
@@ -1778,4 +1875,4 @@ async function createPlaySession(json, opts = {}) {
|
|
|
1778
1875
|
};
|
|
1779
1876
|
}
|
|
1780
1877
|
//#endregion
|
|
1781
|
-
export {
|
|
1878
|
+
export { framingText as S, failingReplays as _, registerAllNodes as a, playtestText as b, ladderText as c, multiplayText as d, playMultiplayer as f, facingText as g, facingReport as h, findFloatingProps as i, ladderVerdict as l, feelText as m, createPlaySession as n, runScript as o, feelReport as p, describeCapture as r, validateScene as s, captureScene as t, multiplayProblems as u, findPlayer as v, describeFraming as x, playtest as y };
|
package/dist/test.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { At as LogEntry, Dt as Node, P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-
|
|
2
|
-
import { c as JsonValue, i as SceneJson } from "./schema-CFeioQRE.js";
|
|
3
|
-
import { t as LoadSceneOptions } from "./loader-
|
|
4
|
-
import { l as auditScene, o as IncantoError, r as ReplayJson } from "./replay-
|
|
1
|
+
import { At as LogEntry, Dt as Node, P as Scene, b as Engine, n as BehaviorCtor } from "./behavior-Do0Da56m.js";
|
|
2
|
+
import { c as JsonValue, i as SceneJson, s as JsonObject } from "./schema-CFeioQRE.js";
|
|
3
|
+
import { t as LoadSceneOptions } from "./loader-Cu_7kJDy.js";
|
|
4
|
+
import { l as auditScene, o as IncantoError, r as ReplayJson } from "./replay-9Fy6C10F.js";
|
|
5
|
+
import { l as LocalGameServerOptions, n as SplitScreenPlayer } from "./split-screen-DNmcX1Pz.js";
|
|
5
6
|
|
|
6
7
|
//#region src/test/facing.d.ts
|
|
7
8
|
interface FacingOptions {
|
|
@@ -137,6 +138,60 @@ declare function feelReport(json: unknown, opts?: FeelOptions): Promise<FeelRepo
|
|
|
137
138
|
/** Numbers, not grades. What "good" means here is a genre decision. */
|
|
138
139
|
declare function feelText(report: FeelReport): string;
|
|
139
140
|
//#endregion
|
|
141
|
+
//#region src/test/multiplay.d.ts
|
|
142
|
+
interface MultiplayOptions {
|
|
143
|
+
/** The scene every client loads (the same JSON, cloned per client). */
|
|
144
|
+
scene: unknown;
|
|
145
|
+
/** Your `Server` class. Omitted: the built-in room protocol only. */
|
|
146
|
+
server?: LocalGameServerOptions["server"];
|
|
147
|
+
/** Scenes a `NetworkSpawner` names (`remote-player`, `coin`, …). */
|
|
148
|
+
scenes?: Record<string, unknown>;
|
|
149
|
+
/** How many clients. Default 2. */
|
|
150
|
+
players?: number;
|
|
151
|
+
/** Simulated seconds. Default 5. */
|
|
152
|
+
seconds?: number;
|
|
153
|
+
/** Fixed frame length in ms. Default 16.67 (60fps). */
|
|
154
|
+
stepMs?: number;
|
|
155
|
+
/** Per-frame hook: drive input, call server functions, assert mid-match. */
|
|
156
|
+
drive?: (player: SplitScreenPlayer, frame: number, seconds: number) => void | Promise<void>;
|
|
157
|
+
/** Called once after every client has joined, before the match runs. */
|
|
158
|
+
start?: (players: SplitScreenPlayer[]) => void | Promise<void>;
|
|
159
|
+
}
|
|
160
|
+
interface MultiplayPlayer {
|
|
161
|
+
account: string;
|
|
162
|
+
room: string;
|
|
163
|
+
/** Nodes each `NetworkSpawner` in this client's scene ended up holding. */
|
|
164
|
+
spawned: Record<string, number>;
|
|
165
|
+
/** Other accounts this client's room state actually carried. */
|
|
166
|
+
sawAccounts: string[];
|
|
167
|
+
/** Frame errors on this client's engine. */
|
|
168
|
+
errors: number;
|
|
169
|
+
}
|
|
170
|
+
interface MultiplayReport {
|
|
171
|
+
ok: boolean;
|
|
172
|
+
seconds: number;
|
|
173
|
+
frames: number;
|
|
174
|
+
/** The rooms the clients joined — more than one is the silent killer. */
|
|
175
|
+
rooms: string[];
|
|
176
|
+
players: MultiplayPlayer[];
|
|
177
|
+
/** The last room state, from the first client. */
|
|
178
|
+
roomState: JsonObject;
|
|
179
|
+
/** What is wrong, in the order it matters. Empty when `ok`. */
|
|
180
|
+
problems: string[];
|
|
181
|
+
}
|
|
182
|
+
/** Run a whole match headlessly and report what the clients ended up sharing. */
|
|
183
|
+
declare function playMultiplayer(opts: MultiplayOptions): Promise<MultiplayReport>;
|
|
184
|
+
/**
|
|
185
|
+
* What is wrong with a finished match, in the order it matters.
|
|
186
|
+
*
|
|
187
|
+
* Pure, and exported, because every one of these is a SILENT failure — the test
|
|
188
|
+
* for "the clients ended up in different rooms" must not depend on being able
|
|
189
|
+
* to reproduce different rooms.
|
|
190
|
+
*/
|
|
191
|
+
declare function multiplayProblems(detail: MultiplayPlayer[], rooms: string[]): string[];
|
|
192
|
+
/** The report as the one paragraph a reader needs. */
|
|
193
|
+
declare function multiplayText(report: MultiplayReport): string;
|
|
194
|
+
//#endregion
|
|
140
195
|
//#region src/test/playtest.d.ts
|
|
141
196
|
type Outcome = "won" | "lost" | "fell" | "error" | "stuck";
|
|
142
197
|
interface PlaytestOptions {
|
|
@@ -457,4 +512,4 @@ interface PlaySession {
|
|
|
457
512
|
*/
|
|
458
513
|
declare function createPlaySession(json: unknown, opts?: PlaySessionOptions): Promise<PlaySession>;
|
|
459
514
|
//#endregion
|
|
460
|
-
export { type FacingOptions, type FacingReport, type FeelOptions, type FeelReport, type FramingEntry, type FramingOptions, type FramingReport, GroundingIssue, GroundingOptions, type LadderVerdict, NodeCapture, type Outcome, PlaySession, PlaySessionOptions, type PlaytestOptions, type PlaytestReport, type PlaytestRun, RunContext, RunFailure, RunResult, RunScriptOptions, type RungResult, type RungStatus, SceneCapture, ScriptStep, ValidateSceneOptions, ValidationResult, type Where, auditScene, captureScene, createPlaySession, describeCapture, describeFraming, facingReport, facingText, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, ladderText, ladderVerdict, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
|
515
|
+
export { type FacingOptions, type FacingReport, type FeelOptions, type FeelReport, type FramingEntry, type FramingOptions, type FramingReport, GroundingIssue, GroundingOptions, type LadderVerdict, type MultiplayOptions, type MultiplayPlayer, type MultiplayReport, NodeCapture, type Outcome, PlaySession, PlaySessionOptions, type PlaytestOptions, type PlaytestReport, type PlaytestRun, RunContext, RunFailure, RunResult, RunScriptOptions, type RungResult, type RungStatus, SceneCapture, ScriptStep, ValidateSceneOptions, ValidationResult, type Where, auditScene, captureScene, createPlaySession, describeCapture, describeFraming, facingReport, facingText, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, ladderText, ladderVerdict, multiplayProblems, multiplayText, playMultiplayer, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
package/dist/test.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { r as auditScene } from "./replay-
|
|
2
|
-
import { _ as
|
|
3
|
-
export { auditScene, captureScene, createPlaySession, describeCapture, describeFraming, facingReport, facingText, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, ladderText, ladderVerdict, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
|
1
|
+
import { r as auditScene } from "./replay-DYNUL4BU.js";
|
|
2
|
+
import { S as framingText, _ as failingReplays, a as registerAllNodes, b as playtestText, c as ladderText, d as multiplayText, f as playMultiplayer, g as facingText, h as facingReport, i as findFloatingProps, l as ladderVerdict, m as feelText, n as createPlaySession, o as runScript, p as feelReport, r as describeCapture, s as validateScene, t as captureScene, u as multiplayProblems, v as findPlayer, x as describeFraming, y as playtest } from "./test-CJRsciFk.js";
|
|
3
|
+
export { auditScene, captureScene, createPlaySession, describeCapture, describeFraming, facingReport, facingText, failingReplays, feelReport, feelText, findFloatingProps, findPlayer, framingText, ladderText, ladderVerdict, multiplayProblems, multiplayText, playMultiplayer, playtest, playtestText, registerAllNodes, runScript, validateScene };
|
|
@@ -80,8 +80,8 @@ var TouchControls = class {
|
|
|
80
80
|
const base = doc.createElement("div");
|
|
81
81
|
style(base, {
|
|
82
82
|
position: "absolute",
|
|
83
|
-
left:
|
|
84
|
-
bottom: "24px",
|
|
83
|
+
left: `calc(${24 + index * 140}px + env(safe-area-inset-left))`,
|
|
84
|
+
bottom: "calc(24px + env(safe-area-inset-bottom))",
|
|
85
85
|
width: "120px",
|
|
86
86
|
height: "120px",
|
|
87
87
|
borderRadius: "50%",
|
|
@@ -150,8 +150,8 @@ var TouchControls = class {
|
|
|
150
150
|
button.textContent = action;
|
|
151
151
|
style(button, {
|
|
152
152
|
position: "absolute",
|
|
153
|
-
right: "24px",
|
|
154
|
-
bottom:
|
|
153
|
+
right: "calc(24px + env(safe-area-inset-right))",
|
|
154
|
+
bottom: `calc(${24 + index * 80}px + env(safe-area-inset-bottom))`,
|
|
155
155
|
width: "64px",
|
|
156
156
|
height: "64px",
|
|
157
157
|
borderRadius: "50%",
|
package/dist/vite.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as VERSION } from "./src-
|
|
1
|
+
import { t as VERSION } from "./src-C9xyZW7M.js";
|
|
2
2
|
import { n as diffSignatures } from "./frame-report-njybhZon.js";
|
|
3
|
-
import { s as validateScene } from "./test-
|
|
3
|
+
import { s as validateScene } from "./test-CJRsciFk.js";
|
|
4
4
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, join, normalize, relative, resolve, sep } from "node:path";
|
|
6
6
|
//#region src/vite/frame-endpoint.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"./index-
|
|
1
|
+
import{n as e}from"./index-CdbsY31G.js";async function t(t){return new n((await e(()=>import(`./GameServer-C56iOUgF.js`),[],import.meta.url)).GameServer,t)}var n=class{raw;active=new Map;reconnecting=!1;disposed=!1;constructor(e,t){this.raw=new e({...t})}get account(){return this.raw.account}get connected(){return this.raw.connected}connect(){return this.raw.connected?Promise.resolve(!0):(this.disposed=!1,this.rawConnect())}rawConnect(){return this.raw.connect({onDisconnect:()=>void this.reconnect()})}disconnect(){this.disposed=!0;for(let e of this.active.values())e.off();return this.active.clear(),this.raw.disconnect()}remoteFunction(e,t,n){return this.raw.remoteFunction(e,t,n)}track(e){let t=Symbol(`sub`),n={make:e,off:e()};return this.active.set(t,n),()=>{n.off(),this.active.delete(t)}}async reconnect(){if(!this.disposed&&!this.reconnecting){this.reconnecting=!0;try{await this.rawConnect();for(let e of this.active.values())e.off(),e.off=e.make()}finally{this.reconnecting=!1}}}subscribeRoomState(e,t){return this.track(()=>this.raw.subscribeRoomState(e,t))}subscribeRoomMyState(e,t){return this.track(()=>this.raw.subscribeRoomMyState(e,t))}subscribeRoomAllUserStates(e,t){return this.track(()=>this.raw.subscribeRoomAllUserStates(e,e=>{let n={};for(let t of e??[]){if(!t||typeof t.account!=`string`||t.__leaved)continue;let{account:e,__updated:r,__leaved:i,...a}=t;n[e]=a}t(n)}))}subscribeRoomCollection(e,t,n){return this.track(()=>this.raw.subscribeRoomCollection(e,t,({items:e})=>{let t={};for(let n of e??[])n&&typeof n.__id==`string`&&(t[n.__id]=n);n(t)}))}onRoomMessage(e,t,n){return this.track(()=>this.raw.onRoomMessage(e,t,n))}onRoomUserJoin(e,t){return this.track(()=>this.raw.onRoomUserJoin(e,t))}onRoomUserLeave(e,t){return this.track(()=>this.raw.onRoomUserLeave(e,t))}subscribeGlobalState(e){return this.track(()=>this.raw.subscribeGlobalState(e))}subscribeGlobalMyState(e){return this.track(()=>this.raw.subscribeGlobalMyState(e))}subscribeGlobalUserState(e,t){return this.track(()=>this.raw.subscribeGlobalUserState(e,t))}subscribeGlobalCollection(e,t){return this.track(()=>this.raw.subscribeGlobalCollection(e,({items:e})=>{let n={};for(let t of e??[])t&&typeof t.__id==`string`&&(n[t.__id]=t);t(n)}))}subscribeAsset(e,t){return this.track(()=>this.raw.subscribeAsset(e,t))}onGlobalMessage(e,t){return this.track(()=>this.raw.onGlobalMessage(e,t))}};export{t as createAgent8Server};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{i as e,r as t,t as n}from"./index-
|
|
1
|
+
import{i as e,r as t,t as n}from"./index-CdbsY31G.js";function r(e,t,n,r=1){let i=Math.min(e.x0,e.x1)*r,a=Math.max(e.x0,e.x1)*r,o=Math.min(e.y0,e.y1)*r,s=Math.max(e.y0,e.y1)*r,c=Math.max(0,Math.min(Math.round(i),t-1)),l=Math.max(0,Math.min(Math.round(o),n-1));return{x:c,y:l,w:Math.max(1,Math.min(Math.round(a-i),t-c)),h:Math.max(1,Math.min(Math.round(s-o),n-l))}}function i(e,t,n,r){let{x:i,y:a,w:o,h:s}=r,c=new Uint8ClampedArray(o*s*4);for(let n=0;n<s;n++){let r=((a+n)*t+i)*4,s=n*o*4;c.set(e.subarray(r,r+o*4),s)}return{pixels:c,width:o,height:s}}function a(e){if(!e)return`// no scene loaded`;let t=e.source??e;return JSON.stringify(t,null,2)}async function o(e){let t=globalThis.navigator;if(!t?.clipboard?.writeText)return!1;try{return await t.clipboard.writeText(e),!0}catch{return!1}}async function s(e,t){let n=globalThis,r=n.navigator?.clipboard?.write;if(!r||!n.ImageData||!n.ClipboardItem)return!1;try{let i=e.createElement(`canvas`);i.width=t.width,i.height=t.height;let a=i.getContext(`2d`);if(!a)return!1;a.putImageData(new n.ImageData(t.pixels,t.width,t.height),0,0);let o=await new Promise(e=>i.toBlob(e,`image/png`));return o?(await r.call(n.navigator?.clipboard,[new n.ClipboardItem({"image/png":o})]),!0):!1}catch{return!1}}var c=[`nw`,`n`,`ne`,`e`,`se`,`s`,`sw`,`w`],l={nw:[0,0],n:[.5,0],ne:[1,0],e:[1,.5],se:[1,1],s:[.5,1],sw:[0,1],w:[0,.5]},u={nw:`nwse-resize`,se:`nwse-resize`,ne:`nesw-resize`,sw:`nesw-resize`,n:`ns-resize`,s:`ns-resize`,e:`ew-resize`,w:`ew-resize`},d=(e,t,n)=>Math.max(t,Math.min(n,e));function f(e){return{x:Math.min(e.x0,e.x1),y:Math.min(e.y0,e.y1),w:Math.abs(e.x1-e.x0),h:Math.abs(e.y1-e.y0)}}function p(e,t,n,r,i,a){let o=e.x,s=e.y,c=e.x+e.w,l=e.y+e.h;return t.includes(`w`)&&(o=d(o+n,0,i)),t.includes(`e`)&&(c=d(c+n,0,i)),t.includes(`n`)&&(s=d(s+r,0,a)),t.includes(`s`)&&(l=d(l+r,0,a)),{x:Math.min(o,c),y:Math.min(s,l),w:Math.max(1,Math.abs(c-o)),h:Math.max(1,Math.abs(l-s))}}function m(e,t,n,r,i){return{...e,x:d(e.x+t,0,Math.max(0,r-e.w)),y:d(e.y+n,0,Math.max(0,i-e.h))}}function h(e,t,n,r,i,a=8){let o=d(e.x+e.w/2-t/2,a,Math.max(a,r-t-a)),s=e.y+e.h+a,c=e.y-n-a;return{x:o,y:s+n<=i-a?s:c>=a?c:d(s,a,Math.max(a,i-n-a))}}function g(e,t,n,r,i,a,o=180,s=120){let c=Math.max(o,n),l=Math.max(s,r);return{x:Math.min(Math.max(0,e),Math.max(0,i-c)),y:Math.min(Math.max(0,t),Math.max(0,a-l)),w:c,h:l}}function _(e,t,n,r,i,a=4){let o=Math.min(Math.max(0,e.x),Math.max(0,r-t)),s=e.y+e.h+a,c=e.y-n-a;return s+n<=i?{x:o,y:s}:c>=0?{x:o,y:c}:{x:o,y:Math.min(Math.max(0,s),Math.max(0,i-n))}}function v(e,t,n){let r=e=>{e.key===`Escape`&&n()},i=e=>{let r=e.target;if(r){for(let e of t)if(e&&(e===r||e.contains?.(r)))return;n()}};return e.addEventListener?.(`keydown`,r),e.addEventListener?.(`pointerdown`,i),()=>{e.removeEventListener?.(`keydown`,r),e.removeEventListener?.(`pointerdown`,i)}}function y(e,t){for(let[n,r]of Object.entries(t))e.style[n]=r}var b=`rgba(18, 20, 26, 0.92)`,x=`1px solid rgba(255,255,255,0.14)`,S=`12px ui-monospace, SFMono-Regular, Menlo, monospace`,C=class{host;el;body;onClose=()=>{};x;y;w;h;constructor(e,t,n,r){this.host=t,this.x=r.x,this.y=r.y,this.w=r.w,this.h=r.h,this.el=e.createElement(`div`),y(this.el,{position:`absolute`,background:b,border:x,borderRadius:`8px`,color:`rgba(255,255,255,0.88)`,font:S,display:`flex`,flexDirection:`column`,overflow:`hidden`,zIndex:`40`,pointerEvents:`auto`,boxShadow:`0 8px 28px rgba(0,0,0,0.45)`});let i=e.createElement(`div`);i.textContent=n,y(i,{padding:`6px 28px 6px 10px`,background:`rgba(255,255,255,0.07)`,cursor:`move`,userSelect:`none`,touchAction:`none`,fontWeight:`700`}),this.el.appendChild(i);let a=e.createElement(`div`);a.textContent=`×`,a.title=`close`,y(a,{position:`absolute`,top:`2px`,right:`8px`,cursor:`pointer`,fontSize:`16px`,lineHeight:`20px`,opacity:`0.7`}),a.addEventListener(`click`,()=>this.onClose()),this.el.appendChild(a),this.body=e.createElement(`div`),y(this.body,{flex:`1`,overflow:`auto`,padding:`8px 10px`}),this.el.appendChild(this.body);let o=e.createElement(`div`);o.textContent=`◢`,y(o,{position:`absolute`,right:`2px`,bottom:`0`,cursor:`nwse-resize`,opacity:`0.5`,userSelect:`none`,touchAction:`none`}),this.el.appendChild(o),this.wireDrag(i,(e,t)=>{this.x+=e,this.y+=t,this.layout()}),this.wireDrag(o,(e,t)=>{this.w+=e,this.h+=t,this.layout()}),this.layout(),t.appendChild(this.el)}remove(){this.el.remove()}layout(){let e=this.host.getBoundingClientRect(),t=g(this.x,this.y,this.w,this.h,e.width,e.height);this.x=t.x,this.y=t.y,this.w=t.w,this.h=t.h,y(this.el,{left:`${t.x}px`,top:`${t.y}px`,width:`${t.w}px`,height:`${t.h}px`})}wireDrag(e,t){let n=null,r=0,i=0;e.addEventListener(`pointerdown`,t=>{n=t.pointerId,r=t.clientX,i=t.clientY,e.setPointerCapture?.(t.pointerId)}),e.addEventListener(`pointermove`,e=>{e.pointerId===n&&(t(e.clientX-r,e.clientY-i),r=e.clientX,i=e.clientY)});let a=e=>{e.pointerId===n&&(n=null)};e.addEventListener(`pointerup`,a),e.addEventListener(`pointercancel`,a)}},w=4,T=`#6ee7dc`,E=`rgba(18,20,26,0.94)`,D=`12px ui-monospace, Menlo, monospace`;function O(e){let{doc:t,container:n}=e,r=t.createElement(`div`);y(r,{position:`absolute`,inset:`0`,cursor:`crosshair`,zIndex:`80`,pointerEvents:`auto`,userSelect:`none`,touchAction:`none`});let i=t.createElement(`div`);y(i,{position:`absolute`,inset:`0`,background:`rgba(10,14,20,0.35)`,pointerEvents:`none`}),r.appendChild(i);let a=t.createElement(`div`);y(a,{position:`absolute`,outline:`1px solid ${T}`,boxShadow:`0 0 0 9999px rgba(10,14,20,0.35)`,cursor:`grab`,display:`none`,pointerEvents:`auto`}),r.appendChild(a);let o=t.createElement(`div`);y(o,{position:`absolute`,padding:`2px 6px`,borderRadius:`4px`,background:E,color:T,font:D,pointerEvents:`none`,display:`none`,whiteSpace:`nowrap`}),r.appendChild(o);let s=t.createElement(`div`);s.textContent=`drag to select · Esc to cancel`,y(s,{position:`absolute`,top:`10px`,left:`50%`,transform:`translateX(-50%)`,padding:`4px 10px`,borderRadius:`6px`,background:E,color:`rgba(255,255,255,0.85)`,font:D,pointerEvents:`none`,whiteSpace:`nowrap`}),r.appendChild(s);let d=null,g=`idle`,_={x:0,y:0},v={x:0,y:0,w:0,h:0},b=()=>({w:n.clientWidth||1,h:n.clientHeight||1});for(let e of c){let n=t.createElement(`div`),[r,i]=l[e];y(n,{position:`absolute`,left:`${r*100}%`,top:`${i*100}%`,width:`10px`,height:`10px`,marginLeft:`-5px`,marginTop:`-5px`,borderRadius:`2px`,background:T,border:`1px solid rgba(10,14,20,0.75)`,cursor:u[e],pointerEvents:`auto`}),n.addEventListener(`pointerdown`,t=>{k(t,e)}),a.appendChild(n)}let x=t.createElement(`div`);y(x,{position:`absolute`,display:`none`,gap:`6px`,padding:`6px`,borderRadius:`8px`,background:E,border:`1px solid rgba(255,255,255,0.18)`,font:D,pointerEvents:`auto`,whiteSpace:`nowrap`,boxShadow:`0 4px 16px rgba(0,0,0,0.45)`});let S=(e,n,r)=>{let i=t.createElement(`div`);return i.textContent=e,y(i,{padding:`5px 10px`,borderRadius:`5px`,cursor:`pointer`,color:n?`#08121a`:`rgba(255,255,255,0.85)`,background:n?T:`rgba(255,255,255,0.08)`,userSelect:`none`}),i.addEventListener(`pointerdown`,e=>{C(e),r()}),i};x.appendChild(S(`Copy to clipboard`,!0,()=>N())),x.appendChild(S(`Cancel`,!1,()=>I())),r.appendChild(x);function C(e){e.preventDefault?.(),e.stopPropagation?.()}function O(e){let t=e,r=n.getBoundingClientRect?.()??{left:0,top:0};return{x:(t.clientX??0)-r.left,y:(t.clientY??0)-r.top}}function k(e,t){C(e),_=O(e),g=t,t===`create`?d={x:_.x,y:_.y,w:0,h:0}:d&&(v=d),y(x,{display:`none`}),y(a,{cursor:t===`move`?`grabbing`:`crosshair`});let n=e.pointerId;n!==void 0&&r.setPointerCapture?.(n),A()}function A(){if(!d){y(a,{display:`none`}),y(o,{display:`none`}),y(i,{display:`block`});return}y(i,{display:`none`}),y(a,{display:`block`,left:`${d.x}px`,top:`${d.y}px`,width:`${d.w}px`,height:`${d.h}px`});let t=e.scale();o.textContent=`${Math.round(d.w*t)}×${Math.round(d.h*t)}`;let n=d.y-24;y(o,{display:`block`,left:`${Math.max(2,d.x)}px`,top:`${n>=2?n:d.y+4}px`})}function j(){if(!d||d.w<w||d.h<w)return;y(x,{display:`flex`});let e=b(),t=x.offsetWidth||200,n=x.offsetHeight||36,r=h(d,t,n,e.w,e.h);y(x,{left:`${r.x}px`,top:`${r.y}px`})}r.addEventListener(`pointerdown`,e=>{k(e,`create`)}),a.addEventListener(`pointerdown`,e=>{k(e,`move`)}),r.addEventListener(`pointermove`,e=>{if(g===`idle`||!d)return;let t=O(e),n=b();if(g===`create`){let e=f({x0:_.x,y0:_.y,x1:t.x,y1:t.y});d={x:Math.max(0,Math.min(e.x,n.w)),y:Math.max(0,Math.min(e.y,n.h)),w:Math.min(e.w,n.w-Math.max(0,Math.min(e.x,n.w))),h:Math.min(e.h,n.h-Math.max(0,Math.min(e.y,n.h)))}}else d=g===`move`?m(v,t.x-_.x,t.y-_.y,n.w,n.h):p(v,g,t.x-_.x,t.y-_.y,n.w,n.h);A()});let M=()=>{g!==`idle`&&(g===`create`&&d&&(d.w<w||d.h<w)&&(d=null),g=`idle`,y(a,{cursor:`grab`}),s.textContent=d?`drag the handles or the middle · Enter to copy · Esc to cancel`:`drag to select · Esc to cancel`,A(),j())};r.addEventListener(`pointerup`,M),r.addEventListener(`pointercancel`,M);function N(){let t=d;I(),t&&e.onCopy(t)}let P=e=>{let t=e.key;t===`Escape`?I():t===`Enter`&&d&&N()};t.addEventListener?.(`keydown`,P);let F=!1;function I(){F||(F=!0,t.removeEventListener?.(`keydown`,P),r.remove(),e.onClose())}return n.appendChild(r),{element:r,close:I}}var k=96,A=200,j=8192;function M(e){return Array.isArray(e)?`Array(${e.length})`:`Object(${Object.keys(e).length} keys)`}function N(e,t){if(Array.isArray(e)&&e.some(e=>typeof e==`object`&&!!e)){let t=e.slice(0,A).map(e=>JSON.stringify(e)),n=e.length-A;return t.join(`
|
|
2
2
|
`)+(n>0?`\n… ${n} more`:``)}return t.length>j?`${t.slice(0,j)} … (${t.length-j} more chars)`:t}function P(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}var F=2,I=4;function L(e,t={}){let n=t.doc??(typeof document<`u`?document:null);if(!n)return null;let r=t.container??(typeof document<`u`?document.body:null);return!r||typeof r.appendChild!=`function`?null:new B(e,r,n,t.statsSource,t.actions,t.frameSource)}var R=300,z=[`log`,`info`,`warn`,`error`,`debug`],B=class{engine;container;doc;statsSource;actions;frameSource;panels=new Map;cleanups=[];menuButton;dropdown=null;dismissDropdown=null;menuPos={x:8,y:8};selected=null;collapsedFlags=new Map;statsChip=null;logRows=[];levelEnabled={debug:!0,info:!0,warn:!0,error:!0};consoleCapture=!1;consolePatched=[];frame=0;editing=0;detailOpen=new WeakMap;resumeScale=1;timeEls=null;hovering=!1;constructor(e,t,n,r,i=[],a){this.engine=e,this.container=t,this.doc=n,this.statsSource=r,this.actions=i,this.frameSource=a,this.menuButton=n.createElement(`div`),this.menuButton.textContent=`☰ debug`,y(this.menuButton,{position:`absolute`,top:`8px`,left:`8px`,padding:`4px 10px`,background:`rgba(18,20,26,0.85)`,border:`1px solid rgba(255,255,255,0.18)`,borderRadius:`6px`,color:`rgba(255,255,255,0.85)`,font:`12px ui-monospace, Menlo, monospace`,cursor:`pointer`,userSelect:`none`,zIndex:`50`,pointerEvents:`auto`}),this.bindMenuDrag(),t.style.position||(t.style.position=`relative`),t.appendChild(this.menuButton),this.cleanups.push(e.log.added.connect(e=>{this.pushLog({level:e.level,source:`engine`,text:e.parts.map(U).join(` `)})})),this.cleanups.push(e.sceneChanged.connect(()=>{this.selected=null,this.applyColliderScope(),this.renderExplorer(),this.renderInspector()})),this.cleanups.push(e.updated.connect(()=>{this.frame+=1,this.frame%30==0&&(this.renderExplorer(),this.renderStats(),this.editing===0&&!this.hovering&&this.renderInspector(),this.syncTime())}))}isOpen(e){return e===`copyScene`||e===`captureRegion`?!1:e===`colliders`?this.colliderMode!==`off`:e===`stats`?this.statsChip!==null:this.panels.has(e)}colliderMode=`off`;setColliders(e){this.colliderMode=e,this.applyColliderScope()}applyColliderScope(){let e=this.colliderMode;for(let t of n(`2d`).concat(n(`3d`)))t.debugDraw=e!==`off`,t.debugScope=e===`selected`?this.selected:null}open(e){if(e===`stats`){this.openStatsChip();return}if(this.panels.has(e)){e===`explorer`&&this.renderExplorer(),e===`inspector`&&this.renderInspector(),e===`logs`&&this.renderLogs(),e===`time`&&this.renderTime();return}let t=new C(this.doc,this.container,{explorer:`Explorer`,inspector:`Inspector`,logs:`Logs`,time:`Time`}[e],{explorer:{x:12,y:44,w:240,h:320},inspector:{x:264,y:44,w:280,h:320},logs:{x:12,y:380,w:532,h:200},time:{x:556,y:44,w:260,h:150}}[e]);t.onClose=()=>this.close(e),e===`inspector`&&(t.body.addEventListener(`pointerenter`,()=>{this.hovering=!0}),t.body.addEventListener(`pointerleave`,()=>{this.hovering=!1})),this.panels.set(e,t),e===`explorer`&&this.renderExplorer(),e===`inspector`&&this.renderInspector(),e===`logs`&&this.renderLogs(),e===`time`&&this.renderTime()}close(e){if(e===`inspector`&&(this.hovering=!1),e===`stats`){this.statsChip?.remove(),this.statsChip=null;return}let t=this.panels.get(e);t&&(t.remove(),this.panels.delete(e))}toggle(e){if(e===`copyScene`){this.copyScene();return}if(e===`captureRegion`){this.captureRegion();return}if(e===`colliders`){this.setColliders({off:`all`,all:`selected`,selected:`off`}[this.colliderMode]);return}this.isOpen(e)?this.close(e):this.open(e)}setLevelEnabled(e,t){this.levelEnabled[e]=t,this.renderLogs()}setConsoleCapture(e){if(e!==this.consoleCapture)if(this.consoleCapture=e,e){this.renderLogs();let e=console;for(let t of z){let n=e[t];e[t]=(...e)=>{n.apply(console,e);let r=t===`log`?`info`:t;this.pushLog({level:r,source:`console`,text:e.map(U).join(` `)})},this.consolePatched.push(()=>{e[t]=n})}}else{for(let e of this.consolePatched)e();this.consolePatched=[],this.renderLogs()}}dispose(){this.engine.debugSelection=null,this.setColliders(`off`),this.setConsoleCapture(!1);for(let e of this.cleanups)e();for(let e of[...this.panels.keys()])this.close(e);this.close(`stats`),this.closeDropdown(),this.menuButton.remove()}menuLabel(e,t){let n=e===`colliders`&&this.colliderMode!==`off`?` · ${this.colliderMode}`:``;return`${this.isOpen(e)?`✓ `:``}${t}${n}`}bindMenuDrag(){let e=null,t=!1;this.menuButton.addEventListener(`pointerdown`,n=>{let r=n;e={x:r.clientX??0,y:r.clientY??0,bx:this.menuPos.x,by:this.menuPos.y},t=!1,r.pointerId!==void 0&&this.menuButton.setPointerCapture?.(r.pointerId)}),this.menuButton.addEventListener(`pointermove`,n=>{if(!e)return;let r=n,i=(r.clientX??0)-e.x,a=(r.clientY??0)-e.y;!t&&Math.hypot(i,a)<I||(t||(t=!0,this.closeDropdown(),y(this.menuButton,{cursor:`grabbing`})),this.moveMenuButton(e.bx+i,e.by+a))});let n=()=>{if(!e)return;let n=t;e=null,t=!1,y(this.menuButton,{cursor:`pointer`}),n||this.toggleDropdown()};this.menuButton.addEventListener(`pointerup`,n),this.menuButton.addEventListener(`pointercancel`,n)}moveMenuButton(e,t){let n=this.menuButton,r=g(e,t,n.offsetWidth??0,n.offsetHeight??0,this.container.clientWidth||0,this.container.clientHeight||0,0,0);this.menuPos={x:r.x,y:r.y},y(this.menuButton,{left:`${r.x}px`,top:`${r.y}px`})}closeDropdown(){this.dismissDropdown?.(),this.dismissDropdown=null,this.dropdown?.remove(),this.dropdown=null}toggleDropdown(){if(this.dropdown){this.closeDropdown();return}let e=this.doc.createElement(`div`);y(e,{position:`absolute`,background:`rgba(18,20,26,0.95)`,border:`1px solid rgba(255,255,255,0.18)`,borderRadius:`6px`,font:`12px ui-monospace, Menlo, monospace`,color:`rgba(255,255,255,0.85)`,zIndex:`60`,pointerEvents:`auto`,overflow:`hidden`});for(let[t,n]of[[`explorer`,`Explorer`],[`inspector`,`Inspector`],[`logs`,`Logs`],[`time`,`Time`],[`copyScene`,`Copy scene JSON`],[`captureRegion`,`Capture region`],[`stats`,`Stats`],[`colliders`,`Colliders`]]){let r=this.doc.createElement(`div`);r.textContent=this.menuLabel(t,n),y(r,{padding:`6px 14px`,cursor:`pointer`,userSelect:`none`}),r.addEventListener(`click`,()=>{if(this.toggle(t),t===`colliders`){r.textContent=this.menuLabel(t,n);return}this.closeDropdown()}),e.appendChild(r)}for(let t of this.actions){let n=this.doc.createElement(`div`);n.textContent=t.label,y(n,{padding:`6px 14px`,cursor:`pointer`,userSelect:`none`,borderTop:`1px solid rgba(255,255,255,0.14)`,color:`rgba(158,232,220,0.95)`}),n.addEventListener(`click`,()=>{this.closeDropdown(),t.run()}),e.appendChild(n)}this.container.appendChild(e),this.dropdown=e;let t=e,n=this.menuButton,r=_({x:this.menuPos.x,y:this.menuPos.y,w:n.offsetWidth??0,h:n.offsetHeight??26},t.offsetWidth??0,t.offsetHeight??0,this.container.clientWidth||0,this.container.clientHeight||0);y(e,{left:`${r.x}px`,top:`${r.y}px`}),this.dismissDropdown=v(this.doc,[e,this.menuButton],()=>this.closeDropdown())}openStatsChip(){if(this.statsChip)return;let e=this.doc.createElement(`div`);y(e,{position:`absolute`,top:`8px`,right:`8px`,padding:`4px 10px`,background:`rgba(18,20,26,0.85)`,border:`1px solid rgba(255,255,255,0.18)`,borderRadius:`6px`,color:`rgba(255,255,255,0.85)`,font:`12px ui-monospace, Menlo, monospace`,textAlign:`right`,whiteSpace:`pre`,userSelect:`none`,pointerEvents:`none`,zIndex:`70`}),this.container.appendChild(e),this.statsChip=e,this.renderStats()}renderStats(){let e=this.statsChip;if(!e)return;let t=this.engine.stats(),n=this.statsSource?.()??{},r=[`nodes ${t.nodes}`,...n.triangles===void 0?[]:[`tris ${H(n.triangles)}`],...n.drawCalls===void 0?[]:[`calls ${n.drawCalls}`]].join(` · `);e.textContent=`${Math.round(t.fps)} fps · ${t.frameMs.toFixed(1)} ms\n${r}`}renderExplorer(){let e=this.panels.get(`explorer`);if(!e)return;let t=e.body.scrollTop;V(e.body);let n=this.engine.scene?.root;if(!n){e.body.scrollTop=t;return}let r=(e,t)=>{let n=e.constructor,i=e.children.length>0,a=this.collapsedFlags.get(e)===!0,o=this.doc.createElement(`div`);y(o,{display:`flex`,alignItems:`center`,cursor:`pointer`,padding:`1px 2px`,borderRadius:`3px`,background:e===this.selected?`rgba(110,160,255,0.25)`:`transparent`});let s=this.doc.createElement(`span`);s.textContent=i?a?`▸`:`▾`:`·`,y(s,{width:`14px`,flex:`none`,textAlign:`center`,opacity:i?`0.85`:`0.25`,userSelect:`none`}),i&&s.addEventListener(`click`,t=>{t.stopPropagation?.(),this.collapsedFlags.set(e,!a),this.renderExplorer()}),o.appendChild(s);let c=this.doc.createElement(`span`);c.textContent=e.name,y(c,{whiteSpace:`nowrap`}),o.appendChild(c);let l=this.doc.createElement(`span`);if(l.textContent=` ${n.typeName}`,y(l,{opacity:`0.45`,whiteSpace:`nowrap`,fontSize:`10px`}),o.appendChild(l),i&&a){let t=this.doc.createElement(`span`);t.textContent=` (${e.children.length})`,y(t,{opacity:`0.35`,fontSize:`10px`}),o.appendChild(t)}if(o.addEventListener(`click`,()=>{this.selected=e,this.engine.debugSelection=e,this.applyColliderScope(),this.open(`inspector`),this.renderExplorer(),this.renderInspector()}),t.appendChild(o),i&&!a){let n=this.doc.createElement(`div`);y(n,{marginLeft:`8px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`});for(let t of e.children)r(t,n);t.appendChild(n)}};r(n,e.body),e.body.scrollTop=t}renderInspector(){let e=this.panels.get(`inspector`);if(!e)return;let n=e.body.scrollTop;V(e.body);let r=this.selected;if(r&&r.tree===null&&(this.selected=null,this.engine.debugSelection=null,this.applyColliderScope(),r=null),!r){let t=this.doc.createElement(`div`);t.textContent=`select a node in the Explorer`,y(t,{opacity:`0.6`}),e.body.appendChild(t);return}let i=r.constructor,a=this.doc.createElement(`div`);if(a.textContent=`${r.getPath()} · ${i.typeName}${r.uid?` · ${r.uid}`:``}`,y(a,{fontWeight:`700`,marginBottom:`6px`,whiteSpace:`pre-wrap`}),e.body.appendChild(a),r.groups.size>0){let t=this.doc.createElement(`div`);t.textContent=`groups: ${[...r.groups].join(`, `)}`,y(t,{opacity:`0.7`,marginBottom:`6px`}),e.body.appendChild(t)}let o=t(i),s=r;for(let t of Object.keys(o)){let n=o[t];this.renderValueRow(e.body,r,t,{read:()=>s[t],write:e=>{s[t]=e},options:n?.options,variants:n?.variants})}e.body.scrollTop=n}renderValueRow(t,n,r,i){let a=i.read(),o=i.options,s={get[r](){return i.read()},set[r](e){i.write(e)}},c=this.doc.createElement(`div`);y(c,{display:`flex`,gap:`6px`,alignItems:`center`,margin:`2px 0`});let l=this.doc.createElement(`div`);l.textContent=r,y(l,{minWidth:`84px`,opacity:`0.75`}),c.appendChild(l);let u=(e,t)=>{let i=this.doc.createElement(`input`);return i.type=`number`,i.value=String(e),y(i,G(`70px`)),this.trackEditing(i),i.addEventListener(`change`,()=>{let e=Number(i.value);Number.isFinite(e)?t(e):i.value=String(n[r])}),i};if(typeof a==`number`)c.appendChild(u(a,e=>{s[r]=e}));else if(typeof a==`boolean`){let e=this.doc.createElement(`input`);e.type=`checkbox`,e.checked=a,this.trackEditing(e),e.addEventListener(`change`,()=>{s[r]=e.checked}),c.appendChild(e)}else if(typeof a==`string`&&o&&o.length>0){let e=this.doc.createElement(`select`);for(let t of o.includes(a)?o:[a,...o]){let n=this.doc.createElement(`option`);n.value=t,n.textContent=t,t===a&&(n.selected=!0),e.appendChild(n)}e.value=a,this.trackEditing(e),e.addEventListener(`change`,()=>{s[r]=e.value}),y(e,G(`110px`)),c.appendChild(e)}else if(typeof a==`string`){let e=this.doc.createElement(`input`);e.type=`text`,e.value=a,this.trackEditing(e),y(e,G(`140px`)),e.addEventListener(`change`,()=>{s[r]=e.value}),c.appendChild(e)}else if(Array.isArray(a)&&a.length<=8&&a.every(e=>typeof e==`number`))for(let e=0;e<a.length;e++)c.appendChild(u(a[e],t=>{let n=[...s[r]];n[e]=t,s[r]=n}));else if(P(a)){let o=this.doc.createElement(`div`);y(o,{marginLeft:`10px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`});let s=i.variants?.tag,l=Object.keys(a);s&&!l.includes(s)&&l.unshift(s);let u=i.path??r;for(let t of l){let r=t===s;this.renderValueRow(o,n,t,{path:`${u}.${t}`,read:()=>i.read()[t]??(r?``:null),write:n=>{if(r&&i.variants){let t=i.variants.byTag[String(n)];if(t!==void 0){i.write(e(t));return}}i.write({...i.read(),[t]:n})},options:r&&i.variants?Object.keys(i.variants.byTag):void 0})}t.appendChild(c),t.appendChild(o);return}else{let o=JSON.stringify(e(a));if(o.length<=k){let e=this.doc.createElement(`div`);e.textContent=o,y(e,{opacity:`0.65`,whiteSpace:`pre-wrap`,wordBreak:`break-all`}),c.appendChild(e)}else{let e=i.path??r,s=this.detailOpen.get(n)?.has(e)??!1,l=this.doc.createElement(`div`);if(l.textContent=`${s?`▾`:`▸`} ${M(a)}`,y(l,{opacity:`0.75`,cursor:`pointer`,userSelect:`none`}),l.addEventListener(`click`,()=>{let t=this.detailOpen.get(n);t||(t=new Set,this.detailOpen.set(n,t)),s?t.delete(e):t.add(e),this.renderInspector()}),c.appendChild(l),t.appendChild(c),s){let e=this.doc.createElement(`div`);e.textContent=N(a,o),y(e,{opacity:`0.6`,whiteSpace:`pre-wrap`,wordBreak:`break-all`,marginLeft:`10px`,paddingLeft:`8px`,borderLeft:`1px solid rgba(255,255,255,0.14)`}),t.appendChild(e)}return}}t.appendChild(c)}trackEditing(e){e.addEventListener(`focus`,()=>{this.editing+=1}),e.addEventListener(`blur`,()=>{this.editing=Math.max(0,this.editing-1)})}pushLog(e){this.logRows.push(e),this.logRows.length>R&&this.logRows.splice(0,this.logRows.length-R),this.renderLogs()}async copyScene(){let e=await o(a(this.engine.scene??null));return this.toast(e?`scene JSON copied`:`could not reach the clipboard`),e}captureRegion(){this.capture||=(this.captureScale=globalThis.devicePixelRatio??1,this.frameSource?.().then(e=>{this.captureScale=e.width/Math.max(1,this.container.clientWidth||e.width)}).catch(()=>{}),O({doc:this.doc,container:this.container,scale:()=>this.captureScale,onCopy:e=>void this.copyRegion({x0:e.x,y0:e.y,x1:e.x+e.w,y1:e.y+e.h}),onClose:()=>{this.capture=null}}))}capture=null;captureScale=1;async copyRegion(e){let t=this.frameSource;if(!t)return this.toast(`no renderer to capture from`),!1;try{let n=await t(),a=n.width/Math.max(1,this.container.clientWidth||n.width),o=r(e,n.width,n.height,a),c=i(n.pixels,n.width,n.height,o),l=await s(this.doc,c);return this.toast(l?`copied ${c.width}×${c.height}`:`could not copy the image`),l}catch(e){return this.toast(`capture failed: ${e instanceof Error?e.message:String(e)}`),!1}}toast(e){let t=this.doc.createElement(`div`);t.textContent=e,y(t,{position:`absolute`,bottom:`16px`,left:`50%`,transform:`translateX(-50%)`,padding:`6px 12px`,borderRadius:`6px`,background:`rgba(18,20,26,0.92)`,color:`rgba(255,255,255,0.9)`,font:`12px ui-monospace, Menlo, monospace`,zIndex:`90`,pointerEvents:`none`}),this.container.appendChild(t),setTimeout(()=>t.remove(),1800)}setTimeScale(e){Number.isFinite(e)&&(this.engine.timeScale=Math.max(0,e),this.syncTime())}setPaused(e){e?(this.engine.timeScale>0&&(this.resumeScale=this.engine.timeScale),this.engine.timeScale=0):this.engine.timeScale=this.resumeScale>0?this.resumeScale:1,this.syncTime()}get paused(){return this.engine.timeScale===0}nextFrame(){this.paused||this.setPaused(!0),this.engine.step(),this.syncTime()}syncTime(){let e=this.timeEls;if(!e)return;let t=this.engine.timeScale,n=String(Math.round(t*1e3)/1e3);this.editing===0&&(e.slider.value=String(Math.min(t,F)),e.box.value=n),e.readout.textContent=`timeScale ${n}${this.paused?` · paused`:``}`,e.pause.textContent=this.paused?`▶ Resume`:`⏸ Pause`}renderTime(){let e=this.panels.get(`time`);if(!e)return;V(e.body),this.timeEls=null;let t=this.doc.createElement(`div`);y(t,{marginBottom:`6px`,opacity:`0.85`}),e.body.appendChild(t);let n=this.doc.createElement(`input`);n.type=`range`,n.min=`0`,n.max=String(F),n.step=`0.05`,n.value=String(Math.min(this.engine.timeScale,F)),y(n,{width:`100%`,marginBottom:`6px`}),this.trackEditing(n),n.addEventListener(`input`,()=>this.setTimeScale(Number(n.value))),e.body.appendChild(n);let r=this.doc.createElement(`div`);y(r,{display:`flex`,gap:`6px`,alignItems:`center`,marginBottom:`8px`});let i=this.doc.createElement(`input`);i.type=`number`,i.min=`0`,i.step=`0.05`,i.value=String(this.engine.timeScale),y(i,G(`72px`)),this.trackEditing(i),i.addEventListener(`change`,()=>{let e=Number(i.value);Number.isFinite(e)&&this.setTimeScale(e),this.syncTimeAfterEdit()}),r.appendChild(i);for(let e of[.25,.5,1,2]){let t=this.doc.createElement(`div`);t.textContent=`${e}×`,y(t,{cursor:`pointer`,opacity:`0.7`,padding:`2px 4px`}),t.addEventListener(`click`,()=>this.setTimeScale(e)),r.appendChild(t)}e.body.appendChild(r);let a=this.doc.createElement(`div`);y(a,{display:`flex`,gap:`8px`});let o=this.doc.createElement(`div`);y(o,W()),o.addEventListener(`click`,()=>this.setPaused(!this.paused)),a.appendChild(o);let s=this.doc.createElement(`div`);s.textContent=`⏭ Next frame`,s.title=`Pauses, then advances one fixed step`,y(s,W()),s.addEventListener(`click`,()=>this.nextFrame()),a.appendChild(s),e.body.appendChild(a),this.timeEls={slider:n,box:i,readout:t,pause:o},this.syncTime()}syncTimeAfterEdit(){let e=this.timeEls;if(!e)return;let t=this.engine.timeScale;e.slider.value=String(Math.min(t,F)),e.box.value=String(Math.round(t*1e3)/1e3)}renderLogs(){let e=this.panels.get(`logs`);if(!e)return;V(e.body);let t=this.doc.createElement(`div`);y(t,{display:`flex`,gap:`8px`,marginBottom:`4px`,flexWrap:`wrap`});for(let e of[`debug`,`info`,`warn`,`error`]){let n=this.doc.createElement(`div`);n.textContent=`${this.levelEnabled[e]?`✓`:`·`}${e}`,y(n,{cursor:`pointer`,opacity:this.levelEnabled[e]?`1`:`0.45`}),n.addEventListener(`click`,()=>this.setLevelEnabled(e,!this.levelEnabled[e])),t.appendChild(n)}let n=this.doc.createElement(`div`);n.textContent=`${this.consoleCapture?`✓`:`·`}console`,y(n,{cursor:`pointer`,marginLeft:`auto`}),n.addEventListener(`click`,()=>this.setConsoleCapture(!this.consoleCapture)),t.appendChild(n),e.body.appendChild(t);let r={debug:`rgba(255,255,255,0.5)`,info:`rgba(255,255,255,0.85)`,warn:`#ffc861`,error:`#ff6b6b`};for(let t of this.logRows){if(!this.levelEnabled[t.level])continue;let n=this.doc.createElement(`div`);n.textContent=`[${t.source===`console`?`console`:t.level}] ${t.text}`,y(n,{color:r[t.level],whiteSpace:`pre-wrap`}),e.body.appendChild(n)}}};function V(e){for(let t of[...e.children])t.remove()}function H(e){return e>=1e6?`${(e/1e6).toFixed(1)}M`:e>=1e3?`${(e/1e3).toFixed(1)}k`:String(e)}function U(e){if(typeof e==`string`)return e;if(e instanceof Error){let t=e.stack?.split(`
|
|
3
3
|
`)[1]?.trim();return`${e.name}: ${e.message}${t?` (${t})`:``}`}try{let t=JSON.stringify(e);return t===`{}`||t===void 0?String(e):t}catch{return String(e)}}function W(){return{background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.2)`,borderRadius:`4px`,padding:`4px 8px`,cursor:`pointer`,userSelect:`none`}}function G(e){return{width:e,background:`rgba(255,255,255,0.08)`,border:`1px solid rgba(255,255,255,0.2)`,borderRadius:`4px`,color:`inherit`,font:`inherit`,padding:`2px 4px`}}export{L as attachDebugOverlay};
|