@nodetool-ai/agents 0.8.2 → 0.8.4-a24.1
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/.nodetool-build-stamp +1 -1
- package/dist/apify/assets.d.ts.map +1 -1
- package/dist/apify/assets.js +4 -0
- package/dist/apify/assets.js.map +1 -1
- package/dist/capabilities/agent-activity.js +1 -1
- package/dist/capabilities/agentic-storyboard-finish.d.ts.map +1 -1
- package/dist/capabilities/agentic-storyboard-finish.js +2 -1
- package/dist/capabilities/agentic-storyboard-finish.js.map +1 -1
- package/dist/capabilities/apps.specs.d.ts.map +1 -1
- package/dist/capabilities/apps.specs.js +4 -3
- package/dist/capabilities/apps.specs.js.map +1 -1
- package/dist/capabilities/assets.d.ts.map +1 -1
- package/dist/capabilities/assets.js +40 -10
- package/dist/capabilities/assets.js.map +1 -1
- package/dist/capabilities/code-grading.d.ts +4 -0
- package/dist/capabilities/code-grading.d.ts.map +1 -1
- package/dist/capabilities/code-grading.js +8 -1
- package/dist/capabilities/code-grading.js.map +1 -1
- package/dist/capabilities/code.d.ts.map +1 -1
- package/dist/capabilities/code.js +13 -7
- package/dist/capabilities/code.js.map +1 -1
- package/dist/capabilities/collections.d.ts +4 -6
- package/dist/capabilities/collections.d.ts.map +1 -1
- package/dist/capabilities/collections.js +16 -11
- package/dist/capabilities/collections.js.map +1 -1
- package/dist/capabilities/derive-shot.d.ts +45 -0
- package/dist/capabilities/derive-shot.d.ts.map +1 -0
- package/dist/capabilities/derive-shot.js +83 -0
- package/dist/capabilities/derive-shot.js.map +1 -0
- package/dist/capabilities/dispatcher.js +2 -2
- package/dist/capabilities/dispatcher.js.map +1 -1
- package/dist/capabilities/documents.d.ts +2 -1
- package/dist/capabilities/documents.d.ts.map +1 -1
- package/dist/capabilities/documents.js +12 -6
- package/dist/capabilities/documents.js.map +1 -1
- package/dist/capabilities/email.d.ts +2 -1
- package/dist/capabilities/email.d.ts.map +1 -1
- package/dist/capabilities/email.js +5 -3
- package/dist/capabilities/email.js.map +1 -1
- package/dist/capabilities/files.d.ts.map +1 -1
- package/dist/capabilities/files.js +3 -7
- package/dist/capabilities/files.js.map +1 -1
- package/dist/capabilities/files.specs.d.ts.map +1 -1
- package/dist/capabilities/files.specs.js +2 -1
- package/dist/capabilities/files.specs.js.map +1 -1
- package/dist/capabilities/flow.d.ts +3 -0
- package/dist/capabilities/flow.d.ts.map +1 -1
- package/dist/capabilities/flow.js +7 -0
- package/dist/capabilities/flow.js.map +1 -1
- package/dist/capabilities/game-asset-browser.d.ts +3 -0
- package/dist/capabilities/game-asset-browser.d.ts.map +1 -0
- package/dist/capabilities/game-asset-browser.js +153 -0
- package/dist/capabilities/game-asset-browser.js.map +1 -0
- package/dist/capabilities/game.d.ts +30 -0
- package/dist/capabilities/game.d.ts.map +1 -1
- package/dist/capabilities/game.js +266 -149
- package/dist/capabilities/game.js.map +1 -1
- package/dist/capabilities/game.specs.d.ts +2 -0
- package/dist/capabilities/game.specs.d.ts.map +1 -1
- package/dist/capabilities/game.specs.js +21 -6
- package/dist/capabilities/game.specs.js.map +1 -1
- package/dist/capabilities/game3d.d.ts.map +1 -1
- package/dist/capabilities/game3d.js +24 -10
- package/dist/capabilities/game3d.js.map +1 -1
- package/dist/capabilities/generations.d.ts.map +1 -1
- package/dist/capabilities/generations.js +7 -21
- package/dist/capabilities/generations.js.map +1 -1
- package/dist/capabilities/invoke.d.ts.map +1 -1
- package/dist/capabilities/invoke.js +15 -2
- package/dist/capabilities/invoke.js.map +1 -1
- package/dist/capabilities/jobs.d.ts +7 -2
- package/dist/capabilities/jobs.d.ts.map +1 -1
- package/dist/capabilities/jobs.js +13 -3
- package/dist/capabilities/jobs.js.map +1 -1
- package/dist/capabilities/lazy-tool-core.d.ts +23 -0
- package/dist/capabilities/lazy-tool-core.d.ts.map +1 -0
- package/dist/capabilities/lazy-tool-core.js +56 -0
- package/dist/capabilities/lazy-tool-core.js.map +1 -0
- package/dist/capabilities/lazy-tool.d.ts +2 -21
- package/dist/capabilities/lazy-tool.d.ts.map +1 -1
- package/dist/capabilities/lazy-tool.js +3 -55
- package/dist/capabilities/lazy-tool.js.map +1 -1
- package/dist/capabilities/legacy-tools.js +1 -1
- package/dist/capabilities/legacy-tools.js.map +1 -1
- package/dist/capabilities/media.d.ts.map +1 -1
- package/dist/capabilities/media.js +26 -1
- package/dist/capabilities/media.js.map +1 -1
- package/dist/capabilities/media.specs.d.ts.map +1 -1
- package/dist/capabilities/media.specs.js +17 -0
- package/dist/capabilities/media.specs.js.map +1 -1
- package/dist/capabilities/metadata.d.ts +62 -0
- package/dist/capabilities/metadata.d.ts.map +1 -0
- package/dist/capabilities/metadata.js +114 -0
- package/dist/capabilities/metadata.js.map +1 -0
- package/dist/capabilities/model3d.d.ts.map +1 -1
- package/dist/capabilities/model3d.js +6 -1
- package/dist/capabilities/model3d.js.map +1 -1
- package/dist/capabilities/nodes.specs.js +1 -1
- package/dist/capabilities/nodes.specs.js.map +1 -1
- package/dist/capabilities/registry.d.ts +4 -19
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +48 -117
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/capabilities/run-agent.js +1 -1
- package/dist/capabilities/scripts.js +2 -2
- package/dist/capabilities/scripts.js.map +1 -1
- package/dist/capabilities/settings.d.ts.map +1 -1
- package/dist/capabilities/settings.js +4 -2
- package/dist/capabilities/settings.js.map +1 -1
- package/dist/capabilities/storyboard-layout-check.d.ts.map +1 -1
- package/dist/capabilities/storyboard-layout-check.js +2 -1
- package/dist/capabilities/storyboard-layout-check.js.map +1 -1
- package/dist/capabilities/storyboard-measured-sources.d.ts +1 -1
- package/dist/capabilities/storyboard-measured-sources.d.ts.map +1 -1
- package/dist/capabilities/storyboard-measured-sources.js +2 -1
- package/dist/capabilities/storyboard-measured-sources.js.map +1 -1
- package/dist/capabilities/storyboards.d.ts +11 -1
- package/dist/capabilities/storyboards.d.ts.map +1 -1
- package/dist/capabilities/storyboards.js +467 -5
- package/dist/capabilities/storyboards.js.map +1 -1
- package/dist/capabilities/storyboards.specs.d.ts +6 -0
- package/dist/capabilities/storyboards.specs.d.ts.map +1 -1
- package/dist/capabilities/storyboards.specs.js +139 -3
- package/dist/capabilities/storyboards.specs.js.map +1 -1
- package/dist/capabilities/threads.js +1 -1
- package/dist/capabilities/threads.js.map +1 -1
- package/dist/capabilities/timeline-asset-ids.d.ts +27 -0
- package/dist/capabilities/timeline-asset-ids.d.ts.map +1 -0
- package/dist/capabilities/timeline-asset-ids.js +88 -0
- package/dist/capabilities/timeline-asset-ids.js.map +1 -0
- package/dist/capabilities/timeline-bake.d.ts.map +1 -1
- package/dist/capabilities/timeline-bake.js +2 -1
- package/dist/capabilities/timeline-bake.js.map +1 -1
- package/dist/capabilities/timeline-bridge.d.ts +9 -0
- package/dist/capabilities/timeline-bridge.d.ts.map +1 -1
- package/dist/capabilities/timeline-bridge.js +60 -68
- package/dist/capabilities/timeline-bridge.js.map +1 -1
- package/dist/capabilities/timeline-clip-frames.d.ts.map +1 -1
- package/dist/capabilities/timeline-clip-frames.js +2 -1
- package/dist/capabilities/timeline-clip-frames.js.map +1 -1
- package/dist/capabilities/timeline-operations.d.ts +52 -0
- package/dist/capabilities/timeline-operations.d.ts.map +1 -0
- package/dist/capabilities/timeline-operations.js +413 -0
- package/dist/capabilities/timeline-operations.js.map +1 -0
- package/dist/capabilities/timelines.d.ts +4 -39
- package/dist/capabilities/timelines.d.ts.map +1 -1
- package/dist/capabilities/timelines.js +45 -414
- package/dist/capabilities/timelines.js.map +1 -1
- package/dist/capabilities/timelines.specs.d.ts.map +1 -1
- package/dist/capabilities/timelines.specs.js +8 -3
- package/dist/capabilities/timelines.specs.js.map +1 -1
- package/dist/capabilities/web.d.ts.map +1 -1
- package/dist/capabilities/web.js +5 -1
- package/dist/capabilities/web.js.map +1 -1
- package/dist/capabilities/web.specs.js +1 -1
- package/dist/capabilities/web.specs.js.map +1 -1
- package/dist/capabilities/workflows.d.ts +3 -23
- package/dist/capabilities/workflows.d.ts.map +1 -1
- package/dist/capabilities/workflows.js +197 -15
- package/dist/capabilities/workflows.js.map +1 -1
- package/dist/capabilities/workflows.specs.d.ts +2 -0
- package/dist/capabilities/workflows.specs.d.ts.map +1 -1
- package/dist/capabilities/workflows.specs.js +65 -1
- package/dist/capabilities/workflows.specs.js.map +1 -1
- package/dist/codeact/nodetool-api.d.ts +17 -1
- package/dist/codeact/nodetool-api.d.ts.map +1 -1
- package/dist/codeact/nodetool-api.js +168 -48
- package/dist/codeact/nodetool-api.js.map +1 -1
- package/dist/codeact/sandbox-packages.d.ts +0 -7
- package/dist/codeact/sandbox-packages.d.ts.map +1 -1
- package/dist/codeact/sandbox-packages.js +1 -10
- package/dist/codeact/sandbox-packages.js.map +1 -1
- package/dist/codeact/tool-api.d.ts.map +1 -1
- package/dist/codeact/tool-api.js +6 -2
- package/dist/codeact/tool-api.js.map +1 -1
- package/dist/evals/app-build-plan.d.ts.map +1 -1
- package/dist/evals/app-build-plan.js +3 -0
- package/dist/evals/app-build-plan.js.map +1 -1
- package/dist/evals/codeact-api-core.d.ts.map +1 -1
- package/dist/evals/codeact-api-core.js +6 -0
- package/dist/evals/codeact-api-core.js.map +1 -1
- package/dist/evals/surfaces/creative-pipeline.d.ts +1 -1
- package/dist/evals/surfaces/creative-pipeline.d.ts.map +1 -1
- package/dist/evals/surfaces/creative-pipeline.js +2 -2
- package/dist/evals/surfaces/creative-pipeline.js.map +1 -1
- package/dist/evals/surfaces/game.d.ts +9 -2
- package/dist/evals/surfaces/game.d.ts.map +1 -1
- package/dist/evals/surfaces/game.js +714 -2
- package/dist/evals/surfaces/game.js.map +1 -1
- package/dist/evals/surfaces/sketch.d.ts +1 -1
- package/dist/evals/surfaces/sketch.d.ts.map +1 -1
- package/dist/evals/surfaces/sketch.js +6 -13
- package/dist/evals/surfaces/sketch.js.map +1 -1
- package/dist/evals/tool-loop-eval.d.ts +1 -1
- package/dist/evals/tool-loop-eval.d.ts.map +1 -1
- package/dist/evals/tool-loop-eval.js +1 -1
- package/dist/evals/tool-loop-eval.js.map +1 -1
- package/dist/game-authoring.d.ts +18868 -3824
- package/dist/game-authoring.d.ts.map +1 -1
- package/dist/game-build.d.ts +3 -0
- package/dist/game-build.d.ts.map +1 -0
- package/dist/game-build.js +3 -0
- package/dist/game-build.js.map +1 -0
- package/dist/game-examples.d.ts +5310 -1056
- package/dist/game-examples.d.ts.map +1 -1
- package/dist/generation-cancel.d.ts +31 -0
- package/dist/generation-cancel.d.ts.map +1 -0
- package/dist/generation-cancel.js +28 -0
- package/dist/generation-cancel.js.map +1 -0
- package/dist/host-access-gate.d.ts +29 -0
- package/dist/host-access-gate.d.ts.map +1 -0
- package/dist/host-access-gate.js +39 -0
- package/dist/host-access-gate.js.map +1 -0
- package/dist/host-modules/dispatcher.d.ts +7 -1
- package/dist/host-modules/dispatcher.d.ts.map +1 -1
- package/dist/host-modules/dispatcher.js +13 -2
- package/dist/host-modules/dispatcher.js.map +1 -1
- package/dist/host-modules/docx.d.ts.map +1 -1
- package/dist/host-modules/docx.js +2 -2
- package/dist/host-modules/docx.js.map +1 -1
- package/dist/host-modules/epub.d.ts.map +1 -1
- package/dist/host-modules/epub.js +2 -2
- package/dist/host-modules/epub.js.map +1 -1
- package/dist/host-modules/fabric.d.ts.map +1 -1
- package/dist/host-modules/fabric.js +1 -1
- package/dist/host-modules/fabric.js.map +1 -1
- package/dist/host-modules/limits.d.ts +10 -8
- package/dist/host-modules/limits.d.ts.map +1 -1
- package/dist/host-modules/limits.js +14 -9
- package/dist/host-modules/limits.js.map +1 -1
- package/dist/host-modules/mammoth.d.ts.map +1 -1
- package/dist/host-modules/mammoth.js +2 -2
- package/dist/host-modules/mammoth.js.map +1 -1
- package/dist/host-modules/ocr.d.ts.map +1 -1
- package/dist/host-modules/ocr.js +1 -1
- package/dist/host-modules/ocr.js.map +1 -1
- package/dist/host-modules/pdf.d.ts.map +1 -1
- package/dist/host-modules/pdf.js +2 -2
- package/dist/host-modules/pdf.js.map +1 -1
- package/dist/host-modules/pdflib.d.ts.map +1 -1
- package/dist/host-modules/pdflib.js +2 -2
- package/dist/host-modules/pdflib.js.map +1 -1
- package/dist/host-modules/pptx.d.ts.map +1 -1
- package/dist/host-modules/pptx.js +2 -2
- package/dist/host-modules/pptx.js.map +1 -1
- package/dist/host-modules/pptxgen.d.ts.map +1 -1
- package/dist/host-modules/pptxgen.js +2 -2
- package/dist/host-modules/pptxgen.js.map +1 -1
- package/dist/host-modules/tfjs.d.ts.map +1 -1
- package/dist/host-modules/tfjs.js +4 -4
- package/dist/host-modules/tfjs.js.map +1 -1
- package/dist/host-modules/tokens.js +2 -2
- package/dist/host-modules/tokens.js.map +1 -1
- package/dist/host-modules/xlsx.d.ts.map +1 -1
- package/dist/host-modules/xlsx.js +2 -2
- package/dist/host-modules/xlsx.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/js-sandbox.d.ts +8 -1
- package/dist/js-sandbox.d.ts.map +1 -1
- package/dist/js-sandbox.js +27 -4
- package/dist/js-sandbox.js.map +1 -1
- package/dist/jtbd/registry.d.ts.map +1 -1
- package/dist/jtbd/registry.js +16 -3
- package/dist/jtbd/registry.js.map +1 -1
- package/dist/jtbd/run.d.ts +2 -2
- package/dist/jtbd/run.d.ts.map +1 -1
- package/dist/jtbd/run.js +3 -3
- package/dist/jtbd/run.js.map +1 -1
- package/dist/jtbd/types.d.ts +1 -1
- package/dist/jtbd/types.d.ts.map +1 -1
- package/dist/model-prompting-skills.d.ts +8 -7
- package/dist/model-prompting-skills.d.ts.map +1 -1
- package/dist/model-prompting-skills.js +35 -16
- package/dist/model-prompting-skills.js.map +1 -1
- package/dist/sandbox-media.d.ts +1 -1
- package/dist/sandbox-media.d.ts.map +1 -1
- package/dist/sandbox-media.js +10 -9
- package/dist/sandbox-media.js.map +1 -1
- package/dist/skill-prompt.d.ts.map +1 -1
- package/dist/skill-prompt.js +1 -1
- package/dist/skill-prompt.js.map +1 -1
- package/dist/system-skills.d.ts +4 -3
- package/dist/system-skills.d.ts.map +1 -1
- package/dist/system-skills.js +4 -3
- package/dist/system-skills.js.map +1 -1
- package/dist/task-planner.js +1 -1
- package/dist/timeline-code-bake.js +2 -2
- package/dist/timeline-code-bake.js.map +1 -1
- package/dist/timeline-preview/compare.d.ts.map +1 -1
- package/dist/timeline-preview/compare.js +4 -1
- package/dist/timeline-preview/compare.js.map +1 -1
- package/dist/timeline-preview/frames.d.ts.map +1 -1
- package/dist/timeline-preview/frames.js +7 -4
- package/dist/timeline-preview/frames.js.map +1 -1
- package/dist/timeline-preview/model3d.d.ts +1 -1
- package/dist/timeline-preview/model3d.d.ts.map +1 -1
- package/dist/timeline-preview/model3d.js +4 -23
- package/dist/timeline-preview/model3d.js.map +1 -1
- package/dist/timeline-preview/rasterize.d.ts +4 -2
- package/dist/timeline-preview/rasterize.d.ts.map +1 -1
- package/dist/timeline-preview/rasterize.js +10 -4
- package/dist/timeline-preview/rasterize.js.map +1 -1
- package/dist/timeline-preview/sheet.d.ts.map +1 -1
- package/dist/timeline-preview/sheet.js +3 -2
- package/dist/timeline-preview/sheet.js.map +1 -1
- package/dist/tools/builtin-tools.d.ts +4 -3
- package/dist/tools/builtin-tools.d.ts.map +1 -1
- package/dist/tools/builtin-tools.js +11 -3
- package/dist/tools/builtin-tools.js.map +1 -1
- package/dist/tools/graph-validation-registry.d.ts.map +1 -1
- package/dist/tools/graph-validation-registry.js +3 -0
- package/dist/tools/graph-validation-registry.js.map +1 -1
- package/dist/tools/mcp-tool-support.js +1 -1
- package/dist/tools/mcp-tool-support.js.map +1 -1
- package/dist/tools/mcp-tools.d.ts.map +1 -1
- package/dist/tools/mcp-tools.js +4 -0
- package/dist/tools/mcp-tools.js.map +1 -1
- package/dist/utils/json-parser.js +1 -1
- package/dist/utils/json-parser.js.map +1 -1
- package/package.json +131 -47
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
2
|
import { mkdtemp, readFile, readdir, rm } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { importPack } from "@nodetool-ai/node-catalog";
|
|
4
6
|
import { createLogger, getManagedWorkspaceDir, managedWorkspaceKey, workspaceStorageKind } from "@nodetool-ai/config";
|
|
5
|
-
import { AmbiguousGameIdError, Asset, Game, Prediction, Project, Workspace } from "@nodetool-ai/models";
|
|
7
|
+
import { AmbiguousGameIdError, MissingGameDraftSourceError, Asset, Game, Prediction, Project, Workspace } from "@nodetool-ai/models";
|
|
6
8
|
import { anyGameAssetBinding as gameAssetBinding, gameAssetBinding as legacyAssetBinding, gameAssetBinding3D, anyGameDocument as gameDocument, gameInputFrame, shortResourceId } from "@nodetool-ai/protocol";
|
|
7
|
-
import { autoplayNativeGame, MAX_GAME_ROUTE_TICKS, createScriptedGameSession, createTopDownRoomGame, createNative3DGame, decodePreparedGameCollider3D, validateAnyGame, trackGameAuthoringEdits, anyGameDocumentOp as gameDocumentOp, GameOpError } from "@nodetool-ai/game-runtime";
|
|
9
|
+
import { autoplayNativeGame, MAX_GAME_ROUTE_TICKS, createMemoryGameSaveStorage, createScriptedGameSession, GamePlayerSaves, createTopDownRoomGame, createNative3DGame, decodePreparedGameCollider3D, validateAnyGame, trackGameAuthoringEdits, anyGameDocumentOp as gameDocumentOp, GameOpError } from "@nodetool-ai/game-runtime";
|
|
8
10
|
import { workspaceFromRow } from "@nodetool-ai/execution/service";
|
|
9
11
|
import { assetKeyCandidates, assetObjectKey } from "@nodetool-ai/storage";
|
|
12
|
+
import { loadCanvas } from "@nodetool-ai/runtime";
|
|
10
13
|
import { gameOutline3D, inputFrames3D, playtestGame3D, captureFrames3D, colliderBinding3D, stageModelGameAsset3D } from "./game3d.js";
|
|
11
14
|
import { gameSpecs } from "./game.specs.js";
|
|
12
15
|
import { persistOutput } from "../tools/asset-persist.js";
|
|
13
16
|
import { getExampleGameBundle, installExampleGameAssets, listExampleGames } from "../game-examples.js";
|
|
14
17
|
import { previewGameAuthoring, applyGameAuthoring } from "../game-authoring.js";
|
|
15
18
|
import { gameDocumentDigest } from "../game-code-bake.js";
|
|
16
|
-
import { previewGameAuthoringSpec, applyGameAuthoringSpec } from "./game.specs.js";
|
|
19
|
+
import { previewGameAuthoringSpec, applyGameAuthoringSpec, browseGameAssetsSpec } from "./game.specs.js";
|
|
20
|
+
import { browseGameAssets } from "./game-asset-browser.js";
|
|
17
21
|
const log = createLogger("capabilities.game");
|
|
18
22
|
const MAX_PLAYTEST_TICKS = MAX_GAME_ROUTE_TICKS;
|
|
19
23
|
const MAX_CAPTURE_FRAMES = 8;
|
|
@@ -121,7 +125,8 @@ function outline(document) {
|
|
|
121
125
|
inputActions: document.inputActions,
|
|
122
126
|
collisionLayers: document.collisionLayers,
|
|
123
127
|
renderEffects: document.renderEffects,
|
|
124
|
-
hudEffectOrder: document.hudEffectOrder
|
|
128
|
+
hudEffectOrder: document.hudEffectOrder,
|
|
129
|
+
audio: document.audio
|
|
125
130
|
},
|
|
126
131
|
scenes: document.scenes.map((scene) => ({
|
|
127
132
|
id: scene.id,
|
|
@@ -183,7 +188,9 @@ async function captureFrames(run, user, document, ticks, inputs, seed, options)
|
|
|
183
188
|
wallTimeLimited = Date.now() >= deadlineAt;
|
|
184
189
|
return cancelled || wallTimeLimited;
|
|
185
190
|
};
|
|
186
|
-
const { captureGameFrame } = await
|
|
191
|
+
const { captureGameFrame } = await importPack("game", "@nodetool-ai/game-renderer/node");
|
|
192
|
+
const { GameParticles2D } = await importPack("game", "@nodetool-ai/game-renderer");
|
|
193
|
+
const particles = new GameParticles2D(document.tickRate);
|
|
187
194
|
const diagnostics = [];
|
|
188
195
|
const storage = run.context.assetStorage;
|
|
189
196
|
const assetCache = new Map();
|
|
@@ -221,7 +228,9 @@ async function captureFrames(run, user, document, ticks, inputs, seed, options)
|
|
|
221
228
|
if (budgetReached()) {
|
|
222
229
|
break;
|
|
223
230
|
}
|
|
224
|
-
|
|
231
|
+
const step = session.step(inputs[simulatedTicks] ?? { pressed: [], justPressed: [] });
|
|
232
|
+
particles.tick(step.frame, session.takePresentationEvents());
|
|
233
|
+
simulatedTicks = step.tick;
|
|
225
234
|
}
|
|
226
235
|
if (budgetReached()) {
|
|
227
236
|
break;
|
|
@@ -238,11 +247,11 @@ async function captureFrames(run, user, document, ticks, inputs, seed, options)
|
|
|
238
247
|
const start = diagnostics.length;
|
|
239
248
|
let png;
|
|
240
249
|
try {
|
|
241
|
-
png = await captureGameFrame(frame, { resolveAsset, scale, effects: document.renderEffects, hudEffectOrder: document.hudEffectOrder, backend: document.renderEffects?.length ? "webgpu" : "canvas2d", onDiagnostic: (message) => diagnostics.push(message) });
|
|
250
|
+
png = await captureGameFrame(frame, { resolveAsset, scale, particles, effects: document.renderEffects, hudEffectOrder: document.hudEffectOrder, backend: document.renderEffects?.length ? "webgpu" : "canvas2d", onDiagnostic: (message) => diagnostics.push(message) });
|
|
242
251
|
}
|
|
243
252
|
catch (error) {
|
|
244
253
|
diagnostics.push(`WebGPU capture failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
245
|
-
png = await captureGameFrame(frame, { resolveAsset, scale, backend: "canvas2d", onDiagnostic: (message) => diagnostics.push(message) });
|
|
254
|
+
png = await captureGameFrame(frame, { resolveAsset, scale, particles, backend: "canvas2d", onDiagnostic: (message) => diagnostics.push(message) });
|
|
246
255
|
}
|
|
247
256
|
if (budgetReached()) {
|
|
248
257
|
break;
|
|
@@ -250,7 +259,7 @@ async function captureFrames(run, user, document, ticks, inputs, seed, options)
|
|
|
250
259
|
const pixelScale = frame.pixelsPerUnit * frame.camera.zoom * scale;
|
|
251
260
|
const overlays = Array.isArray(options.overlays) ? options.overlays.filter((value) => typeof value === "string") : [];
|
|
252
261
|
if (overlays.length > 0) {
|
|
253
|
-
const { createCanvas, loadImage } = await
|
|
262
|
+
const { createCanvas, loadImage } = await loadCanvas();
|
|
254
263
|
const source = await loadImage(Buffer.from(png));
|
|
255
264
|
const canvas = createCanvas(source.width, source.height);
|
|
256
265
|
const context = canvas.getContext("2d");
|
|
@@ -415,7 +424,7 @@ export async function createNativeGame(user, projectId, name, source, dimension
|
|
|
415
424
|
return { error: "Project workspace is unavailable" };
|
|
416
425
|
const id = randomUUID().replace(/-/g, "");
|
|
417
426
|
const revision = randomUUID().replace(/-/g, "");
|
|
418
|
-
const document = validateSource(source ?? (dimension === "3d" ? createNative3DGame(id) : { ...createTopDownRoomGame(id), schemaVersion:
|
|
427
|
+
const document = validateSource(source ?? (dimension === "3d" ? createNative3DGame(id) : { ...createTopDownRoomGame(id), schemaVersion: 4, engineVersion: "3" }), id, revision);
|
|
419
428
|
if ("error" in document)
|
|
420
429
|
return document;
|
|
421
430
|
if (source && ((document.schemaVersion === 3) !== (dimension === "3d"))) {
|
|
@@ -559,6 +568,9 @@ const install = {
|
|
|
559
568
|
if (parsed.data.mediaKind === "font" && !parsed.data.fontFormat) {
|
|
560
569
|
return { error: "Font binding needs a TrueType or OpenType format" };
|
|
561
570
|
}
|
|
571
|
+
if (parsed.data.mediaKind === "hdri") {
|
|
572
|
+
return { error: "HDRI candidates cannot be installed until HDRI preparation verifies their bytes and dimensions" };
|
|
573
|
+
}
|
|
562
574
|
const workspace = await workspaceOf(user, game);
|
|
563
575
|
if (!workspace)
|
|
564
576
|
return { error: "Game workspace is unavailable" };
|
|
@@ -596,7 +608,9 @@ const install = {
|
|
|
596
608
|
if (!candidate || createHash("sha256").update(candidate.bytes).digest("hex") !== parsed.data.digest) {
|
|
597
609
|
return { error: "Candidate asset is missing or changed" };
|
|
598
610
|
}
|
|
599
|
-
const { path,
|
|
611
|
+
const { path, bytes } = candidate;
|
|
612
|
+
let { extension, contentType } = candidate;
|
|
613
|
+
let targetPath = path;
|
|
600
614
|
if (base !== undefined && base !== game.current_revision)
|
|
601
615
|
return { error: "Game was modified concurrently" };
|
|
602
616
|
const draft = await Game.readDraft(user, game.id, workspace);
|
|
@@ -605,8 +619,19 @@ const install = {
|
|
|
605
619
|
const expectedUpdatedAt = typeof args["base_updated_at"] === "string" ? args["base_updated_at"] : draft.game.draft_updated_at;
|
|
606
620
|
let binding = parsed.data;
|
|
607
621
|
try {
|
|
608
|
-
if (binding.mediaKind === "
|
|
609
|
-
const {
|
|
622
|
+
if (draft.document.schemaVersion === 3 && binding.mediaKind === "image") {
|
|
623
|
+
const { prepareGameImage3D } = await importPack("game", "@nodetool-ai/game-renderer/preparation3d");
|
|
624
|
+
const prepared = await prepareGameImage3D(bytes, { assetId: binding.assetId, expectedDigest: binding.digest, signal: run.context.signal });
|
|
625
|
+
if (!prepared.ok) {
|
|
626
|
+
return { error: "Image preparation failed", diagnostics: prepared.diagnostics };
|
|
627
|
+
}
|
|
628
|
+
binding = { ...prepared.image.binding, required: binding.required ?? true };
|
|
629
|
+
extension = prepared.image.binding.format === "jpeg" ? "jpg" : prepared.image.binding.format;
|
|
630
|
+
contentType = `image/${prepared.image.binding.format}`;
|
|
631
|
+
targetPath = `${game.source_root}/assets/${binding.digest}.${extension}`;
|
|
632
|
+
}
|
|
633
|
+
else if (binding.mediaKind === "model") {
|
|
634
|
+
const { prepareGameModelBinding3D } = await importPack("game", "@nodetool-ai/game-renderer/preparation3d");
|
|
610
635
|
const prepared = await prepareGameModelBinding3D(bytes, { assetId: binding.assetId, expectedDigest: binding.digest, signal: run.context.signal });
|
|
611
636
|
if (!prepared.ok) {
|
|
612
637
|
return { error: "Model preparation failed", diagnostics: prepared.diagnostics };
|
|
@@ -645,8 +670,8 @@ const install = {
|
|
|
645
670
|
const storage = run.context.assetStorage;
|
|
646
671
|
if (!storage)
|
|
647
672
|
return { error: "Asset storage is unavailable" };
|
|
648
|
-
if (candidateWorkspace !== workspace)
|
|
649
|
-
await workspace.write(
|
|
673
|
+
if (candidateWorkspace !== workspace || targetPath !== path)
|
|
674
|
+
await workspace.write(targetPath, bytes, contentType);
|
|
650
675
|
const installed = await Asset.create({
|
|
651
676
|
user_id: user,
|
|
652
677
|
project_id: game.project_id,
|
|
@@ -744,6 +769,8 @@ const playtest = {
|
|
|
744
769
|
let wallTimeLimited = false;
|
|
745
770
|
let cancelled = false;
|
|
746
771
|
let scriptError = null;
|
|
772
|
+
// Script saves stay in memory for the playtest, so a run never reads or writes a player's saves.
|
|
773
|
+
const saves = new GamePlayerSaves(createMemoryGameSaveStorage());
|
|
747
774
|
try {
|
|
748
775
|
for (const input of inputs) {
|
|
749
776
|
if (state.tick % 240 === 0)
|
|
@@ -758,7 +785,11 @@ const playtest = {
|
|
|
758
785
|
}
|
|
759
786
|
let result;
|
|
760
787
|
try {
|
|
761
|
-
result = session.step(input);
|
|
788
|
+
result = session.step(saves.withLoaded(input));
|
|
789
|
+
if (result.saveRequests) {
|
|
790
|
+
saves.handle(result.saveRequests, () => session.snapshot());
|
|
791
|
+
await saves.idle();
|
|
792
|
+
}
|
|
762
793
|
}
|
|
763
794
|
catch (error) {
|
|
764
795
|
scriptError = { tick: state.tick + 1, message: error instanceof Error ? error.message : String(error) };
|
|
@@ -914,8 +945,11 @@ const generateAsset = {
|
|
|
914
945
|
return { error: error instanceof Error ? error.message : String(error) };
|
|
915
946
|
}
|
|
916
947
|
}
|
|
917
|
-
if (draft.document.schemaVersion === 3 && kind === "image") {
|
|
918
|
-
|
|
948
|
+
if (draft.document.schemaVersion === 3 && kind === "image" && args["preparation"] !== undefined) {
|
|
949
|
+
const preparation = z.record(z.string(), z.unknown()).safeParse(args["preparation"]);
|
|
950
|
+
if (!preparation.success || Object.keys(preparation.data).length > 0) {
|
|
951
|
+
return { error: "3D images do not support sprite preparation. Set UV tiling and offset on the material's texture slots." };
|
|
952
|
+
}
|
|
919
953
|
}
|
|
920
954
|
const reference = typeof args["reference_slot"] === "string" ? draft.document.assets[args["reference_slot"]] : undefined;
|
|
921
955
|
if (args["reference_slot"] !== undefined && !reference) {
|
|
@@ -924,7 +958,7 @@ const generateAsset = {
|
|
|
924
958
|
if (reference && kind !== "image") {
|
|
925
959
|
return { error: "reference_slot requires kind image" };
|
|
926
960
|
}
|
|
927
|
-
const { imagePreparationSettings, prepareGameImage, imagePreparationMetadata, gameFontFormat } = await
|
|
961
|
+
const { imagePreparationSettings, prepareGameImage, imagePreparationMetadata, gameFontFormat } = await importPack("game", "@nodetool-ai/game-nodes");
|
|
928
962
|
const settings = kind === "image" ? imagePreparationSettings.safeParse(args["preparation"] ?? {}) : null;
|
|
929
963
|
if (settings && !settings.success) {
|
|
930
964
|
return { error: "Invalid image preparation", issues: settings.error.issues };
|
|
@@ -1048,6 +1082,20 @@ const generateAsset = {
|
|
|
1048
1082
|
if (sourceBytes.byteLength > MAX_BUILD_ASSET_BYTES) {
|
|
1049
1083
|
return { error: "Game asset exceeds the supported asset size" };
|
|
1050
1084
|
}
|
|
1085
|
+
if (draft.document.schemaVersion === 3 && kind === "image") {
|
|
1086
|
+
const { prepareGameImage3D } = await importPack("game", "@nodetool-ai/game-renderer/preparation3d");
|
|
1087
|
+
const digest = createHash("sha256").update(sourceBytes).digest("hex");
|
|
1088
|
+
const prepared = await prepareGameImage3D(sourceBytes, { assetId: `generated:${digest}`, expectedDigest: digest, signal: run.context.signal });
|
|
1089
|
+
if (!prepared.ok) {
|
|
1090
|
+
return { error: "Image preparation failed", diagnostics: prepared.diagnostics };
|
|
1091
|
+
}
|
|
1092
|
+
const binding = prepared.image.binding;
|
|
1093
|
+
const extension = binding.format === "jpeg" ? "jpg" : binding.format;
|
|
1094
|
+
await workspace.write(`${game.source_root}/assets/${binding.digest}.${extension}`, prepared.image.bytes, `image/${binding.format}`);
|
|
1095
|
+
return { game_id: shortResourceId(game.id), slot, binding, candidate_workspace_id: game.workspace_id,
|
|
1096
|
+
draft_updated_at: draft.game.draft_updated_at, installed: false,
|
|
1097
|
+
next: "Call install_native_game_asset with this binding to install the verified candidate." };
|
|
1098
|
+
}
|
|
1051
1099
|
let bytes = sourceBytes;
|
|
1052
1100
|
let width = 1;
|
|
1053
1101
|
let height = 1;
|
|
@@ -1085,7 +1133,7 @@ const generateAsset = {
|
|
|
1085
1133
|
if (prepared.baseline !== undefined && frames?.[0]) {
|
|
1086
1134
|
binding.pivot = { x: 0.5, y: (prepared.baseline + 1) / frames[0].height };
|
|
1087
1135
|
}
|
|
1088
|
-
if (draft.document.schemaVersion
|
|
1136
|
+
if (draft.document.schemaVersion !== 1) {
|
|
1089
1137
|
binding.preparation = imagePreparationMetadata(settings.data);
|
|
1090
1138
|
binding.originalDimensions = { width: prepared.originalWidth, height: prepared.originalHeight };
|
|
1091
1139
|
if (prepared.trim) {
|
|
@@ -1123,7 +1171,7 @@ const generateAsset = {
|
|
|
1123
1171
|
binding.digest = digest;
|
|
1124
1172
|
binding.width = width;
|
|
1125
1173
|
binding.height = height;
|
|
1126
|
-
if (draft.document.schemaVersion
|
|
1174
|
+
if (draft.document.schemaVersion !== 1 && draft.document.schemaVersion !== 3) {
|
|
1127
1175
|
binding.provenance = typeof inputFile === "string" ? `import:${inputFile}`
|
|
1128
1176
|
: makeLut ? "generated:color-cube" : kind === "sfx" ? `node:${args["node_type"]}` : `${provider}:${model}:${generated.generation_id ?? ""}`;
|
|
1129
1177
|
}
|
|
@@ -1139,6 +1187,25 @@ const generateAsset = {
|
|
|
1139
1187
|
return { game_id: shortResourceId(game.id), slot, binding: parsedBinding.data, candidate_workspace_id: game.workspace_id,
|
|
1140
1188
|
draft_updated_at: draft.game.draft_updated_at, installed: false, next: "Call install_native_game_asset with this binding to install the candidate." };
|
|
1141
1189
|
}
|
|
1190
|
+
if (args["install"] === false) {
|
|
1191
|
+
const parsedBinding = legacyAssetBinding.safeParse(binding);
|
|
1192
|
+
if (!parsedBinding.success) {
|
|
1193
|
+
return { error: "Invalid asset binding", diagnostics: parsedBinding.error.issues };
|
|
1194
|
+
}
|
|
1195
|
+
const staged = { game_id: shortResourceId(game.id), slot, binding: parsedBinding.data, candidate_workspace_id: game.workspace_id,
|
|
1196
|
+
draft_updated_at: draft.game.draft_updated_at, installed: false, generation_id: generated.generation_id,
|
|
1197
|
+
next: "Call install_native_game_asset with this binding to install the candidate." };
|
|
1198
|
+
if (frames) {
|
|
1199
|
+
staged.frames = frames;
|
|
1200
|
+
}
|
|
1201
|
+
if (tiles) {
|
|
1202
|
+
staged.tiles = tiles;
|
|
1203
|
+
}
|
|
1204
|
+
if (lutSize) {
|
|
1205
|
+
staged.lut_size = lutSize;
|
|
1206
|
+
}
|
|
1207
|
+
return staged;
|
|
1208
|
+
}
|
|
1142
1209
|
const installed = await install.impl(run, { game_id: game.id, slot, binding, base_updated_at: draft.game.draft_updated_at });
|
|
1143
1210
|
if (typeof installed !== "object" || installed === null) {
|
|
1144
1211
|
return { generation_id: generated.generation_id, result: installed };
|
|
@@ -1165,141 +1232,177 @@ const generateAsset = {
|
|
|
1165
1232
|
return { generation_id: generated.generation_id, ...installed, ...extras };
|
|
1166
1233
|
}
|
|
1167
1234
|
};
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1235
|
+
const BUILD_CONTENT_TYPES = {
|
|
1236
|
+
js: "text/javascript", css: "text/css", json: "application/json", html: "text/html", png: "image/png", jpg: "image/jpeg",
|
|
1237
|
+
webp: "image/webp", wav: "audio/wav", ogg: "audio/ogg", mp3: "audio/mpeg", ttf: "font/ttf", otf: "font/otf",
|
|
1238
|
+
glb: "model/gltf-binary", wasm: "application/wasm", md: "text/markdown", toml: "text/plain", rs: "text/plain",
|
|
1239
|
+
ico: "image/vnd.microsoft.icon", icns: "image/icns"
|
|
1240
|
+
};
|
|
1241
|
+
/**
|
|
1242
|
+
* Builds an owned, published game revision into its project workspace: a static web player under
|
|
1243
|
+
* `game-builds/<game-id>/<revision>`, or a Tauri desktop project under `game-builds/<game-id>/<revision>-tauri-<settings>`.
|
|
1244
|
+
* The capability and the editor's export download share this path.
|
|
1245
|
+
*/
|
|
1246
|
+
export async function buildOwnedNativeGame(request) {
|
|
1247
|
+
const user = request.userId;
|
|
1248
|
+
const target = request.target ?? "web";
|
|
1249
|
+
const game = await ownedGame(user, request.gameId);
|
|
1250
|
+
if (!game)
|
|
1251
|
+
return { error: "Game not found" };
|
|
1252
|
+
const workspace = await workspaceOf(user, game);
|
|
1253
|
+
if (!workspace)
|
|
1254
|
+
return { error: "Game workspace is unavailable" };
|
|
1255
|
+
const revision = request.revision ?? game.current_revision;
|
|
1256
|
+
const document = await readDocument(workspace, game, revision);
|
|
1257
|
+
if (!document)
|
|
1258
|
+
return { error: "Game revision not found" };
|
|
1259
|
+
if (Object.keys(document.assets).length > MAX_BUILD_ASSETS) {
|
|
1260
|
+
return { error: `A standalone build supports at most ${MAX_BUILD_ASSETS} assets` };
|
|
1261
|
+
}
|
|
1262
|
+
const expectedDigests = new Map();
|
|
1263
|
+
for (const binding of Object.values(document.assets)) {
|
|
1264
|
+
if (binding.assetId.startsWith("builtin:"))
|
|
1265
|
+
continue;
|
|
1266
|
+
const digests = expectedDigests.get(binding.assetId) ?? new Set();
|
|
1267
|
+
digests.add(binding.digest);
|
|
1268
|
+
expectedDigests.set(binding.assetId, digests);
|
|
1269
|
+
}
|
|
1270
|
+
if ([...expectedDigests.values()].some((digests) => digests.size !== 1)) {
|
|
1271
|
+
return { error: "One source asset has conflicting digests in this revision" };
|
|
1272
|
+
}
|
|
1273
|
+
let buildPath = `game-builds/${game.id}/${revision}`;
|
|
1274
|
+
let entryPath = `${buildPath}/index.html`;
|
|
1275
|
+
let tauriApp;
|
|
1276
|
+
let metadata;
|
|
1277
|
+
if (target === "tauri") {
|
|
1278
|
+
tauriApp = { name: game.name };
|
|
1279
|
+
const { tauriAppMetadata } = await importPack("game", "@nodetool-ai/game-renderer/build-tauri");
|
|
1280
|
+
try {
|
|
1281
|
+
metadata = tauriAppMetadata(document, tauriApp);
|
|
1187
1282
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1283
|
+
catch (error) {
|
|
1284
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
1285
|
+
}
|
|
1286
|
+
// The app name comes from the game row, which can change without a new revision.
|
|
1287
|
+
const settings = createHash("sha256").update(JSON.stringify(metadata)).digest("hex").slice(0, 12);
|
|
1288
|
+
buildPath = `game-builds/${game.id}/${revision}-tauri-${settings}`;
|
|
1289
|
+
entryPath = `${buildPath}/src-tauri/tauri.conf.json`;
|
|
1290
|
+
}
|
|
1291
|
+
const done = { game_id: game.id, revision, target, build_path: buildPath, entry_path: entryPath, workspace, app: metadata ?? null };
|
|
1292
|
+
if (await workspace.exists(entryPath)) {
|
|
1293
|
+
return { ...done, cached: true };
|
|
1294
|
+
}
|
|
1295
|
+
const storage = request.storage;
|
|
1296
|
+
const cache = new Map();
|
|
1297
|
+
let totalAssetBytes = 0;
|
|
1298
|
+
const resolveAsset = async (assetId) => {
|
|
1299
|
+
const cached = cache.get(assetId);
|
|
1300
|
+
if (cached)
|
|
1301
|
+
return cached;
|
|
1302
|
+
if (!/^[a-f0-9]{32}$/.test(assetId))
|
|
1303
|
+
return null;
|
|
1304
|
+
const asset = await Asset.find(user, assetId);
|
|
1305
|
+
if (!asset || !storage)
|
|
1306
|
+
return null;
|
|
1307
|
+
const extension = MEDIA_EXTENSIONS[asset.content_type];
|
|
1308
|
+
if (!extension || (asset.size !== null && asset.size > MAX_BUILD_ASSET_BYTES))
|
|
1309
|
+
return null;
|
|
1310
|
+
const keys = assetKeyCandidates(user, `${asset.id}.${extension}`);
|
|
1311
|
+
let bytes = null;
|
|
1312
|
+
for (const key of keys) {
|
|
1313
|
+
bytes = await storage.retrieve(storage.uriForKey(key));
|
|
1314
|
+
if (bytes)
|
|
1315
|
+
break;
|
|
1195
1316
|
}
|
|
1196
|
-
if (
|
|
1197
|
-
return
|
|
1317
|
+
if (!bytes || bytes.byteLength > MAX_BUILD_ASSET_BYTES)
|
|
1318
|
+
return null;
|
|
1319
|
+
const digest = createHash("sha256").update(bytes).digest("hex");
|
|
1320
|
+
if (!expectedDigests.get(assetId)?.has(digest)) {
|
|
1321
|
+
throw new Error(`Asset digest changed for ${assetId}`);
|
|
1198
1322
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
return { game_id: game.id, revision, build_path: buildPath, entry_path: indexPath, cached: true };
|
|
1323
|
+
totalAssetBytes += bytes.byteLength;
|
|
1324
|
+
if (totalAssetBytes > MAX_BUILD_BYTES) {
|
|
1325
|
+
throw new Error(`A standalone build supports at most ${MAX_BUILD_BYTES} asset bytes`);
|
|
1203
1326
|
}
|
|
1204
|
-
const
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
const
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
};
|
|
1240
|
-
const scratch = await workspace.scratchDir();
|
|
1241
|
-
const staging = await mkdtemp(join(scratch, ".native-game-build-"));
|
|
1242
|
-
try {
|
|
1243
|
-
if (document.schemaVersion === 3) {
|
|
1244
|
-
const { buildStandaloneGame3D } = await import("@nodetool-ai/game-renderer/build3d");
|
|
1245
|
-
await buildStandaloneGame3D({ document, outputDir: join(staging, "player"), resolveAsset, signal: run.context.signal });
|
|
1246
|
-
}
|
|
1247
|
-
else {
|
|
1248
|
-
const { buildStandaloneGame } = await import("@nodetool-ai/game-renderer/build");
|
|
1249
|
-
await buildStandaloneGame({ document, outputDir: join(staging, "player"), resolveAsset });
|
|
1250
|
-
}
|
|
1251
|
-
const files = [];
|
|
1252
|
-
const pending = [""];
|
|
1253
|
-
let totalBytes = 0;
|
|
1254
|
-
while (pending.length > 0) {
|
|
1255
|
-
const directory = pending.pop() ?? "";
|
|
1256
|
-
for (const entry of await readdir(join(staging, "player", directory), { withFileTypes: true })) {
|
|
1257
|
-
const path = directory ? `${directory}/${entry.name}` : entry.name;
|
|
1258
|
-
if (entry.isDirectory()) {
|
|
1259
|
-
pending.push(path);
|
|
1260
|
-
}
|
|
1261
|
-
else if (entry.isFile()) {
|
|
1262
|
-
const bytes = await readFile(join(staging, "player", path));
|
|
1263
|
-
totalBytes += bytes.byteLength;
|
|
1264
|
-
if (files.length >= MAX_BUILD_FILES || totalBytes > MAX_BUILD_BYTES) {
|
|
1265
|
-
throw new Error("Standalone build exceeds file or byte limits");
|
|
1266
|
-
}
|
|
1267
|
-
files.push({ path, bytes });
|
|
1327
|
+
const resolved = { bytes, mimeType: asset.content_type };
|
|
1328
|
+
cache.set(assetId, resolved);
|
|
1329
|
+
return resolved;
|
|
1330
|
+
};
|
|
1331
|
+
const scratch = await workspace.scratchDir();
|
|
1332
|
+
const staging = await mkdtemp(join(scratch, ".native-game-build-"));
|
|
1333
|
+
try {
|
|
1334
|
+
const output = join(staging, "player");
|
|
1335
|
+
if (tauriApp) {
|
|
1336
|
+
const { buildTauriGameProject } = await importPack("game", "@nodetool-ai/game-renderer/build-tauri");
|
|
1337
|
+
await buildTauriGameProject({ document, outputDir: output, resolveAsset, app: tauriApp, signal: request.signal });
|
|
1338
|
+
}
|
|
1339
|
+
else if (document.schemaVersion === 3) {
|
|
1340
|
+
const { buildStandaloneGame3D } = await importPack("game", "@nodetool-ai/game-renderer/build3d");
|
|
1341
|
+
await buildStandaloneGame3D({ document, outputDir: output, resolveAsset, signal: request.signal });
|
|
1342
|
+
}
|
|
1343
|
+
else {
|
|
1344
|
+
const { buildStandaloneGame } = await importPack("game", "@nodetool-ai/game-renderer/build");
|
|
1345
|
+
await buildStandaloneGame({ document, outputDir: output, resolveAsset });
|
|
1346
|
+
}
|
|
1347
|
+
const files = [];
|
|
1348
|
+
const pending = [""];
|
|
1349
|
+
let totalBytes = 0;
|
|
1350
|
+
while (pending.length > 0) {
|
|
1351
|
+
const directory = pending.pop() ?? "";
|
|
1352
|
+
for (const entry of await readdir(join(output, directory), { withFileTypes: true })) {
|
|
1353
|
+
const path = directory ? `${directory}/${entry.name}` : entry.name;
|
|
1354
|
+
if (entry.isDirectory()) {
|
|
1355
|
+
pending.push(path);
|
|
1356
|
+
}
|
|
1357
|
+
else if (entry.isFile()) {
|
|
1358
|
+
const bytes = await readFile(join(output, path));
|
|
1359
|
+
totalBytes += bytes.byteLength;
|
|
1360
|
+
if (files.length >= MAX_BUILD_FILES || totalBytes > MAX_BUILD_BYTES) {
|
|
1361
|
+
throw new Error("Standalone build exceeds file or byte limits");
|
|
1268
1362
|
}
|
|
1363
|
+
files.push({ path, bytes });
|
|
1269
1364
|
}
|
|
1270
1365
|
}
|
|
1271
|
-
if (!files.some((file) => file.path === "index.html")) {
|
|
1272
|
-
throw new Error("Standalone player entry is missing");
|
|
1273
|
-
}
|
|
1274
|
-
for (const file of files.filter((item) => item.path !== "index.html")) {
|
|
1275
|
-
const contentType = file.path.endsWith(".js") ? "text/javascript"
|
|
1276
|
-
: file.path.endsWith(".css") ? "text/css"
|
|
1277
|
-
: file.path.endsWith(".json") ? "application/json"
|
|
1278
|
-
: file.path.endsWith(".png") ? "image/png"
|
|
1279
|
-
: file.path.endsWith(".jpg") ? "image/jpeg"
|
|
1280
|
-
: file.path.endsWith(".webp") ? "image/webp"
|
|
1281
|
-
: file.path.endsWith(".wav") ? "audio/wav"
|
|
1282
|
-
: file.path.endsWith(".ogg") ? "audio/ogg"
|
|
1283
|
-
: file.path.endsWith(".mp3") ? "audio/mpeg"
|
|
1284
|
-
: file.path.endsWith(".ttf") ? "font/ttf"
|
|
1285
|
-
: file.path.endsWith(".otf") ? "font/otf"
|
|
1286
|
-
: file.path.endsWith(".glb") ? "model/gltf-binary"
|
|
1287
|
-
: file.path.endsWith(".wasm") ? "application/wasm"
|
|
1288
|
-
: "application/octet-stream";
|
|
1289
|
-
await workspace.write(`${buildPath}/${file.path}`, file.bytes, contentType);
|
|
1290
|
-
}
|
|
1291
|
-
const index = files.find((file) => file.path === "index.html");
|
|
1292
|
-
if (!index)
|
|
1293
|
-
throw new Error("Standalone player entry is missing");
|
|
1294
|
-
await workspace.write(indexPath, index.bytes, "text/html");
|
|
1295
|
-
return { game_id: game.id, revision, build_path: buildPath, entry_path: indexPath, cached: false };
|
|
1296
|
-
}
|
|
1297
|
-
catch (error) {
|
|
1298
|
-
return { error: error instanceof Error ? error.message : String(error) };
|
|
1299
|
-
}
|
|
1300
|
-
finally {
|
|
1301
|
-
await rm(staging, { recursive: true, force: true });
|
|
1302
1366
|
}
|
|
1367
|
+
const entryFile = entryPath.slice(buildPath.length + 1);
|
|
1368
|
+
const entry = files.find((file) => file.path === entryFile);
|
|
1369
|
+
if (!entry)
|
|
1370
|
+
throw new Error("Standalone build entry is missing");
|
|
1371
|
+
const contentType = (path) => BUILD_CONTENT_TYPES[path.slice(path.lastIndexOf(".") + 1)] ?? "application/octet-stream";
|
|
1372
|
+
// The entry file is written last, so its presence marks a complete build for the cache check above.
|
|
1373
|
+
for (const file of files.filter((item) => item !== entry)) {
|
|
1374
|
+
await workspace.write(`${buildPath}/${file.path}`, file.bytes, contentType(file.path));
|
|
1375
|
+
}
|
|
1376
|
+
await workspace.write(entryPath, entry.bytes, contentType(entry.path));
|
|
1377
|
+
return { ...done, cached: false };
|
|
1378
|
+
}
|
|
1379
|
+
catch (error) {
|
|
1380
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
1381
|
+
}
|
|
1382
|
+
finally {
|
|
1383
|
+
await rm(staging, { recursive: true, force: true });
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
const buildGame = {
|
|
1387
|
+
spec: gameSpecs[5],
|
|
1388
|
+
impl: async (run, args) => {
|
|
1389
|
+
const user = userId(run);
|
|
1390
|
+
const id = args["game_id"];
|
|
1391
|
+
if (!user || typeof id !== "string")
|
|
1392
|
+
return { error: "game_id is required in a user session" };
|
|
1393
|
+
const target = args["target"] ?? "web";
|
|
1394
|
+
if (target !== "web" && target !== "tauri")
|
|
1395
|
+
return { error: "target must be web or tauri" };
|
|
1396
|
+
const revision = args["revision"];
|
|
1397
|
+
const result = await buildOwnedNativeGame({ userId: user, gameId: id, revision: typeof revision === "string" ? revision : undefined,
|
|
1398
|
+
target, storage: run.context.assetStorage, signal: run.context.signal });
|
|
1399
|
+
if ("error" in result)
|
|
1400
|
+
return { error: result.error };
|
|
1401
|
+
const report = { game_id: result.game_id, revision: result.revision, target: result.target, build_path: result.build_path,
|
|
1402
|
+
entry_path: result.entry_path, cached: result.cached };
|
|
1403
|
+
return target === "tauri"
|
|
1404
|
+
? { ...report, next: "The project builds desktop installers with `npm install && npm run build` in a checkout that has Rust and the Tauri prerequisites." }
|
|
1405
|
+
: report;
|
|
1303
1406
|
}
|
|
1304
1407
|
};
|
|
1305
1408
|
const listExamples = {
|
|
@@ -1423,10 +1526,24 @@ const autoplay = {
|
|
|
1423
1526
|
return { game_id: shortResourceId(game.id), revision: document.revision, ...route, win_tick: winTick, reached_targets: reachedTargets, level_stats: levelStats, state: snapshot };
|
|
1424
1527
|
}
|
|
1425
1528
|
};
|
|
1529
|
+
function withDraftRecovery(capability) {
|
|
1530
|
+
return { ...capability, impl: async (run, args) => {
|
|
1531
|
+
try {
|
|
1532
|
+
return await capability.impl(run, args);
|
|
1533
|
+
}
|
|
1534
|
+
catch (error) {
|
|
1535
|
+
if (error instanceof MissingGameDraftSourceError) {
|
|
1536
|
+
return { error: error.message, code: "game_draft_source_unavailable" };
|
|
1537
|
+
}
|
|
1538
|
+
throw error;
|
|
1539
|
+
}
|
|
1540
|
+
} };
|
|
1541
|
+
}
|
|
1426
1542
|
export const module = {
|
|
1427
1543
|
module: "game",
|
|
1428
1544
|
exports: [create, get, save, install, playtest, buildGame, edit, capture, generateAsset, listExamples, getExample, installExample, autoplay,
|
|
1429
1545
|
{ spec: previewGameAuthoringSpec, impl: previewGameAuthoring },
|
|
1430
|
-
{ spec: applyGameAuthoringSpec, impl: applyGameAuthoring }
|
|
1546
|
+
{ spec: applyGameAuthoringSpec, impl: applyGameAuthoring },
|
|
1547
|
+
{ spec: browseGameAssetsSpec, impl: browseGameAssets }].map(withDraftRecovery)
|
|
1431
1548
|
};
|
|
1432
1549
|
//# sourceMappingURL=game.js.map
|