@bitmagic/cli 0.1.52-dev.6 → 0.1.52-dev.8
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 +40 -0
- package/dist/assets/add.js +14 -12
- package/dist/assets/add.js.map +1 -1
- package/dist/assets/file-kind.d.ts +8 -0
- package/dist/assets/file-kind.js +17 -2
- package/dist/assets/file-kind.js.map +1 -1
- package/dist/assets/vxl-summary.d.ts +9 -0
- package/dist/assets/vxl-summary.js +15 -0
- package/dist/assets/vxl-summary.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/assets.d.ts +3 -10
- package/dist/commands/assets.js +11 -18
- package/dist/commands/assets.js.map +1 -1
- package/dist/commands/cover.js +2 -3
- package/dist/commands/cover.js.map +1 -1
- package/dist/commands/forge.d.ts +2 -9
- package/dist/commands/forge.js +2 -13
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/generate.d.ts +3 -10
- package/dist/commands/generate.js +3 -36
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/judge.d.ts +2 -7
- package/dist/commands/judge.js +2 -3
- package/dist/commands/judge.js.map +1 -1
- 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/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/editor/journal.d.ts +13 -0
- package/dist/editor/journal.js +2 -0
- package/dist/editor/journal.js.map +1 -1
- package/dist/editor/server.js +72 -91
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.js +119 -114
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/editor/voxel-save.js +8 -12
- package/dist/editor/voxel-save.js.map +1 -1
- 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 +9 -0
- package/dist/forge/apply-modifications.js +12 -18
- package/dist/forge/apply-modifications.js.map +1 -1
- package/dist/forge/voxelize-session.d.ts +37 -0
- package/dist/forge/voxelize-session.js +2 -2
- package/dist/forge/voxelize-session.js.map +1 -1
- 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/scaffold/project-files.js +3 -1
- package/dist/scaffold/project-files.js.map +1 -1
- package/package.json +4 -4
|
@@ -3,6 +3,15 @@ export interface ApplyModificationsOutcome {
|
|
|
3
3
|
applied: number;
|
|
4
4
|
/** One human-readable line per modification, for the command to print. */
|
|
5
5
|
summary: string[];
|
|
6
|
+
/**
|
|
7
|
+
* How many entries each modification actually changed, in the same order.
|
|
8
|
+
*
|
|
9
|
+
* Reported from the write itself rather than counted off an earlier read. A caller that counts
|
|
10
|
+
* beforehand — "removing this asset will take 3 placed instances with it" — is describing a
|
|
11
|
+
* document that may not be the one written: `bitmagic dev`'s whole premise is that the creator's
|
|
12
|
+
* agent edits world.json concurrently, and the applier re-reads it under its own atomic write.
|
|
13
|
+
*/
|
|
14
|
+
changed: number[];
|
|
6
15
|
}
|
|
7
16
|
/**
|
|
8
17
|
* Read world.json, apply every modification in order, write once.
|
|
@@ -47,13 +47,6 @@ function ensureArray(parent, key) {
|
|
|
47
47
|
}
|
|
48
48
|
return parent[key];
|
|
49
49
|
}
|
|
50
|
-
/**
|
|
51
|
-
* Apply one modification in place, returning the path it touched for the summary line.
|
|
52
|
-
*
|
|
53
|
-
* `*Root` types address the document root; the rest address paths inside `worldProfileData`.
|
|
54
|
-
* Writing a `set` at the root instead would put it where the engine never looks, which reads as
|
|
55
|
-
* success — the same trap `generate/apply-patches.ts` documents for `WorldPatch`.
|
|
56
|
-
*/
|
|
57
50
|
function applyOne(world, modification) {
|
|
58
51
|
const isRoot = modification.type.endsWith('Root');
|
|
59
52
|
let target;
|
|
@@ -85,35 +78,35 @@ function applyOne(world, modification) {
|
|
|
85
78
|
case 'set':
|
|
86
79
|
case 'setRoot':
|
|
87
80
|
parent[lastKey] = modification.value;
|
|
88
|
-
return `set ${where}
|
|
81
|
+
return { summary: `set ${where}`, changed: 1 };
|
|
89
82
|
case 'push':
|
|
90
83
|
case 'pushRoot':
|
|
91
84
|
ensureArray(parent, lastKey).push(modification.value);
|
|
92
|
-
return `added to ${where}
|
|
85
|
+
return { summary: `added to ${where}`, changed: 1 };
|
|
93
86
|
case 'update':
|
|
94
87
|
case 'updateRoot': {
|
|
95
88
|
const { predicate, value } = modification;
|
|
96
89
|
const array = parent[lastKey];
|
|
97
90
|
if (!Array.isArray(array))
|
|
98
|
-
return `skipped ${where} (not an array)
|
|
91
|
+
return { summary: `skipped ${where} (not an array)`, changed: 0 };
|
|
99
92
|
const index = array.findIndex(predicate);
|
|
100
93
|
if (index === -1)
|
|
101
|
-
return `no match to update in ${where}
|
|
94
|
+
return { summary: `no match to update in ${where}`, changed: 0 };
|
|
102
95
|
array[index] = typeof value === 'function'
|
|
103
96
|
? value(array[index])
|
|
104
97
|
: value;
|
|
105
|
-
return `updated ${where}[${index}]
|
|
98
|
+
return { summary: `updated ${where}[${index}]`, changed: 1 };
|
|
106
99
|
}
|
|
107
100
|
case 'remove':
|
|
108
101
|
case 'removeRoot': {
|
|
109
102
|
const { predicate } = modification;
|
|
110
103
|
const array = parent[lastKey];
|
|
111
104
|
if (!Array.isArray(array))
|
|
112
|
-
return `skipped ${where} (not an array)
|
|
105
|
+
return { summary: `skipped ${where} (not an array)`, changed: 0 };
|
|
113
106
|
const kept = array.filter((item) => !predicate(item));
|
|
114
107
|
const removed = array.length - kept.length;
|
|
115
108
|
parent[lastKey] = kept;
|
|
116
|
-
return `removed ${removed} from ${where}
|
|
109
|
+
return { summary: `removed ${removed} from ${where}`, changed: removed };
|
|
117
110
|
}
|
|
118
111
|
case 'upsert':
|
|
119
112
|
case 'upsertRoot': {
|
|
@@ -122,10 +115,10 @@ function applyOne(world, modification) {
|
|
|
122
115
|
const index = array.findIndex(predicate);
|
|
123
116
|
if (index === -1) {
|
|
124
117
|
array.push(value);
|
|
125
|
-
return `added to ${where}
|
|
118
|
+
return { summary: `added to ${where}`, changed: 1 };
|
|
126
119
|
}
|
|
127
120
|
array[index] = value;
|
|
128
|
-
return `updated ${where}[${index}]
|
|
121
|
+
return { summary: `updated ${where}[${index}]`, changed: 1 };
|
|
129
122
|
}
|
|
130
123
|
default: {
|
|
131
124
|
// Not a degradation to swallow: an unknown type means this applier and the shared package's
|
|
@@ -144,7 +137,8 @@ export function applyModificationsToWorld(worldPath, modifications, options = {}
|
|
|
144
137
|
// Baseline BEFORE applying: a violation already present in a legacy or imported world must not
|
|
145
138
|
// block an unrelated write. Only violations this batch INTRODUCES are refused.
|
|
146
139
|
const preExisting = new Set(validateWorldData(world));
|
|
147
|
-
const
|
|
140
|
+
const results = modifications.map((modification) => applyOne(world, modification));
|
|
141
|
+
const summary = results.map((result) => result.summary);
|
|
148
142
|
// The engine statically casts world.json to Partial<GameData> (game/src/bundle/WorldDataLoader.ts),
|
|
149
143
|
// so a non-conforming document breaks `tsc` in a file the creator cannot edit. The gate matters
|
|
150
144
|
// more in this lane than in the web one: by the time it runs the design and geometry are already
|
|
@@ -159,6 +153,6 @@ export function applyModificationsToWorld(worldPath, modifications, options = {}
|
|
|
159
153
|
+ `schema, so nothing was written. Violations: ${introduced.join('; ')}`);
|
|
160
154
|
}
|
|
161
155
|
writeWorldJsonAtomic(worldPath, world);
|
|
162
|
-
return { applied: modifications.length, summary };
|
|
156
|
+
return { applied: modifications.length, summary, changed: results.map((result) => result.changed) };
|
|
163
157
|
}
|
|
164
158
|
//# sourceMappingURL=apply-modifications.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply-modifications.js","sourceRoot":"","sources":["../../src/forge/apply-modifications.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAmB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"apply-modifications.js","sourceRoot":"","sources":["../../src/forge/apply-modifications.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAE7B,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,uDAAuD,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAmB,MAAM,0BAA0B,CAAC;AAiB9G,+FAA+F;AAC/F,SAAS,eAAe,CAAC,IAAgB,EAAE,QAAkB;IAC3D,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAC9B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACxB,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,OAAO,CAAe,CAAC;IAC3C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,WAAW,CAAC,MAAkB,EAAE,GAAW;IAClD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IACnB,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAc,CAAC;AAClC,CAAC;AAeD,SAAS,QAAQ,CAAC,KAAiB,EAAE,YAAmC;IACtE,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAElD,IAAI,MAAkB,CAAC;IACvB,IAAI,MAAc,CAAC;IACnB,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,GAAG,KAAK,CAAC;QACf,MAAM,GAAG,EAAE,CAAC;IACd,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC1C,KAAK,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC9B,CAAC;QACD,MAAM,GAAG,KAAK,CAAC,gBAA8B,CAAC;QAC9C,MAAM,GAAG,mBAAmB,CAAC;IAC/B,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC;IACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,QAAQ,CAAC,kEAAkE,CAAC,CAAC;IACzF,CAAC;IACD,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,KAAK,GAAG,GAAG,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;IAE/C,wFAAwF;IACxF,+FAA+F;IAC/F,4FAA4F;IAC5F,8FAA8F;IAC9F,iFAAiF;IACjF,QAAQ,YAAY,CAAC,IAAI,EAAE,CAAC;QAC1B,KAAK,KAAK,CAAC;QACX,KAAK,SAAS;YACZ,MAAM,CAAC,OAAO,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC;YACrC,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAEjD,KAAK,MAAM,CAAC;QACZ,KAAK,UAAU;YACb,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACtD,OAAO,EAAE,OAAO,EAAE,YAAY,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAEtD,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC;YAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,EAAE,OAAO,EAAE,WAAW,KAAK,iBAAiB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;YAC7F,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACzC,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,OAAO,EAAE,OAAO,EAAE,yBAAyB,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;YACnF,KAAK,CAAC,KAAK,CAAC,GAAG,OAAO,KAAK,KAAK,UAAU;gBACxC,CAAC,CAAE,KAAoC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACrD,CAAC,CAAC,KAAK,CAAC;YACV,OAAO,EAAE,OAAO,EAAE,WAAW,KAAK,IAAI,KAAK,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC/D,CAAC;QAED,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,EAAE,SAAS,EAAE,GAAG,YAAY,CAAC;YACnC,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,EAAE,OAAO,EAAE,WAAW,KAAK,iBAAiB,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;YAC7F,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YACtD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;YAC3C,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;YACvB,OAAO,EAAE,OAAO,EAAE,WAAW,OAAO,SAAS,KAAK,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QAC3E,CAAC;QAED,KAAK,QAAQ,CAAC;QACd,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC;YAC1C,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;YACzC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAClB,OAAO,EAAE,OAAO,EAAE,YAAY,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;YACtD,CAAC;YACD,KAAK,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;YACrB,OAAO,EAAE,OAAO,EAAE,WAAW,KAAK,IAAI,KAAK,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAC/D,CAAC;QAED,OAAO,CAAC,CAAC,CAAC;YACR,4FAA4F;YAC5F,wFAAwF;YACxF,yFAAyF;YACzF,8BAA8B;YAC9B,MAAM,SAAS,GAAU,YAAY,CAAC;YACtC,MAAM,IAAI,QAAQ,CAChB,sEAAsE;kBACpE,IAAK,SAAmC,CAAC,IAAI,IAAI,CACpD,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAuBD,MAAM,UAAU,yBAAyB,CACvC,SAAiB,EACjB,aAAsC,EACtC,UAAqC,EAAE;IAEvC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,kBAAkB,CAAC;IACtD,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAEvC,+FAA+F;IAC/F,+EAA+E;IAC/E,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;IAEtD,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC;IACnF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAExD,oGAAoG;IACpG,gGAAgG;IAChG,iGAAiG;IACjG,+FAA+F;IAC/F,mEAAmE;IACnE,EAAE;IACF,iGAAiG;IACjG,+CAA+C;IAC/C,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;IACvF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,QAAQ,CAChB,GAAG,OAAO,eAAe,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,yCAAyC;cACxF,+CAA+C,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzE,CAAC;IACJ,CAAC;IAED,oBAAoB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAEvC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;AACtG,CAAC"}
|
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One headless engine, ready to voxelize — the arrangement `bitmagic generate prop`, `generate
|
|
3
|
+
* vehicle` and `assets add <file.glb>` all need and none should own.
|
|
4
|
+
*
|
|
5
|
+
* Only the engine can voxelize, so each of those runs the project's own game in a headless Chrome
|
|
6
|
+
* and talks to it over the forge transport. Getting there is five steps in a fixed order, and the
|
|
7
|
+
* teardown has to run in the reverse order whatever happened:
|
|
8
|
+
*
|
|
9
|
+
* 1. reserve a port in 3000–3199 — the only origins the asset CDN allows, so the page can fetch
|
|
10
|
+
* the project's assets and the GLB it is about to bake;
|
|
11
|
+
* 2. spawn the project's vendored `vite` on it, and wait for it;
|
|
12
|
+
* 3. start the upload proxy — the engine uploads the baked `.vxl` itself, to a presigned URL it
|
|
13
|
+
* asks for at a hardcoded path with no credentials, and the proxy turns that into the
|
|
14
|
+
* authenticated api-server route while keeping the token out of the page
|
|
15
|
+
* (see `upload-proxy.ts`);
|
|
16
|
+
* 4. launch Chrome on the page and wait for the game to load;
|
|
17
|
+
* 5. run the caller's body against `host.transport`, then close the page (it may be mid-upload),
|
|
18
|
+
* then the proxy it was uploading through, then the server underneath both.
|
|
19
|
+
*
|
|
20
|
+
* Three commands carrying three copies of this is how a fix to one of them fails to reach the
|
|
21
|
+
* other two, so it lives here once. The two setup failures a creator can actually hit — no free
|
|
22
|
+
* port, vite not coming up — are reported with the caller's own `retryHint`, because what is kept
|
|
23
|
+
* and how to retry differs per command (a paid mesh, a paid vehicle design, an already-uploaded
|
|
24
|
+
* file) and that sentence is the useful part of the message.
|
|
25
|
+
*/
|
|
26
|
+
import { type ChildProcess } from 'child_process';
|
|
1
27
|
import type { Environment } from '../config/environments.js';
|
|
2
28
|
import { ForgeBrowserHost } from './browser-host.js';
|
|
3
29
|
import { type ProjectContext } from '../project/context.js';
|
|
@@ -22,3 +48,14 @@ export interface VoxelizeSession {
|
|
|
22
48
|
port: number;
|
|
23
49
|
}
|
|
24
50
|
export declare function withVoxelizeSession<T>(options: VoxelizeSessionOptions, body: (session: VoxelizeSession) => Promise<T>): Promise<T>;
|
|
51
|
+
/**
|
|
52
|
+
* Run one teardown step, reporting a failure rather than propagating it. Cleanup must never mask
|
|
53
|
+
* the error that caused it, and must never stop the steps that follow.
|
|
54
|
+
*/
|
|
55
|
+
export declare function closeQuietly(what: string, close: () => Promise<void> | undefined, log: (message: string) => void): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Stop vite, then stop waiting for it. `unref` matters as much as the signal: the child was
|
|
58
|
+
* spawned with a piped stderr, so its handle keeps the parent's event loop alive and a vite that
|
|
59
|
+
* ignores SIGTERM left the CLI hanging after it had already printed its result.
|
|
60
|
+
*/
|
|
61
|
+
export declare function killQuietly(server: ChildProcess, log: (message: string) => void): void;
|
|
@@ -111,7 +111,7 @@ export async function withVoxelizeSession(options, body) {
|
|
|
111
111
|
* Run one teardown step, reporting a failure rather than propagating it. Cleanup must never mask
|
|
112
112
|
* the error that caused it, and must never stop the steps that follow.
|
|
113
113
|
*/
|
|
114
|
-
async function closeQuietly(what, close, log) {
|
|
114
|
+
export async function closeQuietly(what, close, log) {
|
|
115
115
|
try {
|
|
116
116
|
await close();
|
|
117
117
|
}
|
|
@@ -124,7 +124,7 @@ async function closeQuietly(what, close, log) {
|
|
|
124
124
|
* spawned with a piped stderr, so its handle keeps the parent's event loop alive and a vite that
|
|
125
125
|
* ignores SIGTERM left the CLI hanging after it had already printed its result.
|
|
126
126
|
*/
|
|
127
|
-
function killQuietly(server, log) {
|
|
127
|
+
export function killQuietly(server, log) {
|
|
128
128
|
try {
|
|
129
129
|
server.kill('SIGTERM');
|
|
130
130
|
server.stderr?.removeAllListeners('data');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voxelize-session.js","sourceRoot":"","sources":["../../src/forge/voxelize-session.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"voxelize-session.js","sourceRoot":"","sources":["../../src/forge/voxelize-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -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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"}
|
|
@@ -854,7 +854,9 @@ bitmagic forge --prompt "a ruined desert temple with a central courtyard"
|
|
|
854
854
|
\`\`\`
|
|
855
855
|
|
|
856
856
|
This designs a scene, builds its geometry, bakes it to voxels and writes the finished level —
|
|
857
|
-
terrain, props, spawn points — into \`src/work/world.json\`.
|
|
857
|
+
terrain, props, spawn points — into \`src/work/world.json\`. \`bitmagic levels list | add | rename
|
|
858
|
+
| set-start | remove\` manages them afterwards (free, local); \`set-start\` is how you change which
|
|
859
|
+
level the game boots into, and it moves the legacy mirror fields with it. Add \`--city\`, \`--dungeon\`,
|
|
858
860
|
\`--platformer\` or \`--freeform\` (at most one) for a different kind of place; omit them all for
|
|
859
861
|
natural terrain.
|
|
860
862
|
|