@bitmagic/cli 0.1.52-dev.0 → 0.1.52-dev.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -6
- package/dist/assets/add.d.ts +40 -0
- package/dist/assets/add.js +229 -0
- package/dist/assets/add.js.map +1 -0
- package/dist/assets/entry.d.ts +44 -0
- package/dist/assets/entry.js +71 -0
- package/dist/assets/entry.js.map +1 -0
- package/dist/assets/file-kind.d.ts +61 -0
- package/dist/assets/file-kind.js +195 -0
- package/dist/assets/file-kind.js.map +1 -0
- package/dist/assets/list.d.ts +27 -0
- package/dist/assets/list.js +50 -0
- package/dist/assets/list.js.map +1 -0
- package/dist/assets/remove.d.ts +50 -0
- package/dist/assets/remove.js +91 -0
- package/dist/assets/remove.js.map +1 -0
- package/dist/assets/voxelize-glb.d.ts +37 -0
- package/dist/assets/voxelize-glb.js +65 -0
- package/dist/assets/voxelize-glb.js.map +1 -0
- package/dist/assets/vxl-summary.d.ts +32 -0
- package/dist/assets/vxl-summary.js +118 -0
- package/dist/assets/vxl-summary.js.map +1 -0
- package/dist/bin.js +25 -0
- package/dist/bin.js.map +1 -1
- package/dist/browser-gl.d.ts +46 -8
- package/dist/browser-gl.js +54 -26
- package/dist/browser-gl.js.map +1 -1
- package/dist/cli.d.ts +22 -0
- package/dist/cli.js +17 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/assets.d.ts +56 -0
- package/dist/commands/assets.js +328 -0
- package/dist/commands/assets.js.map +1 -0
- package/dist/commands/cover.js +2 -3
- package/dist/commands/cover.js.map +1 -1
- package/dist/commands/forge.d.ts +7 -9
- package/dist/commands/forge.js +12 -14
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.d.ts +41 -9
- package/dist/commands/generate.js +234 -54
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.js +19 -12
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/judge.d.ts +36 -0
- package/dist/commands/judge.js +216 -0
- package/dist/commands/judge.js.map +1 -0
- package/dist/commands/levels.d.ts +33 -0
- package/dist/commands/levels.js +241 -0
- package/dist/commands/levels.js.map +1 -0
- package/dist/commands/number-flag.d.ts +7 -0
- package/dist/commands/number-flag.js +38 -0
- package/dist/commands/number-flag.js.map +1 -0
- package/dist/commands/publish.js +13 -0
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/reset-account.js +2 -8
- package/dist/commands/reset-account.js.map +1 -1
- package/dist/commands/run-deps.d.ts +27 -0
- package/dist/commands/run-deps.js +43 -0
- package/dist/commands/run-deps.js.map +1 -0
- package/dist/commands/verify.js +3 -3
- package/dist/commands/verify.js.map +1 -1
- package/dist/editor/journal.d.ts +48 -1
- package/dist/editor/journal.js +44 -0
- package/dist/editor/journal.js.map +1 -1
- package/dist/editor/previews.d.ts +6 -0
- package/dist/editor/previews.js +16 -0
- package/dist/editor/previews.js.map +1 -1
- package/dist/editor/server.js +228 -53
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.js +221 -116
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/editor/voxel-save.d.ts +47 -0
- package/dist/editor/voxel-save.js +49 -0
- package/dist/editor/voxel-save.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +16 -0
- package/dist/errors.js.map +1 -1
- package/dist/forge/apply-modifications.d.ts +18 -1
- package/dist/forge/apply-modifications.js +15 -32
- package/dist/forge/apply-modifications.js.map +1 -1
- package/dist/forge/browser-host.js +9 -4
- package/dist/forge/browser-host.js.map +1 -1
- package/dist/forge/voxelize-session.d.ts +61 -0
- package/dist/forge/voxelize-session.js +137 -0
- package/dist/forge/voxelize-session.js.map +1 -0
- package/dist/generate/model.d.ts +36 -0
- package/dist/generate/model.js +90 -0
- package/dist/generate/model.js.map +1 -0
- package/dist/generate/prop.d.ts +52 -4
- package/dist/generate/prop.js +169 -102
- package/dist/generate/prop.js.map +1 -1
- package/dist/generate/stream.d.ts +7 -0
- package/dist/generate/stream.js +2 -1
- package/dist/generate/stream.js.map +1 -1
- package/dist/generate/vehicle.js +5 -85
- package/dist/generate/vehicle.js.map +1 -1
- package/dist/http/signed-upload.d.ts +55 -0
- package/dist/http/signed-upload.js +168 -0
- package/dist/http/signed-upload.js.map +1 -0
- package/dist/judge/facts.d.ts +18 -0
- package/dist/judge/facts.js +180 -0
- package/dist/judge/facts.js.map +1 -0
- package/dist/judge/record.d.ts +16 -0
- package/dist/judge/record.js +55 -0
- package/dist/judge/record.js.map +1 -0
- package/dist/judge/stream.d.ts +36 -0
- package/dist/judge/stream.js +122 -0
- package/dist/judge/stream.js.map +1 -0
- package/dist/levels/registry.d.ts +88 -0
- package/dist/levels/registry.js +309 -0
- package/dist/levels/registry.js.map +1 -0
- package/dist/messages/check.d.ts +30 -0
- package/dist/messages/check.js +119 -0
- package/dist/messages/check.js.map +1 -0
- package/dist/messages/notice.d.ts +58 -0
- package/dist/messages/notice.js +54 -0
- package/dist/messages/notice.js.map +1 -0
- package/dist/messages/refresh.d.ts +19 -0
- package/dist/messages/refresh.js +42 -0
- package/dist/messages/refresh.js.map +1 -0
- package/dist/project/jobs.d.ts +1 -1
- package/dist/project/jobs.js.map +1 -1
- package/dist/publish/client.d.ts +13 -0
- package/dist/publish/client.js +13 -34
- package/dist/publish/client.js.map +1 -1
- package/dist/publish/platform.d.ts +46 -0
- package/dist/publish/platform.js +92 -0
- package/dist/publish/platform.js.map +1 -0
- package/dist/scaffold/project-files.d.ts +8 -0
- package/dist/scaffold/project-files.js +192 -34
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.js +2 -1
- package/dist/scaffold/project.js.map +1 -1
- package/dist/verify/browser.d.ts +11 -6
- package/dist/verify/browser.js +31 -15
- package/dist/verify/browser.js.map +1 -1
- package/dist/verify/iterate.js +3 -3
- package/dist/verify/iterate.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The on-disk record of the last `bitmagic judge` run.
|
|
3
|
+
*
|
|
4
|
+
* Lives at `.bitmagic/judge.json` — inside `.bitmagic` so it is gitignored and
|
|
5
|
+
* excluded from the publish fingerprint (judging never invalidates a passing
|
|
6
|
+
* verify), and deliberately NOT inside `.bitmagic/verify/`: that directory has
|
|
7
|
+
* gating semantics of its own (`--fast` deletes files there, publish reads
|
|
8
|
+
* `result.json`), and a foreign file in it invites accidents.
|
|
9
|
+
*
|
|
10
|
+
* `fingerprint` is the same project fingerprint verify stamps, so a reader can
|
|
11
|
+
* tell "judged this exact state" from "judged, then edited". There is no
|
|
12
|
+
* designHash-style cache on purpose — the game changes between judges by
|
|
13
|
+
* design, and every run is a deliberate, cheap spend.
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import { judgeScorecardSchema } from '@bitmagic/asset-core';
|
|
18
|
+
const JUDGE_RECORD_FILE = path.join('.bitmagic', 'judge.json');
|
|
19
|
+
export function judgeRecordPath(root) {
|
|
20
|
+
return path.join(root, JUDGE_RECORD_FILE);
|
|
21
|
+
}
|
|
22
|
+
export function writeJudgeRecord(root, record) {
|
|
23
|
+
fs.mkdirSync(path.dirname(judgeRecordPath(root)), { recursive: true });
|
|
24
|
+
fs.writeFileSync(judgeRecordPath(root), `${JSON.stringify(record, null, 2)}\n`);
|
|
25
|
+
}
|
|
26
|
+
export function readJudgeRecord(root) {
|
|
27
|
+
let parsed;
|
|
28
|
+
try {
|
|
29
|
+
parsed = JSON.parse(fs.readFileSync(judgeRecordPath(root), 'utf-8'));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
35
|
+
return null;
|
|
36
|
+
const record = parsed;
|
|
37
|
+
const scorecard = judgeScorecardSchema.safeParse(record.scorecard);
|
|
38
|
+
if (!scorecard.success)
|
|
39
|
+
return null;
|
|
40
|
+
if (typeof record.fingerprint !== 'string'
|
|
41
|
+
|| typeof record.at !== 'string'
|
|
42
|
+
|| typeof record.model !== 'string'
|
|
43
|
+
|| typeof record.screenshotPath !== 'string') {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
scorecard: scorecard.data,
|
|
48
|
+
fingerprint: record.fingerprint,
|
|
49
|
+
at: record.at,
|
|
50
|
+
model: record.model,
|
|
51
|
+
screenshotPath: record.screenshotPath,
|
|
52
|
+
...(typeof record.genre === 'string' ? { genre: record.genre } : {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/judge/record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAuB,MAAM,sBAAsB,CAAC;AAEjF,MAAM,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AAE/D,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC;AAC5C,CAAC;AAeD,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,MAAmB;IAChE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvE,EAAE,CAAC,aAAa,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/D,MAAM,MAAM,GAAG,MAAiC,CAAC;IACjD,MAAM,SAAS,GAAG,oBAAoB,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACnE,IAAI,CAAC,SAAS,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACpC,IACE,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ;WACnC,OAAO,MAAM,CAAC,EAAE,KAAK,QAAQ;WAC7B,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;WAChC,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ,EAC5C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO;QACL,SAAS,EAAE,SAAS,CAAC,IAAI;QACzB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,EAAE,EAAE,MAAM,CAAC,EAAE;QACb,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,GAAG,CAAC,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client for `POST /api/cli/v1/judge/stream` — one vision call that grades
|
|
3
|
+
* the verify screenshot(s) against a rubric and returns a scorecard.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `forge/stream.ts` in shape (same SSE plumbing, same pre-stream
|
|
6
|
+
* status mapping, same "a truncated stream is not a confirmed failure"
|
|
7
|
+
* discipline), but far simpler: there is no job to resume — a judge run that
|
|
8
|
+
* dies is simply re-run — and the terminal payload is
|
|
9
|
+
* `{ success, scorecard, model, sparks }`.
|
|
10
|
+
*/
|
|
11
|
+
import { type JudgeScorecard } from '@bitmagic/asset-core';
|
|
12
|
+
import type { Environment } from '../config/environments.js';
|
|
13
|
+
export interface JudgeSuccess {
|
|
14
|
+
success: true;
|
|
15
|
+
scorecard: JudgeScorecard;
|
|
16
|
+
model: string;
|
|
17
|
+
sparks: number;
|
|
18
|
+
}
|
|
19
|
+
export interface JudgeFailure {
|
|
20
|
+
success: false;
|
|
21
|
+
message: string;
|
|
22
|
+
sparks: number;
|
|
23
|
+
}
|
|
24
|
+
export type JudgeOutcome = JudgeSuccess | JudgeFailure;
|
|
25
|
+
export interface JudgeStreamDeps {
|
|
26
|
+
fetch: typeof globalThis.fetch;
|
|
27
|
+
/** Called for every progress frame the server sends (there are few). */
|
|
28
|
+
onProgress: (message: string) => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* POSTs the rubric + screenshot(s) and consumes the SSE response. A stream
|
|
32
|
+
* that closes with no terminal frame is a truncated connection, not a
|
|
33
|
+
* confirmed failure — nothing is saved server-side, so the remedy is simply
|
|
34
|
+
* to run the command again.
|
|
35
|
+
*/
|
|
36
|
+
export declare function requestJudge(environment: Environment, accessToken: string, body: Record<string, unknown>, deps: JudgeStreamDeps): Promise<JudgeOutcome>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client for `POST /api/cli/v1/judge/stream` — one vision call that grades
|
|
3
|
+
* the verify screenshot(s) against a rubric and returns a scorecard.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `forge/stream.ts` in shape (same SSE plumbing, same pre-stream
|
|
6
|
+
* status mapping, same "a truncated stream is not a confirmed failure"
|
|
7
|
+
* discipline), but far simpler: there is no job to resume — a judge run that
|
|
8
|
+
* dies is simply re-run — and the terminal payload is
|
|
9
|
+
* `{ success, scorecard, model, sparks }`.
|
|
10
|
+
*/
|
|
11
|
+
import { judgeScorecardSchema } from '@bitmagic/asset-core';
|
|
12
|
+
import { CliError } from '../errors.js';
|
|
13
|
+
import { consumeSseStream } from '../http/sse.js';
|
|
14
|
+
import { isSpillConsentRefusal, sparkRefusalCode, spillConsentMessage } from '../http/spark-refusal.js';
|
|
15
|
+
import { apiServerHeaders } from '../telemetry/request-headers.js';
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === 'object' && value !== null;
|
|
18
|
+
}
|
|
19
|
+
/** Map a pre-stream error status to the message a creator can act on. */
|
|
20
|
+
async function toCliError(response, gameId) {
|
|
21
|
+
const body = await response
|
|
22
|
+
.json()
|
|
23
|
+
.then((parsed) => (isRecord(parsed) ? parsed : {}))
|
|
24
|
+
.catch(() => ({}));
|
|
25
|
+
const serverMessage = typeof body.error === 'string' && body.error.length > 0 ? body.error : null;
|
|
26
|
+
if (response.status === 403) {
|
|
27
|
+
return new CliError(serverMessage ?? `You cannot judge game "${gameId}".`);
|
|
28
|
+
}
|
|
29
|
+
if (response.status === 402) {
|
|
30
|
+
const code = sparkRefusalCode(body);
|
|
31
|
+
if (isSpillConsentRefusal(body)) {
|
|
32
|
+
return new CliError(spillConsentMessage('a judge run'), 1, code);
|
|
33
|
+
}
|
|
34
|
+
const { balance, required } = body;
|
|
35
|
+
const detail = typeof balance === 'number' && typeof required === 'number'
|
|
36
|
+
? ` You have ${balance} sparks; a judge run requires at least ${required}.`
|
|
37
|
+
: '';
|
|
38
|
+
return new CliError(`Not enough sparks to run the judge.${detail}`, 1, code);
|
|
39
|
+
}
|
|
40
|
+
if (response.status === 400) {
|
|
41
|
+
return new CliError(serverMessage ?? 'The server rejected this judge request.');
|
|
42
|
+
}
|
|
43
|
+
return new CliError(`api-server returned HTTP ${response.status} starting the judge`
|
|
44
|
+
+ (serverMessage ? `: ${serverMessage}.` : '.'));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* POSTs the rubric + screenshot(s) and consumes the SSE response. A stream
|
|
48
|
+
* that closes with no terminal frame is a truncated connection, not a
|
|
49
|
+
* confirmed failure — nothing is saved server-side, so the remedy is simply
|
|
50
|
+
* to run the command again.
|
|
51
|
+
*/
|
|
52
|
+
export async function requestJudge(environment, accessToken, body, deps) {
|
|
53
|
+
const gameId = typeof body.gameId === 'string' ? body.gameId : '(unknown game)';
|
|
54
|
+
const url = `${environment.apiUrl}/api/cli/v1/judge/stream`;
|
|
55
|
+
let response;
|
|
56
|
+
try {
|
|
57
|
+
response = await deps.fetch(url, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
...apiServerHeaders(),
|
|
61
|
+
Authorization: `Bearer ${accessToken}`,
|
|
62
|
+
Accept: 'text/event-stream',
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify(body),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
throw new CliError(`Cannot reach api-server at ${environment.apiUrl}.`, 1, 'unreachable');
|
|
70
|
+
}
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw await toCliError(response, gameId);
|
|
73
|
+
}
|
|
74
|
+
if (!response.body) {
|
|
75
|
+
throw new CliError('The server accepted the judge request but returned no stream body.');
|
|
76
|
+
}
|
|
77
|
+
let outcome = null;
|
|
78
|
+
const onEvent = (event) => {
|
|
79
|
+
if (event.event === 'progress') {
|
|
80
|
+
const message = isRecord(event.data) && typeof event.data.message === 'string'
|
|
81
|
+
? event.data.message
|
|
82
|
+
: null;
|
|
83
|
+
if (message !== null)
|
|
84
|
+
deps.onProgress(message);
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (event.event === 'result') {
|
|
88
|
+
const data = isRecord(event.data) ? event.data : {};
|
|
89
|
+
// Validated HERE, not trusted: `.bitmagic/judge.json` promises a
|
|
90
|
+
// schema-valid scorecard to whoever reads it, and the shared schema is
|
|
91
|
+
// exactly what makes that promise checkable on both sides of the wire.
|
|
92
|
+
const scorecard = judgeScorecardSchema.safeParse(data.scorecard);
|
|
93
|
+
if (data.success === true && scorecard.success) {
|
|
94
|
+
outcome = {
|
|
95
|
+
success: true,
|
|
96
|
+
scorecard: scorecard.data,
|
|
97
|
+
model: typeof data.model === 'string' ? data.model : '(unknown)',
|
|
98
|
+
sparks: typeof data.sparks === 'number' ? data.sparks : 0,
|
|
99
|
+
};
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
if (event.event === 'error') {
|
|
105
|
+
const data = isRecord(event.data) ? event.data : {};
|
|
106
|
+
outcome = {
|
|
107
|
+
success: false,
|
|
108
|
+
message: typeof data.message === 'string' ? data.message : 'The judge failed without reporting a reason.',
|
|
109
|
+
sparks: typeof data.sparks === 'number' ? data.sparks : 0,
|
|
110
|
+
};
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
const sawTerminal = await consumeSseStream(response.body, onEvent);
|
|
116
|
+
if (!sawTerminal || outcome === null) {
|
|
117
|
+
throw new CliError('The connection dropped before the judge finished — this is not a confirmed failure. '
|
|
118
|
+
+ 'Nothing is saved server-side; run `bitmagic judge` again.');
|
|
119
|
+
}
|
|
120
|
+
return outcome;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stream.js","sourceRoot":"","sources":["../../src/judge/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,oBAAoB,EAAuB,MAAM,sBAAsB,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,OAAO,EAAE,gBAAgB,EAAiB,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACxG,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAyBnE,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,yEAAyE;AACzE,KAAK,UAAU,UAAU,CAAC,QAAuB,EAAE,MAAc;IAC/D,MAAM,IAAI,GAA4B,MAAM,QAAQ;SACjD,IAAI,EAAE;SACN,IAAI,CAAC,CAAC,MAAe,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SAC3D,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrB,MAAM,aAAa,GAAG,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IAElG,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,IAAI,QAAQ,CAAC,aAAa,IAAI,0BAA0B,MAAM,IAAI,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,OAAO,IAAI,QAAQ,CAAC,mBAAmB,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;QACnC,MAAM,MAAM,GAAG,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;YACxE,CAAC,CAAC,aAAa,OAAO,0CAA0C,QAAQ,GAAG;YAC3E,CAAC,CAAC,EAAE,CAAC;QACP,OAAO,IAAI,QAAQ,CAAC,sCAAsC,MAAM,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/E,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC5B,OAAO,IAAI,QAAQ,CAAC,aAAa,IAAI,yCAAyC,CAAC,CAAC;IAClF,CAAC;IACD,OAAO,IAAI,QAAQ,CACjB,4BAA4B,QAAQ,CAAC,MAAM,qBAAqB;UAC5D,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,aAAa,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAClD,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,WAAwB,EACxB,WAAmB,EACnB,IAA6B,EAC7B,IAAqB;IAErB,MAAM,MAAM,GAAG,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC;IAChF,MAAM,GAAG,GAAG,GAAG,WAAW,CAAC,MAAM,0BAA0B,CAAC;IAE5D,IAAI,QAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE;YAC/B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,GAAG,gBAAgB,EAAE;gBACrB,aAAa,EAAE,UAAU,WAAW,EAAE;gBACtC,MAAM,EAAE,mBAAmB;gBAC3B,cAAc,EAAE,kBAAkB;aACnC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,QAAQ,CAAC,8BAA8B,WAAW,CAAC,MAAM,GAAG,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC;IAC5F,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC3C,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,QAAQ,CAAC,oEAAoE,CAAC,CAAC;IAC3F,CAAC;IAED,IAAI,OAAO,GAAwB,IAAI,CAAC;IAExC,MAAM,OAAO,GAAG,CAAC,KAAe,EAAW,EAAE;QAC3C,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;YAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,KAAK,QAAQ;gBAC5E,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO;gBACpB,CAAC,CAAC,IAAI,CAAC;YACT,IAAI,OAAO,KAAK,IAAI;gBAAE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YAC/C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,iEAAiE;YACjE,uEAAuE;YACvE,uEAAuE;YACvE,MAAM,SAAS,GAAG,oBAAoB,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACjE,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,SAAS,CAAC,OAAO,EAAE,CAAC;gBAC/C,OAAO,GAAG;oBACR,OAAO,EAAE,IAAI;oBACb,SAAS,EAAE,SAAS,CAAC,IAAI;oBACzB,KAAK,EAAE,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW;oBAChE,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;iBAC1D,CAAC;gBACF,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,OAAO,GAAG;gBACR,OAAO,EAAE,KAAK;gBACd,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,8CAA8C;gBACzG,MAAM,EAAE,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;aAC1D,CAAC;YACF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACnE,IAAI,CAAC,WAAW,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrC,MAAM,IAAI,QAAQ,CAChB,sFAAsF;cACpF,2DAA2D,CAC9D,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bitmagic levels` — the level registry in `worldProfileData.levels`, without the web Creator.
|
|
3
|
+
*
|
|
4
|
+
* `forge` can CREATE levels but never could manage them: only a game's first forge becomes the
|
|
5
|
+
* start level (or `forge --make-start` at creation), so fixing a wrong start, renaming, or deleting
|
|
6
|
+
* a level meant opening the Creator.
|
|
7
|
+
*
|
|
8
|
+
* ── The invariant this file exists to respect ────────────────────────────────────────────────
|
|
9
|
+
*
|
|
10
|
+
* `worldProfileData` keeps three LEGACY MIRRORS — `voxelUrl`, `spawnPoints` and
|
|
11
|
+
* `playerSpawnPosition` — describing the START level, because frozen genre and work-template code
|
|
12
|
+
* boots from those rather than from the registry (see `WorldLevel` in game/src/types/game.ts).
|
|
13
|
+
*
|
|
14
|
+
* Nothing self-heals on the normal boot path. `applyBootLevelOverride` does recompute them, but
|
|
15
|
+
* only when the main screen's level chooser passes a `bootLevelId`. So a `startLevelId` moved
|
|
16
|
+
* without its mirrors boots the NEW level's objects, lighting and navmesh over the OLD level's
|
|
17
|
+
* terrain, and nothing throws. That is the failure this module exists to prevent.
|
|
18
|
+
*
|
|
19
|
+
* The mirrors come from `buildStartLevelMirrors` in `@bitmagic/world-forger`, deliberately rather
|
|
20
|
+
* than reimplemented — the same function the forge uses, carrying a subtlety worth not
|
|
21
|
+
* rediscovering: spawn mirrors are written ONLY when the level owns spawn points, because a level
|
|
22
|
+
* without its own inherits the globals, which are then already correct.
|
|
23
|
+
*
|
|
24
|
+
* Pure: it computes modifications and never applies them.
|
|
25
|
+
*/
|
|
26
|
+
import { type LevelEntry, type WorldJsonModification } from '@bitmagic/world-forger/pipeline/index.js';
|
|
27
|
+
import { type JsonObject } from '../project/world-json.js';
|
|
28
|
+
export interface LevelRow {
|
|
29
|
+
id: string;
|
|
30
|
+
name: string;
|
|
31
|
+
vwldAssetId: string;
|
|
32
|
+
/** The `.vwld` asset is absent from `assets[]`, so this level has nothing to load. */
|
|
33
|
+
missingAsset: boolean;
|
|
34
|
+
start: boolean;
|
|
35
|
+
spawnPoints: number;
|
|
36
|
+
/** Placed objects tagged to this level. Untagged ones are global and belong to none. */
|
|
37
|
+
placed: number;
|
|
38
|
+
}
|
|
39
|
+
export declare function listLevels(world: JsonObject): LevelRow[];
|
|
40
|
+
export interface LevelPlan {
|
|
41
|
+
modifications: WorldJsonModification[];
|
|
42
|
+
/** Said after the write. Never a reason to refuse. */
|
|
43
|
+
notes: string[];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Make `levelId` the level the game boots into — registry AND mirrors, together.
|
|
47
|
+
*
|
|
48
|
+
* Refused when the level's `.vwld` asset is missing or has no url: the mirrors are what actually
|
|
49
|
+
* boot the game, so writing `startLevelId` alone would leave `voxelUrl` describing the previous
|
|
50
|
+
* level, and the registry and the thing that loads would disagree with no error anywhere.
|
|
51
|
+
*/
|
|
52
|
+
export declare function planSetStart(world: JsonObject, levelId: string): LevelPlan;
|
|
53
|
+
export declare function planRename(world: JsonObject, levelId: string, name: string): LevelPlan;
|
|
54
|
+
export interface AddPlan extends LevelPlan {
|
|
55
|
+
level: LevelEntry;
|
|
56
|
+
/** True when this made the game multi-level for the first time. */
|
|
57
|
+
converted: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Register an existing `.vwld` asset as a level.
|
|
61
|
+
*
|
|
62
|
+
* Start-level policy matches `forge`'s: a game's FIRST level becomes the start, later ones do not,
|
|
63
|
+
* and `--make-start` overrides. Stealing the start from a working game because someone registered
|
|
64
|
+
* a second level would be the surprising outcome.
|
|
65
|
+
*/
|
|
66
|
+
export declare function planAdd(world: JsonObject, vwldAssetId: string, options: {
|
|
67
|
+
name?: string;
|
|
68
|
+
makeStart: boolean;
|
|
69
|
+
}): AddPlan;
|
|
70
|
+
export interface RemovePlan extends LevelPlan {
|
|
71
|
+
level: LevelEntry;
|
|
72
|
+
/** Placed instances tagged to this level, which go with it. */
|
|
73
|
+
placedRemoved: number;
|
|
74
|
+
/** Doors and key items scoped to it, which also go. */
|
|
75
|
+
scopedRemoved: string[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Remove a level from the registry.
|
|
79
|
+
*
|
|
80
|
+
* Two refusals, matching the agent's `manage-levels delete` so the two lanes cannot disagree:
|
|
81
|
+
* the last level, and the START level. The second matters most — `startLevelId` naming nothing
|
|
82
|
+
* falls back to `levels[0]` at boot while the mirrors still describe the deleted level, which is
|
|
83
|
+
* the silent stale-mirror failure. Move the start first, deliberately.
|
|
84
|
+
*
|
|
85
|
+
* The `.vwld` asset is left alone: it may be large, nothing here deletes uploaded bytes, and a
|
|
86
|
+
* re-add is then free.
|
|
87
|
+
*/
|
|
88
|
+
export declare function planRemove(world: JsonObject, levelId: string): RemovePlan;
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bitmagic levels` — the level registry in `worldProfileData.levels`, without the web Creator.
|
|
3
|
+
*
|
|
4
|
+
* `forge` can CREATE levels but never could manage them: only a game's first forge becomes the
|
|
5
|
+
* start level (or `forge --make-start` at creation), so fixing a wrong start, renaming, or deleting
|
|
6
|
+
* a level meant opening the Creator.
|
|
7
|
+
*
|
|
8
|
+
* ── The invariant this file exists to respect ────────────────────────────────────────────────
|
|
9
|
+
*
|
|
10
|
+
* `worldProfileData` keeps three LEGACY MIRRORS — `voxelUrl`, `spawnPoints` and
|
|
11
|
+
* `playerSpawnPosition` — describing the START level, because frozen genre and work-template code
|
|
12
|
+
* boots from those rather than from the registry (see `WorldLevel` in game/src/types/game.ts).
|
|
13
|
+
*
|
|
14
|
+
* Nothing self-heals on the normal boot path. `applyBootLevelOverride` does recompute them, but
|
|
15
|
+
* only when the main screen's level chooser passes a `bootLevelId`. So a `startLevelId` moved
|
|
16
|
+
* without its mirrors boots the NEW level's objects, lighting and navmesh over the OLD level's
|
|
17
|
+
* terrain, and nothing throws. That is the failure this module exists to prevent.
|
|
18
|
+
*
|
|
19
|
+
* The mirrors come from `buildStartLevelMirrors` in `@bitmagic/world-forger`, deliberately rather
|
|
20
|
+
* than reimplemented — the same function the forge uses, carrying a subtlety worth not
|
|
21
|
+
* rediscovering: spawn mirrors are written ONLY when the level owns spawn points, because a level
|
|
22
|
+
* without its own inherits the globals, which are then already correct.
|
|
23
|
+
*
|
|
24
|
+
* Pure: it computes modifications and never applies them.
|
|
25
|
+
*/
|
|
26
|
+
import { buildConversionModifications, buildStartLevelMirrors, getLevels, getStartLevel, mintLevelId, } from '@bitmagic/world-forger/pipeline/index.js';
|
|
27
|
+
import { CliError } from '../errors.js';
|
|
28
|
+
import { isJsonObject } from '../project/world-json.js';
|
|
29
|
+
/** The same document, seen through the shared helpers' narrower view. */
|
|
30
|
+
function asWorldJson(world) {
|
|
31
|
+
return world;
|
|
32
|
+
}
|
|
33
|
+
function assetsOf(world) {
|
|
34
|
+
return (Array.isArray(world.assets) ? world.assets : []).filter(isJsonObject);
|
|
35
|
+
}
|
|
36
|
+
export function listLevels(world) {
|
|
37
|
+
const start = getStartLevel(asWorldJson(world));
|
|
38
|
+
const assets = assetsOf(world);
|
|
39
|
+
const objects = (Array.isArray(world.environmentObjects) ? world.environmentObjects : [])
|
|
40
|
+
.filter(isJsonObject);
|
|
41
|
+
return getLevels(asWorldJson(world)).map((level) => ({
|
|
42
|
+
id: level.id,
|
|
43
|
+
name: level.name,
|
|
44
|
+
vwldAssetId: level.vwldAssetId,
|
|
45
|
+
missingAsset: !assets.some((asset) => asset.id === level.vwldAssetId),
|
|
46
|
+
start: start !== null && start.id === level.id,
|
|
47
|
+
spawnPoints: level.spawnPoints?.length ?? 0,
|
|
48
|
+
placed: objects.filter((object) => object.levelId === level.id).length,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
function requireLevel(world, levelId) {
|
|
52
|
+
const levels = getLevels(asWorldJson(world));
|
|
53
|
+
const level = levels.find((entry) => entry.id === levelId);
|
|
54
|
+
if (level === undefined) {
|
|
55
|
+
throw new CliError(`No level with id "${levelId}" in this project. `
|
|
56
|
+
+ (levels.length > 0
|
|
57
|
+
? `This game has: ${levels.map((entry) => `${entry.id} ("${entry.name}")`).join(', ')}. `
|
|
58
|
+
: 'This game has no levels — `bitmagic forge` creates one. ')
|
|
59
|
+
+ 'world.json is unchanged.');
|
|
60
|
+
}
|
|
61
|
+
return level;
|
|
62
|
+
}
|
|
63
|
+
/** The url a level's `.vwld` asset serves, or null when it is missing or not a level bake. */
|
|
64
|
+
function vwldUrlFor(world, level) {
|
|
65
|
+
const asset = assetsOf(world).find((item) => item.id === level.vwldAssetId);
|
|
66
|
+
if (asset === undefined || asset.type !== 'vwld')
|
|
67
|
+
return null;
|
|
68
|
+
return typeof asset.url === 'string' && asset.url !== '' ? asset.url : null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Make `levelId` the level the game boots into — registry AND mirrors, together.
|
|
72
|
+
*
|
|
73
|
+
* Refused when the level's `.vwld` asset is missing or has no url: the mirrors are what actually
|
|
74
|
+
* boot the game, so writing `startLevelId` alone would leave `voxelUrl` describing the previous
|
|
75
|
+
* level, and the registry and the thing that loads would disagree with no error anywhere.
|
|
76
|
+
*/
|
|
77
|
+
export function planSetStart(world, levelId) {
|
|
78
|
+
const level = requireLevel(world, levelId);
|
|
79
|
+
const current = getStartLevel(asWorldJson(world));
|
|
80
|
+
if (current !== null && current.id === levelId) {
|
|
81
|
+
throw new CliError(`"${level.name}" is already the start level. world.json is unchanged.`);
|
|
82
|
+
}
|
|
83
|
+
const vwldUrl = vwldUrlFor(world, level);
|
|
84
|
+
if (vwldUrl === null) {
|
|
85
|
+
throw new CliError(`Level "${level.name}" points at asset "${level.vwldAssetId}", which this project has no `
|
|
86
|
+
+ 'loadable .vwld for — so it has nothing to boot. Fix the level first (`bitmagic assets '
|
|
87
|
+
+ 'list` shows what is there). world.json is unchanged.');
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
modifications: [
|
|
91
|
+
{ type: 'set', path: ['startLevelId'], value: levelId },
|
|
92
|
+
// Without these the game boots the OLD level's terrain. See the file header.
|
|
93
|
+
...buildStartLevelMirrors(level, vwldUrl),
|
|
94
|
+
],
|
|
95
|
+
notes: (level.spawnPoints?.length ?? 0) === 0
|
|
96
|
+
? [`"${level.name}" has no spawn points of its own, so it keeps the game's global ones.`]
|
|
97
|
+
: [],
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function planRename(world, levelId, name) {
|
|
101
|
+
const level = requireLevel(world, levelId);
|
|
102
|
+
const trimmed = name.trim();
|
|
103
|
+
if (trimmed === '')
|
|
104
|
+
throw new CliError('A level name cannot be empty. world.json is unchanged.');
|
|
105
|
+
if (trimmed === level.name) {
|
|
106
|
+
throw new CliError(`"${level.name}" already has that name. world.json is unchanged.`);
|
|
107
|
+
}
|
|
108
|
+
const clash = getLevels(asWorldJson(world)).some((e) => e.id !== levelId && e.name === trimmed);
|
|
109
|
+
return {
|
|
110
|
+
modifications: [{
|
|
111
|
+
type: 'update',
|
|
112
|
+
path: ['levels'],
|
|
113
|
+
predicate: (item) => isJsonObject(item) && item.id === levelId,
|
|
114
|
+
// An updater, not a `{ ...level, name }` snapshot (which is what the agent's tool builds):
|
|
115
|
+
// the snapshot was read before the write and reverts anything another writer changed on this
|
|
116
|
+
// level in between. Same reason editor/save.ts gives for its own level write.
|
|
117
|
+
value: (item) => ({ ...(isJsonObject(item) ? item : {}), name: trimmed }),
|
|
118
|
+
}],
|
|
119
|
+
notes: clash
|
|
120
|
+
? [`Another level is also called "${trimmed}" — names are for humans, ids are what code uses.`]
|
|
121
|
+
: [],
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Register an existing `.vwld` asset as a level.
|
|
126
|
+
*
|
|
127
|
+
* Start-level policy matches `forge`'s: a game's FIRST level becomes the start, later ones do not,
|
|
128
|
+
* and `--make-start` overrides. Stealing the start from a working game because someone registered
|
|
129
|
+
* a second level would be the surprising outcome.
|
|
130
|
+
*/
|
|
131
|
+
export function planAdd(world, vwldAssetId, options) {
|
|
132
|
+
const asset = assetsOf(world).find((item) => item.id === vwldAssetId);
|
|
133
|
+
if (asset === undefined) {
|
|
134
|
+
const available = assetsOf(world).filter((item) => item.type === 'vwld');
|
|
135
|
+
throw new CliError(`No asset with id "${vwldAssetId}" in this project. `
|
|
136
|
+
+ (available.length > 0
|
|
137
|
+
? `Baked levels here: ${available.map((a) => `${String(a.id)} ("${String(a.name)}")`).join(', ')}. `
|
|
138
|
+
: 'This project has no baked levels — `bitmagic forge` makes one. ')
|
|
139
|
+
+ 'world.json is unchanged.');
|
|
140
|
+
}
|
|
141
|
+
if (asset.type !== 'vwld') {
|
|
142
|
+
throw new CliError(`Asset "${vwldAssetId}" is ${typeof asset.type === 'string' ? `a ${asset.type}` : 'an unknown'} `
|
|
143
|
+
+ 'asset, not a baked level (.vwld). Only a level bake can become a level. '
|
|
144
|
+
+ 'world.json is unchanged.');
|
|
145
|
+
}
|
|
146
|
+
const url = typeof asset.url === 'string' ? asset.url : '';
|
|
147
|
+
if (url === '') {
|
|
148
|
+
throw new CliError(`Asset "${vwldAssetId}" has no url, so a level made from it would have nothing to load. `
|
|
149
|
+
+ 'world.json is unchanged.');
|
|
150
|
+
}
|
|
151
|
+
// Two levels on one bake is the same place twice, and the forge's re-bake path resolves a level
|
|
152
|
+
// by `vwldAssetId` — with duplicates it picks the first arbitrarily.
|
|
153
|
+
const sharing = getLevels(asWorldJson(world)).find((entry) => entry.vwldAssetId === vwldAssetId);
|
|
154
|
+
if (sharing !== undefined) {
|
|
155
|
+
throw new CliError(`Level "${sharing.name}" (${sharing.id}) already uses that bake. world.json is unchanged.`);
|
|
156
|
+
}
|
|
157
|
+
const assetName = typeof asset.name === 'string' && asset.name !== '' ? asset.name : vwldAssetId;
|
|
158
|
+
const level = {
|
|
159
|
+
id: mintLevelId(),
|
|
160
|
+
name: (options.name ?? assetName).trim() || assetName,
|
|
161
|
+
vwldAssetId,
|
|
162
|
+
};
|
|
163
|
+
const existing = getLevels(asWorldJson(world));
|
|
164
|
+
if (existing.length > 0) {
|
|
165
|
+
const makeStart = options.makeStart;
|
|
166
|
+
return {
|
|
167
|
+
level,
|
|
168
|
+
converted: false,
|
|
169
|
+
modifications: [
|
|
170
|
+
{ type: 'push', path: ['levels'], value: level },
|
|
171
|
+
...(makeStart
|
|
172
|
+
? [
|
|
173
|
+
{ type: 'set', path: ['startLevelId'], value: level.id },
|
|
174
|
+
...buildStartLevelMirrors(level, url),
|
|
175
|
+
]
|
|
176
|
+
: []),
|
|
177
|
+
],
|
|
178
|
+
notes: makeStart
|
|
179
|
+
? [`"${level.name}" is now the start level.`]
|
|
180
|
+
: ['The start level is unchanged — pass --make-start, or use `bitmagic levels set-start`.'],
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// No registry yet. If the game already HAS a world (a `voxelUrl` naming a registered bake), that
|
|
184
|
+
// world must become a level too — otherwise turning on multi-level would strand the place the
|
|
185
|
+
// game currently loads, with no entry naming it.
|
|
186
|
+
const conversion = safeConversion(world);
|
|
187
|
+
if (conversion !== null) {
|
|
188
|
+
return {
|
|
189
|
+
level,
|
|
190
|
+
converted: true,
|
|
191
|
+
modifications: [
|
|
192
|
+
// Everything the conversion does EXCEPT its own `levels` write, which named only level 1.
|
|
193
|
+
...conversion.modifications.filter((mod) => !('path' in mod) || mod.path[0] !== 'levels'),
|
|
194
|
+
{ type: 'set', path: ['levels'], value: [conversion.level1, level] },
|
|
195
|
+
],
|
|
196
|
+
notes: [
|
|
197
|
+
`This game's existing world became level "${conversion.level1.name}" and remains the start `
|
|
198
|
+
+ 'level; every object that was not already tagged now belongs to it.',
|
|
199
|
+
],
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// A procedural game with no world of its own: the new level is the only one, so it must boot.
|
|
203
|
+
return {
|
|
204
|
+
level,
|
|
205
|
+
converted: false,
|
|
206
|
+
modifications: [
|
|
207
|
+
{ type: 'set', path: ['levels'], value: [level] },
|
|
208
|
+
{ type: 'set', path: ['startLevelId'], value: level.id },
|
|
209
|
+
...buildStartLevelMirrors(level, url),
|
|
210
|
+
],
|
|
211
|
+
notes: [`This is the game's first level, so it is the start level.`],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The legacy→levels conversion, or null when there is nothing to convert.
|
|
216
|
+
*
|
|
217
|
+
* Throws inside the shared helper when the current world is not a registered bake (a legacy map or
|
|
218
|
+
* an unregistered one); that is a refusal for the caller, not a crash.
|
|
219
|
+
*/
|
|
220
|
+
function safeConversion(world) {
|
|
221
|
+
try {
|
|
222
|
+
return buildConversionModifications(asWorldJson(world));
|
|
223
|
+
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
throw new CliError('This game\'s current world is not a registered level bake, so it cannot be turned into a '
|
|
226
|
+
+ `level alongside the new one: ${error instanceof Error ? error.message : String(error)} `
|
|
227
|
+
+ 'Re-bake it with `bitmagic forge` first. world.json is unchanged.');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Remove a level from the registry.
|
|
232
|
+
*
|
|
233
|
+
* Two refusals, matching the agent's `manage-levels delete` so the two lanes cannot disagree:
|
|
234
|
+
* the last level, and the START level. The second matters most — `startLevelId` naming nothing
|
|
235
|
+
* falls back to `levels[0]` at boot while the mirrors still describe the deleted level, which is
|
|
236
|
+
* the silent stale-mirror failure. Move the start first, deliberately.
|
|
237
|
+
*
|
|
238
|
+
* The `.vwld` asset is left alone: it may be large, nothing here deletes uploaded bytes, and a
|
|
239
|
+
* re-add is then free.
|
|
240
|
+
*/
|
|
241
|
+
export function planRemove(world, levelId) {
|
|
242
|
+
const level = requireLevel(world, levelId);
|
|
243
|
+
const levels = getLevels(asWorldJson(world));
|
|
244
|
+
if (levels.length <= 1) {
|
|
245
|
+
throw new CliError('This is the game\'s only level, and a levels-mode game needs one. world.json is unchanged.');
|
|
246
|
+
}
|
|
247
|
+
const start = getStartLevel(asWorldJson(world));
|
|
248
|
+
if (start !== null && start.id === levelId) {
|
|
249
|
+
const other = levels.find((entry) => entry.id !== levelId);
|
|
250
|
+
throw new CliError(`"${level.name}" is the start level. Removing it would leave the game booting one level's `
|
|
251
|
+
+ 'terrain under another\'s objects. Move the start first:\n'
|
|
252
|
+
+ ` bitmagic levels set-start ${other?.id ?? '<levelId>'}\n`
|
|
253
|
+
+ 'world.json is unchanged.');
|
|
254
|
+
}
|
|
255
|
+
const profile = isJsonObject(world.worldProfileData) ? world.worldProfileData : {};
|
|
256
|
+
const objects = (Array.isArray(world.environmentObjects) ? world.environmentObjects : [])
|
|
257
|
+
.filter(isJsonObject);
|
|
258
|
+
const placedRemoved = objects.filter((object) => object.levelId === levelId).length;
|
|
259
|
+
const modifications = [
|
|
260
|
+
{
|
|
261
|
+
type: 'remove',
|
|
262
|
+
path: ['levels'],
|
|
263
|
+
predicate: (item) => isJsonObject(item) && item.id === levelId,
|
|
264
|
+
},
|
|
265
|
+
// Instances belonging to this level go with it. Left behind they would load in no level at
|
|
266
|
+
// all — invisible, and still counted as references that pin their assets against a purge.
|
|
267
|
+
{
|
|
268
|
+
type: 'removeRoot',
|
|
269
|
+
path: ['environmentObjects'],
|
|
270
|
+
predicate: (item) => isJsonObject(item) && item.levelId === levelId,
|
|
271
|
+
},
|
|
272
|
+
];
|
|
273
|
+
// Doors and key items are level-scoped the same way, and the agent's tool leaves them — which
|
|
274
|
+
// makes them permanent dead weight: they never spawn, yet `persist-level` still counts their
|
|
275
|
+
// assetIds as referenced, so the placeholder assets can never be purged either.
|
|
276
|
+
const scopedRemoved = [];
|
|
277
|
+
for (const key of ['doors', 'keyItems']) {
|
|
278
|
+
const entries = Array.isArray(profile[key]) ? profile[key].filter(isJsonObject) : [];
|
|
279
|
+
const doomed = entries.filter((entry) => entry.levelId === levelId).length;
|
|
280
|
+
if (doomed === 0)
|
|
281
|
+
continue;
|
|
282
|
+
scopedRemoved.push(`${doomed} ${key}`);
|
|
283
|
+
modifications.push({
|
|
284
|
+
type: 'remove',
|
|
285
|
+
path: [key],
|
|
286
|
+
predicate: (item) => isJsonObject(item) && item.levelId === levelId,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
const notes = [];
|
|
290
|
+
if (placedRemoved > 0) {
|
|
291
|
+
notes.push(`${placedRemoved} placed object(s) belonged to it and were removed with it.`);
|
|
292
|
+
}
|
|
293
|
+
if (scopedRemoved.length > 0)
|
|
294
|
+
notes.push(`Also removed: ${scopedRemoved.join(', ')}.`);
|
|
295
|
+
// The start-screen chooser filters by id and is hand-authored; it warns and skips unknown ids on
|
|
296
|
+
// its own, so this is reported rather than rewritten.
|
|
297
|
+
if (choosersNaming(profile, levelId) > 0) {
|
|
298
|
+
notes.push('The main screen\'s level chooser still lists this level; it will skip it, but tidy '
|
|
299
|
+
+ '`hud.startScreen.selections[].levelIds` when you get a chance.');
|
|
300
|
+
}
|
|
301
|
+
return { level, modifications, notes, placedRemoved, scopedRemoved };
|
|
302
|
+
}
|
|
303
|
+
function choosersNaming(profile, levelId) {
|
|
304
|
+
const hud = isJsonObject(profile.hud) ? profile.hud : {};
|
|
305
|
+
const startScreen = isJsonObject(hud.startScreen) ? hud.startScreen : {};
|
|
306
|
+
const selections = Array.isArray(startScreen.selections) ? startScreen.selections : [];
|
|
307
|
+
return selections.filter((step) => isJsonObject(step) && Array.isArray(step.levelIds) && step.levelIds.includes(levelId)).length;
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/levels/registry.ts"],"names":[],"mappings":"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"}
|