@bitmagic/world-forger 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-types.d.ts +295 -0
- package/dist/artifact-types.d.ts.map +1 -0
- package/dist/artifact-types.js +17 -0
- package/dist/artifact-types.js.map +1 -0
- package/dist/forge-constants.d.ts +106 -0
- package/dist/forge-constants.d.ts.map +1 -0
- package/dist/forge-constants.js +111 -0
- package/dist/forge-constants.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/log.d.ts +26 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +14 -0
- package/dist/log.js.map +1 -0
- package/dist/pipeline/archetype-skips.d.ts +57 -0
- package/dist/pipeline/archetype-skips.d.ts.map +1 -0
- package/dist/pipeline/archetype-skips.js +51 -0
- package/dist/pipeline/archetype-skips.js.map +1 -0
- package/dist/pipeline/artifact-store.d.ts +4 -0
- package/dist/pipeline/artifact-store.d.ts.map +1 -0
- package/dist/pipeline/artifact-store.js +10 -0
- package/dist/pipeline/artifact-store.js.map +1 -0
- package/dist/pipeline/asset-catalog.d.ts +27 -0
- package/dist/pipeline/asset-catalog.d.ts.map +1 -0
- package/dist/pipeline/asset-catalog.js +47 -0
- package/dist/pipeline/asset-catalog.js.map +1 -0
- package/dist/pipeline/create-level-archetypes.d.ts +45 -0
- package/dist/pipeline/create-level-archetypes.d.ts.map +1 -0
- package/dist/pipeline/create-level-archetypes.js +197 -0
- package/dist/pipeline/create-level-archetypes.js.map +1 -0
- package/dist/pipeline/deps.d.ts +72 -0
- package/dist/pipeline/deps.d.ts.map +1 -0
- package/dist/pipeline/deps.js +2 -0
- package/dist/pipeline/deps.js.map +1 -0
- package/dist/pipeline/errors.d.ts +10 -0
- package/dist/pipeline/errors.d.ts.map +1 -0
- package/dist/pipeline/errors.js +13 -0
- package/dist/pipeline/errors.js.map +1 -0
- package/dist/pipeline/forged-work-store.d.ts +46 -0
- package/dist/pipeline/forged-work-store.d.ts.map +1 -0
- package/dist/pipeline/forged-work-store.js +69 -0
- package/dist/pipeline/forged-work-store.js.map +1 -0
- package/dist/pipeline/index.d.ts +36 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +36 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/job-types.d.ts +145 -0
- package/dist/pipeline/job-types.d.ts.map +1 -0
- package/dist/pipeline/job-types.js +2 -0
- package/dist/pipeline/job-types.js.map +1 -0
- package/dist/pipeline/level-registry.d.ts +57 -0
- package/dist/pipeline/level-registry.d.ts.map +1 -0
- package/dist/pipeline/level-registry.js +108 -0
- package/dist/pipeline/level-registry.js.map +1 -0
- package/dist/pipeline/model-types.d.ts +51 -0
- package/dist/pipeline/model-types.d.ts.map +1 -0
- package/dist/pipeline/model-types.js +2 -0
- package/dist/pipeline/model-types.js.map +1 -0
- package/dist/pipeline/persist-level.d.ts +82 -0
- package/dist/pipeline/persist-level.d.ts.map +1 -0
- package/dist/pipeline/persist-level.js +201 -0
- package/dist/pipeline/persist-level.js.map +1 -0
- package/dist/pipeline/place-and-persist-level.d.ts +83 -0
- package/dist/pipeline/place-and-persist-level.d.ts.map +1 -0
- package/dist/pipeline/place-and-persist-level.js +405 -0
- package/dist/pipeline/place-and-persist-level.js.map +1 -0
- package/dist/pipeline/request-id.d.ts +7 -0
- package/dist/pipeline/request-id.d.ts.map +1 -0
- package/dist/pipeline/request-id.js +10 -0
- package/dist/pipeline/request-id.js.map +1 -0
- package/dist/pipeline/spawn-points.d.ts +47 -0
- package/dist/pipeline/spawn-points.d.ts.map +1 -0
- package/dist/pipeline/spawn-points.js +100 -0
- package/dist/pipeline/spawn-points.js.map +1 -0
- package/dist/pipeline/starter-scatter.d.ts +34 -0
- package/dist/pipeline/starter-scatter.d.ts.map +1 -0
- package/dist/pipeline/starter-scatter.js +49 -0
- package/dist/pipeline/starter-scatter.js.map +1 -0
- package/dist/pipeline/storage-types.d.ts +48 -0
- package/dist/pipeline/storage-types.d.ts.map +1 -0
- package/dist/pipeline/storage-types.js +19 -0
- package/dist/pipeline/storage-types.js.map +1 -0
- package/dist/pipeline/transport-types.d.ts +35 -0
- package/dist/pipeline/transport-types.d.ts.map +1 -0
- package/dist/pipeline/transport-types.js +2 -0
- package/dist/pipeline/transport-types.js.map +1 -0
- package/dist/pipeline/voxelize-level.d.ts +24 -0
- package/dist/pipeline/voxelize-level.d.ts.map +1 -0
- package/dist/pipeline/voxelize-level.js +63 -0
- package/dist/pipeline/voxelize-level.js.map +1 -0
- package/dist/pipeline/world-json-source.d.ts +78 -0
- package/dist/pipeline/world-json-source.d.ts.map +1 -0
- package/dist/pipeline/world-json-source.js +2 -0
- package/dist/pipeline/world-json-source.js.map +1 -0
- package/dist/pipeline/world-json-types.d.ts +104 -0
- package/dist/pipeline/world-json-types.d.ts.map +1 -0
- package/dist/pipeline/world-json-types.js +15 -0
- package/dist/pipeline/world-json-types.js.map +1 -0
- package/dist/terrain-height-grid.d.ts +45 -0
- package/dist/terrain-height-grid.d.ts.map +1 -0
- package/dist/terrain-height-grid.js +50 -0
- package/dist/terrain-height-grid.js.map +1 -0
- package/package.json +72 -0
package/dist/log.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function emit(name, level) {
|
|
2
|
+
return (context, message) => {
|
|
3
|
+
console[level](`[${name}] ${message}`, context);
|
|
4
|
+
};
|
|
5
|
+
}
|
|
6
|
+
export function createLogger(name) {
|
|
7
|
+
return {
|
|
8
|
+
info: emit(name, 'info'),
|
|
9
|
+
warn: emit(name, 'warn'),
|
|
10
|
+
error: emit(name, 'error'),
|
|
11
|
+
debug: emit(name, 'debug'),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAyBA,SAAS,IAAI,CAAC,IAAY,EAAE,KAA0C;IACpE,OAAO,CAAC,OAAgC,EAAE,OAAe,EAAQ,EAAE;QACjE,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QACxB,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QACxB,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;QAC1B,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC;KAC3B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { ForgeArchetypeRef } from './job-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Recorded in `archetype-progress` / `archetype-assets` IN PLACE of the asset record
|
|
4
|
+
* of an archetype that could not be baked. It is a placeholder, not an asset: keeping
|
|
5
|
+
* the array INDEX-ALIGNED with `forge.archetypes` is what makes the resume cache (keyed
|
|
6
|
+
* by that index) and the summary's archetype↔asset pairing stay correct while the module
|
|
7
|
+
* itself disappears from the world. Every consumer must route it through
|
|
8
|
+
* {@link isSkippedArchetype} — it must never be written to world.json as an asset.
|
|
9
|
+
*
|
|
10
|
+
* A type alias rather than an interface so it is assignable to the `Record<string, unknown>`
|
|
11
|
+
* the artifacts are typed as.
|
|
12
|
+
*/
|
|
13
|
+
export type SkippedArchetype = {
|
|
14
|
+
skippedArchetype: true;
|
|
15
|
+
name: string;
|
|
16
|
+
/** Why the bake gave up — the browser's error text, or what the artifact lacked. */
|
|
17
|
+
reason: string;
|
|
18
|
+
/** Instances the level loses with it (all of them). */
|
|
19
|
+
instanceCount: number;
|
|
20
|
+
/**
|
|
21
|
+
* A used kit module (exact floor/wall/surround): its skip leaves bare recessed
|
|
22
|
+
* shell where carved stone belonged, so the summary note names that cost and a
|
|
23
|
+
* resume retries the bake. Absent on ordinary prop/landmark skips.
|
|
24
|
+
*/
|
|
25
|
+
structural?: true;
|
|
26
|
+
};
|
|
27
|
+
export declare function isSkippedArchetype(entry: Record<string, unknown> | null | undefined): entry is SkippedArchetype;
|
|
28
|
+
/**
|
|
29
|
+
* The forge-advisory line a skipped archetype contributes to the summary.
|
|
30
|
+
*/
|
|
31
|
+
export declare function skipNote(skipped: SkippedArchetype): string;
|
|
32
|
+
/**
|
|
33
|
+
* The structural-module variant: the cost is real and visible — the shell behind
|
|
34
|
+
* an exact kit module is intentionally recessed/flattened, so the note must say
|
|
35
|
+
* what now shows there rather than pretend the drop was cosmetic.
|
|
36
|
+
*/
|
|
37
|
+
export declare function structuralSkipNote(skipped: SkippedArchetype): string;
|
|
38
|
+
/**
|
|
39
|
+
* The forge-advisory line a module that came back OVER its LOD0 leaf budget contributes.
|
|
40
|
+
* It was KEPT at the kit's pinned voxel size rather than re-baked coarser (see the attempt
|
|
41
|
+
* loop in `createLevelArchetypes`), so the cost is real and worth reporting: the
|
|
42
|
+
* alternative was a wall with no relief at all.
|
|
43
|
+
*/
|
|
44
|
+
export declare function moduleOverBudgetNote(name: string, voxelCount: number, budget: number): string;
|
|
45
|
+
/**
|
|
46
|
+
* Deliberately the SAME predicate `forge-world.ts` pins the bake resolution with, imported
|
|
47
|
+
* rather than restated. The two decisions must agree: if this file stopped classifying a
|
|
48
|
+
* real kit piece as a module while forge-world kept pinning it, the piece would be pinned to
|
|
49
|
+
* the finest voxel size AND become eligible for the coarsen-rebake loop again — silently
|
|
50
|
+
* reinstating the exact defect that exemption exists to prevent, with every test still green.
|
|
51
|
+
* (`forge-world.ts`'s other predicate, the prefix-only one, is NOT interchangeable with this:
|
|
52
|
+
* it guards the world-GLB omission and must fail safe by over-matching, the opposite way.)
|
|
53
|
+
*/
|
|
54
|
+
export declare const isCladdingModule: (archetype: ForgeArchetypeRef) => boolean;
|
|
55
|
+
/** A used kit module is structural: the exact floor/wall/surround has no visual fallback. */
|
|
56
|
+
export declare const isUsedStructuralModule: (archetype: ForgeArchetypeRef) => boolean;
|
|
57
|
+
//# sourceMappingURL=archetype-skips.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archetype-skips.d.ts","sourceRoot":"","sources":["../../src/pipeline/archetype-skips.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gBAAgB,EAAE,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;IACf,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,UAAU,CAAC,EAAE,IAAI,CAAC;CACnB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,gBAAgB,CAE/G;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAG1D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAGpE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAI7F;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAAI,WAAW,iBAAiB,KAAG,OAA0C,CAAC;AAE3G,6FAA6F;AAC7F,eAAO,MAAM,sBAAsB,GAAI,WAAW,iBAAiB,KAAG,OACV,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A legacy archetype that was SKIPPED, and the advisory lines it contributes.
|
|
3
|
+
*
|
|
4
|
+
* Shared by step 3 (which records the skips) and step 5 (which drops their
|
|
5
|
+
* placements and reports them in the summary), so the placeholder shape and
|
|
6
|
+
* the notes that describe its cost have exactly one definition.
|
|
7
|
+
*/
|
|
8
|
+
import { isKitModuleArchetype } from '../forge-constants.js';
|
|
9
|
+
export function isSkippedArchetype(entry) {
|
|
10
|
+
return !!entry && entry.skippedArchetype === true;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The forge-advisory line a skipped archetype contributes to the summary.
|
|
14
|
+
*/
|
|
15
|
+
export function skipNote(skipped) {
|
|
16
|
+
const n = skipped.instanceCount;
|
|
17
|
+
return `Archetype "${skipped.name}" was dropped (${skipped.reason}), along with its ${n} instance${n === 1 ? '' : 's'}; the level keeps everything else.`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The structural-module variant: the cost is real and visible — the shell behind
|
|
21
|
+
* an exact kit module is intentionally recessed/flattened, so the note must say
|
|
22
|
+
* what now shows there rather than pretend the drop was cosmetic.
|
|
23
|
+
*/
|
|
24
|
+
export function structuralSkipNote(skipped) {
|
|
25
|
+
const n = skipped.instanceCount;
|
|
26
|
+
return `Structural cladding module "${skipped.name}" failed to bake (${skipped.reason}) — its ${n} placement${n === 1 ? '' : 's'} were dropped, leaving bare recessed shell where its carved stone belongs; the level keeps everything else.`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The forge-advisory line a module that came back OVER its LOD0 leaf budget contributes.
|
|
30
|
+
* It was KEPT at the kit's pinned voxel size rather than re-baked coarser (see the attempt
|
|
31
|
+
* loop in `createLevelArchetypes`), so the cost is real and worth reporting: the
|
|
32
|
+
* alternative was a wall with no relief at all.
|
|
33
|
+
*/
|
|
34
|
+
export function moduleOverBudgetNote(name, voxelCount, budget) {
|
|
35
|
+
return `Cladding module "${name}" baked to ${voxelCount} LOD0 leaves, over the ${budget} budget for its role — `
|
|
36
|
+
+ 'kept at the kit\'s pinned voxel size instead of re-baked coarser, because coarsening deletes the stone relief '
|
|
37
|
+
+ 'the module exists to carry. Its instances are heavier than a standard library asset.';
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Deliberately the SAME predicate `forge-world.ts` pins the bake resolution with, imported
|
|
41
|
+
* rather than restated. The two decisions must agree: if this file stopped classifying a
|
|
42
|
+
* real kit piece as a module while forge-world kept pinning it, the piece would be pinned to
|
|
43
|
+
* the finest voxel size AND become eligible for the coarsen-rebake loop again — silently
|
|
44
|
+
* reinstating the exact defect that exemption exists to prevent, with every test still green.
|
|
45
|
+
* (`forge-world.ts`'s other predicate, the prefix-only one, is NOT interchangeable with this:
|
|
46
|
+
* it guards the world-GLB omission and must fail safe by over-matching, the opposite way.)
|
|
47
|
+
*/
|
|
48
|
+
export const isCladdingModule = (archetype) => isKitModuleArchetype(archetype);
|
|
49
|
+
/** A used kit module is structural: the exact floor/wall/surround has no visual fallback. */
|
|
50
|
+
export const isUsedStructuralModule = (archetype) => isCladdingModule(archetype) && archetype.instanceCount > 0;
|
|
51
|
+
//# sourceMappingURL=archetype-skips.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"archetype-skips.js","sourceRoot":"","sources":["../../src/pipeline/archetype-skips.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AA6B7D,MAAM,UAAU,kBAAkB,CAAC,KAAiD;IAClF,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,gBAAgB,KAAK,IAAI,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAyB;IAChD,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAChC,OAAO,cAAc,OAAO,CAAC,IAAI,kBAAkB,OAAO,CAAC,MAAM,qBAAqB,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,oCAAoC,CAAC;AAC5J,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAyB;IAC1D,MAAM,CAAC,GAAG,OAAO,CAAC,aAAa,CAAC;IAChC,OAAO,+BAA+B,OAAO,CAAC,IAAI,qBAAqB,OAAO,CAAC,MAAM,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,6GAA6G,CAAC;AAChP,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY,EAAE,UAAkB,EAAE,MAAc;IACnF,OAAO,oBAAoB,IAAI,cAAc,UAAU,0BAA0B,MAAM,yBAAyB;UAC5G,gHAAgH;UAChH,sFAAsF,CAAC;AAC7F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,SAA4B,EAAW,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;AAE3G,6FAA6F;AAC7F,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,SAA4B,EAAW,EAAE,CAC9E,gBAAgB,CAAC,SAAS,CAAC,IAAI,SAAS,CAAC,aAAa,GAAG,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ForgedWorkStore, ForgeArtifactName } from './forged-work-store.js';
|
|
2
|
+
/** Read a required artifact, or fail the step with a clear message. */
|
|
3
|
+
export declare function requireArtifact<T>(store: ForgedWorkStore, name: ForgeArtifactName): Promise<T>;
|
|
4
|
+
//# sourceMappingURL=artifact-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-store.d.ts","sourceRoot":"","sources":["../../src/pipeline/artifact-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAGjF,uEAAuE;AACvE,wBAAsB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,CAMpG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ForgeStepError } from './errors.js';
|
|
2
|
+
/** Read a required artifact, or fail the step with a clear message. */
|
|
3
|
+
export async function requireArtifact(store, name) {
|
|
4
|
+
const value = await store.get(name);
|
|
5
|
+
if (value === null) {
|
|
6
|
+
throw new ForgeStepError(`Forged-work artifact "${name}" missing for job ${store.jobId} — cannot continue`);
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=artifact-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-store.js","sourceRoot":"","sources":["../../src/pipeline/artifact-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,uEAAuE;AACvE,MAAM,CAAC,KAAK,UAAU,eAAe,CAAI,KAAsB,EAAE,IAAuB;IACtF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,GAAG,CAAI,IAAI,CAAC,CAAC;IACvC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,cAAc,CAAC,yBAAyB,IAAI,qBAAqB,KAAK,CAAC,KAAK,oBAAoB,CAAC,CAAC;IAC9G,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact view of the game's asset library handed to the designer LLM so a
|
|
3
|
+
* forge can REUSE existing assets (HQ-generated landmarks, user imports)
|
|
4
|
+
* instead of minting new placeholders. Built from world.json `assets[]`.
|
|
5
|
+
*
|
|
6
|
+
* Moved from game-play-agent verbatim: the transform itself (records ->
|
|
7
|
+
* catalog entries) is pure. The READ that used to feed it —
|
|
8
|
+
* `readWorkWorldJson().assets` via a monorepo path walk — stayed on the host
|
|
9
|
+
* side; the host calls `buildAssetCatalog` on what it read and hands the
|
|
10
|
+
* result in as `ForgeDeps.assetCatalog`.
|
|
11
|
+
*/
|
|
12
|
+
export interface CatalogEntry {
|
|
13
|
+
assetId: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
/** Real-world dimensions in meters (y = height). fitBox wins over boundingBox. */
|
|
17
|
+
dims: {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
z: number;
|
|
21
|
+
};
|
|
22
|
+
/** hq = HQ-regenerated placeholder; placeholder = un-upgraded forge stand-in; library = user import / other finished asset. */
|
|
23
|
+
kind: 'hq' | 'placeholder' | 'library';
|
|
24
|
+
type: 'vxl' | 'glb';
|
|
25
|
+
}
|
|
26
|
+
export declare function buildAssetCatalog(assets: Array<Record<string, unknown>>): CatalogEntry[];
|
|
27
|
+
//# sourceMappingURL=asset-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-catalog.d.ts","sourceRoot":"","sources":["../../src/pipeline/asset-catalog.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kFAAkF;IAClF,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,+HAA+H;IAC/H,IAAI,EAAE,IAAI,GAAG,aAAa,GAAG,SAAS,CAAC;IACvC,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC;CACrB;AAMD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,YAAY,EAAE,CA2BxF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createLogger } from '../log.js';
|
|
2
|
+
const log = createLogger('world-forger-asset-catalog');
|
|
3
|
+
const PLACEABLE_TYPES = new Set(['vxl', 'voxels', 'glb', 'gltf', 'polygon-mesh']);
|
|
4
|
+
const CATALOG_CAP = 200;
|
|
5
|
+
const KIND_ORDER = { hq: 0, library: 1, placeholder: 2 };
|
|
6
|
+
export function buildAssetCatalog(assets) {
|
|
7
|
+
const entries = [];
|
|
8
|
+
for (const a of assets) {
|
|
9
|
+
const type = String(a.type ?? '');
|
|
10
|
+
if (!PLACEABLE_TYPES.has(type))
|
|
11
|
+
continue;
|
|
12
|
+
const dims = deriveDims(a);
|
|
13
|
+
if (!dims)
|
|
14
|
+
continue;
|
|
15
|
+
entries.push({
|
|
16
|
+
assetId: String(a.id),
|
|
17
|
+
name: String(a.name ?? a.id),
|
|
18
|
+
description: typeof a.description === 'string' ? a.description : undefined,
|
|
19
|
+
dims,
|
|
20
|
+
kind: a.hqGeneratedAt ? 'hq' : a.placeholder === true ? 'placeholder' : 'library',
|
|
21
|
+
type: type === 'glb' || type === 'gltf' || type === 'polygon-mesh' ? 'glb' : 'vxl',
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
// Stable sort (Node guarantees stability): hq > library > placeholder, so
|
|
25
|
+
// the cap drops the least valuable entries first.
|
|
26
|
+
entries.sort((x, y) => KIND_ORDER[x.kind] - KIND_ORDER[y.kind]);
|
|
27
|
+
if (entries.length > CATALOG_CAP) {
|
|
28
|
+
// No silent caps — dropped entries are invisible to the designer.
|
|
29
|
+
log.warn({ total: entries.length, dropped: entries.length - CATALOG_CAP }, 'Asset catalog over the designer cap — placeholder/library tail dropped');
|
|
30
|
+
}
|
|
31
|
+
return entries.slice(0, CATALOG_CAP);
|
|
32
|
+
}
|
|
33
|
+
function deriveDims(a) {
|
|
34
|
+
const fit = a.fitBox;
|
|
35
|
+
if (fit && fit.x && fit.z && fit.height)
|
|
36
|
+
return { x: fit.x, y: fit.height, z: fit.z };
|
|
37
|
+
const bb = a.boundingBox;
|
|
38
|
+
if (!bb)
|
|
39
|
+
return null;
|
|
40
|
+
const dx = (bb.maxX ?? 0) - (bb.minX ?? 0);
|
|
41
|
+
const dy = (bb.maxY ?? 0) - (bb.minY ?? 0);
|
|
42
|
+
const dz = (bb.maxZ ?? 0) - (bb.minZ ?? 0);
|
|
43
|
+
if (dx <= 0 || dy <= 0 || dz <= 0)
|
|
44
|
+
return null;
|
|
45
|
+
return { x: dx, y: dy, z: dz };
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=asset-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"asset-catalog.js","sourceRoot":"","sources":["../../src/pipeline/asset-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Step 3 — create the archetype LIBRARY ASSETS by voxelizing each uploaded GLB
|
|
3
|
+
* in the browser, through the host's transport seam.
|
|
4
|
+
*
|
|
5
|
+
* Moved from game-play-agent's `pipeline.ts` verbatim, with the one
|
|
6
|
+
* `sendAndWaitForResult` call site turned into `deps.transport.sendAndWait`
|
|
7
|
+
* (same arguments, same `null`-on-timeout contract — see transport-types.ts)
|
|
8
|
+
* and the logger turned into `deps.logger`.
|
|
9
|
+
*/
|
|
10
|
+
import type { ForgedWorkStore } from './forged-work-store.js';
|
|
11
|
+
import type { ForgeDeps } from './deps.js';
|
|
12
|
+
/**
|
|
13
|
+
* Minimum gap between archetype-progress checkpoint writes. GCS rate-limits
|
|
14
|
+
* mutations of one object to ~1/s sustained — sub-second archetype bakes used to
|
|
15
|
+
* rewrite the checkpoint faster than that and 429-fail the whole step.
|
|
16
|
+
*/
|
|
17
|
+
export declare const ARCHETYPE_CHECKPOINT_MIN_INTERVAL_MS = 2000;
|
|
18
|
+
/**
|
|
19
|
+
* Create the archetype LIBRARY ASSETS by voxelizing each uploaded GLB at fine
|
|
20
|
+
* resolution in the browser. Before the level bake, so an unresponsive browser
|
|
21
|
+
* is caught BEFORE the expensive bake.
|
|
22
|
+
*
|
|
23
|
+
* Resumability: each archetype's engine record is cached (keyed by its index in
|
|
24
|
+
* forge.archetypes) the moment it bakes, so a resume after a partial failure
|
|
25
|
+
* skips the ones already baked instead of re-baking the whole set. Each bake is
|
|
26
|
+
* a SINGLE attempt (maxRetries: 1) — like the level bake, it is an expensive,
|
|
27
|
+
* non-idempotent browser job; retrying just re-fires a fresh bake (new asset id)
|
|
28
|
+
* into an already-struggling browser. A failed/slow archetype is retried only by
|
|
29
|
+
* the orchestrator's resume, which now picks up from the per-archetype cache.
|
|
30
|
+
*
|
|
31
|
+
* A bake FAILURE (the browser answered and rejected this one GLB) never aborts, not
|
|
32
|
+
* even for a used structural dungeon module: the failure is individual and
|
|
33
|
+
* deterministic, so aborting would fail every resume identically and forfeit the
|
|
34
|
+
* whole forge for one archetype. The archetype is dropped with a note — structural
|
|
35
|
+
* modules get a note naming the bare shell left behind — and the forge keeps
|
|
36
|
+
* everything else. A TIMEOUT still aborts resumably: the browser is not answering,
|
|
37
|
+
* so every remaining bake would burn the same timeout, and a resume can succeed.
|
|
38
|
+
* Structural skip markers are removed on resume so those bakes get a second chance.
|
|
39
|
+
*
|
|
40
|
+
* Modules remain exempt from the budget COARSENING below: a module is kept at the
|
|
41
|
+
* voxel size forge-world.ts pinned it to and noted, because doubling that size deletes the
|
|
42
|
+
* relief the kit exists for. Every other archetype coarsens exactly as before.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createLevelArchetypes(store: ForgedWorkStore, deps: ForgeDeps): Promise<void>;
|
|
45
|
+
//# sourceMappingURL=create-level-archetypes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-level-archetypes.d.ts","sourceRoot":"","sources":["../../src/pipeline/create-level-archetypes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAY3C;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,OAAO,CAAC;AAEzD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAwLlG"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { requireArtifact } from './artifact-store.js';
|
|
2
|
+
import { ForgeStepError } from './errors.js';
|
|
3
|
+
import { forgeRequestId } from './request-id.js';
|
|
4
|
+
import { isCladdingModule, isSkippedArchetype, isUsedStructuralModule, } from './archetype-skips.js';
|
|
5
|
+
import { ASSET_LOD0_VOXEL_BUDGET } from '../forge-constants.js';
|
|
6
|
+
/** Per-archetype asset voxelization (small objects at fine resolution). */
|
|
7
|
+
const ASSET_CREATE_TIMEOUT_MS = 5 * 60 * 1000;
|
|
8
|
+
/**
|
|
9
|
+
* Minimum gap between archetype-progress checkpoint writes. GCS rate-limits
|
|
10
|
+
* mutations of one object to ~1/s sustained — sub-second archetype bakes used to
|
|
11
|
+
* rewrite the checkpoint faster than that and 429-fail the whole step.
|
|
12
|
+
*/
|
|
13
|
+
export const ARCHETYPE_CHECKPOINT_MIN_INTERVAL_MS = 2000;
|
|
14
|
+
/**
|
|
15
|
+
* Create the archetype LIBRARY ASSETS by voxelizing each uploaded GLB at fine
|
|
16
|
+
* resolution in the browser. Before the level bake, so an unresponsive browser
|
|
17
|
+
* is caught BEFORE the expensive bake.
|
|
18
|
+
*
|
|
19
|
+
* Resumability: each archetype's engine record is cached (keyed by its index in
|
|
20
|
+
* forge.archetypes) the moment it bakes, so a resume after a partial failure
|
|
21
|
+
* skips the ones already baked instead of re-baking the whole set. Each bake is
|
|
22
|
+
* a SINGLE attempt (maxRetries: 1) — like the level bake, it is an expensive,
|
|
23
|
+
* non-idempotent browser job; retrying just re-fires a fresh bake (new asset id)
|
|
24
|
+
* into an already-struggling browser. A failed/slow archetype is retried only by
|
|
25
|
+
* the orchestrator's resume, which now picks up from the per-archetype cache.
|
|
26
|
+
*
|
|
27
|
+
* A bake FAILURE (the browser answered and rejected this one GLB) never aborts, not
|
|
28
|
+
* even for a used structural dungeon module: the failure is individual and
|
|
29
|
+
* deterministic, so aborting would fail every resume identically and forfeit the
|
|
30
|
+
* whole forge for one archetype. The archetype is dropped with a note — structural
|
|
31
|
+
* modules get a note naming the bare shell left behind — and the forge keeps
|
|
32
|
+
* everything else. A TIMEOUT still aborts resumably: the browser is not answering,
|
|
33
|
+
* so every remaining bake would burn the same timeout, and a resume can succeed.
|
|
34
|
+
* Structural skip markers are removed on resume so those bakes get a second chance.
|
|
35
|
+
*
|
|
36
|
+
* Modules remain exempt from the budget COARSENING below: a module is kept at the
|
|
37
|
+
* voxel size forge-world.ts pinned it to and noted, because doubling that size deletes the
|
|
38
|
+
* relief the kit exists for. Every other archetype coarsens exactly as before.
|
|
39
|
+
*/
|
|
40
|
+
export async function createLevelArchetypes(store, deps) {
|
|
41
|
+
const forge = await requireArtifact(store, 'forge');
|
|
42
|
+
const progress = (await store.get('archetype-progress')) ?? {};
|
|
43
|
+
const structuralNames = new Set(forge.archetypes.filter(isUsedStructuralModule).map((archetype) => archetype.name));
|
|
44
|
+
for (const [index, record] of Object.entries(progress)) {
|
|
45
|
+
const archetype = forge.archetypes[Number(index)];
|
|
46
|
+
const recordedName = typeof record.name === 'string' ? record.name : null;
|
|
47
|
+
// Progress is index-addressed for ordering, but NAME-addressed for identity.
|
|
48
|
+
// A resumed forge may reorder archetypes, so an index hit alone cannot prove
|
|
49
|
+
// that the cached asset belongs to its current occupant.
|
|
50
|
+
const identityMatches = !!archetype && recordedName === archetype.name;
|
|
51
|
+
// A structural skip is retried on resume — the skip was a degrade, and a
|
|
52
|
+
// resume is a free second chance at the exact visual the kit exists for.
|
|
53
|
+
// Match it by the RECORDED name so reordering cannot hide it under a
|
|
54
|
+
// non-structural current occupant.
|
|
55
|
+
const isStructuralSkip = isSkippedArchetype(record)
|
|
56
|
+
&& recordedName !== null
|
|
57
|
+
&& structuralNames.has(recordedName);
|
|
58
|
+
if (!identityMatches || isStructuralSkip) {
|
|
59
|
+
delete progress[index];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// A dungeon's glow (rune inlays, crystal veins, torch flames) is authored as
|
|
63
|
+
// PALETTE COLORS, and the fixture/key/door-leaf archetypes are baked from the
|
|
64
|
+
// same palette as the level. The map is passed WHOLE to every archetype of a
|
|
65
|
+
// dungeon level rather than sliced per archetype: matching is exact-color, so
|
|
66
|
+
// a key whose color never appears in that archetype is a silent no-op (it
|
|
67
|
+
// comes back in `emissiveUnmatched`, which is a note, never an error).
|
|
68
|
+
const emissiveByColor = forge.dungeonEmissiveByColor;
|
|
69
|
+
// GCS caps mutations of a SINGLE object at ~1/s; checkpointing after every archetype
|
|
70
|
+
// hammered that limit when small props baked sub-second, 429-failing forges whose bakes
|
|
71
|
+
// had all succeeded. The checkpoint is pure resume state (the in-memory map is
|
|
72
|
+
// authoritative for this run), so writes are throttled AND best-effort: a skipped or
|
|
73
|
+
// failed write only means a resume re-bakes the archetypes recorded since the last one.
|
|
74
|
+
let lastCheckpointAt = 0;
|
|
75
|
+
const checkpoint = async () => {
|
|
76
|
+
if (Date.now() - lastCheckpointAt < ARCHETYPE_CHECKPOINT_MIN_INTERVAL_MS)
|
|
77
|
+
return;
|
|
78
|
+
lastCheckpointAt = Date.now(); // advance even on failure — re-hitting a 429ing object makes the storm worse
|
|
79
|
+
try {
|
|
80
|
+
await store.put('archetype-progress', progress);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
deps.logger.warn({ err, jobId: store.jobId }, 'archetype-progress checkpoint write failed — continuing; a resume re-bakes the unrecorded archetypes');
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
for (let i = 0; i < forge.archetypes.length; i++) {
|
|
87
|
+
if (progress[String(i)])
|
|
88
|
+
continue; // already baked in a prior attempt — skip
|
|
89
|
+
const archetype = forge.archetypes[i];
|
|
90
|
+
// Reused library asset: no GLB, no voxelize — record a stub that carries
|
|
91
|
+
// the EXISTING asset id so placement/persist resolve instances to it. The
|
|
92
|
+
// asset record itself is never rewritten (it may be an HQ model).
|
|
93
|
+
if (archetype.reuse) {
|
|
94
|
+
progress[String(i)] = { id: archetype.reuse.assetId, name: archetype.reuse.assetName, reused: true };
|
|
95
|
+
await checkpoint();
|
|
96
|
+
deps.logger.info({ jobId: store.jobId, archetype: archetype.name, assetId: archetype.reuse.assetId }, 'Archetype reuses existing library asset — bake skipped');
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
const isModule = isCladdingModule(archetype);
|
|
100
|
+
const isStructuralModule = isUsedStructuralModule(archetype);
|
|
101
|
+
// Neither a bake nor a reuse target. Even a structural module degrades to a
|
|
102
|
+
// skip: the defect is deterministic (a resume would find the same artifact),
|
|
103
|
+
// so aborting would trade a level that is 99% fine for no level at all.
|
|
104
|
+
if (!archetype.glbUrl || !archetype.assetVoxelOptions) {
|
|
105
|
+
const skipped = {
|
|
106
|
+
skippedArchetype: true,
|
|
107
|
+
name: archetype.name,
|
|
108
|
+
reason: 'the forge artifact carried neither a GLB nor a reuse target for it',
|
|
109
|
+
instanceCount: archetype.instanceCount,
|
|
110
|
+
...(isStructuralModule ? { structural: true } : {}),
|
|
111
|
+
};
|
|
112
|
+
progress[String(i)] = skipped;
|
|
113
|
+
await checkpoint();
|
|
114
|
+
deps.logger.warn({ jobId: store.jobId, archetype: archetype.name, structural: isStructuralModule }, 'Archetype has no GLB and no reuse target — dropped, the level keeps everything else');
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// Bake at the budget-predicted size first (deriveArchetypeVoxelBake); if the
|
|
118
|
+
// result still exceeds the archetype's own LOD0 budget (bbox under-estimated
|
|
119
|
+
// the real surface), re-bake with doubled voxel sizes until it fits. The
|
|
120
|
+
// re-bake upserts the same asset name, so only the final bake survives.
|
|
121
|
+
const leafBudget = archetype.assetVoxelOptions.lod0Budget ?? ASSET_LOD0_VOXEL_BUDGET;
|
|
122
|
+
let opts = { ...archetype.assetVoxelOptions };
|
|
123
|
+
let asset = null;
|
|
124
|
+
let failureReason = null;
|
|
125
|
+
const MAX_COARSEN_REBAKES = 2;
|
|
126
|
+
for (let attempt = 0;; attempt++) {
|
|
127
|
+
const requestId = forgeRequestId('asset');
|
|
128
|
+
const result = await deps.transport.sendAndWait({
|
|
129
|
+
type: 'CREATE_ASSET_FROM_GLB_URL',
|
|
130
|
+
requestId,
|
|
131
|
+
name: archetype.name,
|
|
132
|
+
glbUrl: archetype.glbUrl,
|
|
133
|
+
options: { ...opts, ...(emissiveByColor ? { emissiveByColor } : {}) },
|
|
134
|
+
}, 'ASSET_FROM_GLB_URL_RESULT', ASSET_CREATE_TIMEOUT_MS, { maxRetries: 1 });
|
|
135
|
+
if (!result) {
|
|
136
|
+
// The browser did not answer inside the timeout — infrastructure, not an
|
|
137
|
+
// individual archetype error: with the editor gone, every remaining bake
|
|
138
|
+
// would burn the same timeout. Abort resumably; baked archetypes are
|
|
139
|
+
// checkpointed and the resume retries from here.
|
|
140
|
+
throw new ForgeStepError(`Voxelizing archetype "${archetype.name}" timed out — is the editor open?`, forge.glbUrl);
|
|
141
|
+
}
|
|
142
|
+
if (!result.success || !result.asset) {
|
|
143
|
+
failureReason = result.error ?? 'unknown error';
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
asset = result.asset;
|
|
147
|
+
const voxelCount = typeof asset.voxelCount === 'number' ? asset.voxelCount : 0;
|
|
148
|
+
if (voxelCount <= leafBudget)
|
|
149
|
+
break;
|
|
150
|
+
// A cladding module is EXEMPT from coarsening. Its relief is authored in multiples of
|
|
151
|
+
// MODULE_MIN_VOXEL_M — mortar recesses one voxel deep, protrusions two — which is why
|
|
152
|
+
// forge-world.ts pins the bake there. The first doubling already drops the mortar
|
|
153
|
+
// lines and the chamfers below a voxel; the second takes the protrusions with them and
|
|
154
|
+
// the piece bakes to flat colour, the very surface the kit exists to replace. Nothing
|
|
155
|
+
// downstream can tell: the asset is still produced and every other check still passes.
|
|
156
|
+
// So keep the bake at its pinned size and NOTE it (moduleOverBudgetNote) — heavier
|
|
157
|
+
// instances are a cost the caller can see and act on; a flattened kit is not.
|
|
158
|
+
if (isModule) {
|
|
159
|
+
deps.logger.warn({ jobId: store.jobId, archetype: archetype.name, voxelCount, budget: leafBudget, minVoxelSize: opts.minVoxelSize }, 'Cladding module baked over its voxel budget — kept at the pinned module voxel size and noted; coarsening it would delete the stone relief');
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
if (attempt >= MAX_COARSEN_REBAKES) {
|
|
163
|
+
deps.logger.warn({ jobId: store.jobId, archetype: archetype.name, voxelCount, minVoxelSize: opts.minVoxelSize }, 'Archetype still over the voxel budget after coarsening rebakes — keeping the last bake');
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
opts = { ...opts, minVoxelSize: opts.minVoxelSize * 2, maxVoxelSize: opts.maxVoxelSize * 2 };
|
|
167
|
+
deps.logger.info({ jobId: store.jobId, archetype: archetype.name, voxelCount, budget: leafBudget, nextMinVoxelSize: opts.minVoxelSize }, 'Archetype bake over the voxel budget — re-baking at doubled voxel sizes');
|
|
168
|
+
}
|
|
169
|
+
if (failureReason !== null) {
|
|
170
|
+
// The browser ANSWERED and rejected this one bake — an individual,
|
|
171
|
+
// deterministic error. Aborting would fail every resume identically and
|
|
172
|
+
// forfeit the whole forge for one archetype, so degrade instead: drop it
|
|
173
|
+
// (its placements follow at persist) with a note. For a structural module
|
|
174
|
+
// the cost is bare recessed shell where its carved stone belonged — the
|
|
175
|
+
// note says so, and a resume retries the bake for a second chance.
|
|
176
|
+
const skipped = {
|
|
177
|
+
skippedArchetype: true,
|
|
178
|
+
name: archetype.name,
|
|
179
|
+
reason: failureReason,
|
|
180
|
+
instanceCount: archetype.instanceCount,
|
|
181
|
+
...(isStructuralModule ? { structural: true } : {}),
|
|
182
|
+
};
|
|
183
|
+
progress[String(i)] = skipped;
|
|
184
|
+
await checkpoint();
|
|
185
|
+
deps.logger.warn({ jobId: store.jobId, archetype: archetype.name, structural: isStructuralModule, reason: failureReason }, 'Archetype bake failed — dropped, the level keeps everything else');
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
progress[String(i)] = asset;
|
|
189
|
+
await checkpoint(); // persist incrementally for resume (throttled, best-effort)
|
|
190
|
+
deps.logger.info({ jobId: store.jobId, archetype: archetype.name, assetId: asset.id }, 'Archetype asset created');
|
|
191
|
+
}
|
|
192
|
+
// All archetypes baked — write the ordered combined array (the completion
|
|
193
|
+
// marker the runner skips on; index order matches buildSummary's pairing).
|
|
194
|
+
const created = forge.archetypes.map((_, i) => progress[String(i)]);
|
|
195
|
+
await store.put('archetype-assets', created);
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=create-level-archetypes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-level-archetypes.js","sourceRoot":"","sources":["../../src/pipeline/create-level-archetypes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ForgeDeps — everything the world-forger pipeline needs from its host, so
|
|
3
|
+
* the SAME implementation runs behind game-play-agent's Mastra web lane and
|
|
4
|
+
* the CLI lane (`bitmagic forge`, split across api-server for steps 1-2 and
|
|
5
|
+
* the CLI's own headless browser for steps 3-5). Modeled on
|
|
6
|
+
* `shared/asset-core/src/types.ts`'s
|
|
7
|
+
* `GenerateDeps` (read that first) — a generator/pipeline takes its host's
|
|
8
|
+
* capabilities as a parameter and owns no framework, filesystem or tenant of
|
|
9
|
+
* its own.
|
|
10
|
+
*
|
|
11
|
+
* Six seams:
|
|
12
|
+
* - `storage` — the StorageEnv shape `createForgedWorkStore(jobId, env)`
|
|
13
|
+
* already accepts (see forged-work-store.ts) and step 2's
|
|
14
|
+
* uploads write through.
|
|
15
|
+
* - `model` — the LLM client the director and designer call.
|
|
16
|
+
* - `transport` — replaces `sendAndWaitForResult`: the browser round-trips
|
|
17
|
+
* steps 3-5 make (archetype bakes, the level bake, instance
|
|
18
|
+
* placement). Not read by steps 1-2.
|
|
19
|
+
* - `worldJson` — reads the game's current world.json for step 5's persist
|
|
20
|
+
* plan. Read-only by design: the step RETURNS modifications
|
|
21
|
+
* and the host applies them (see world-json-source.ts).
|
|
22
|
+
* - `assetCatalog` — optional; absence degrades to "no reuse", never a
|
|
23
|
+
* failure. The host reads world.json and calls
|
|
24
|
+
* `buildAssetCatalog` itself (see asset-catalog.ts).
|
|
25
|
+
* - `hqAssetIds` — optional, defaults to empty. Read by step 5's placeholder
|
|
26
|
+
* purge, and read WHEN that purge runs rather than when these
|
|
27
|
+
* deps were built (see the field).
|
|
28
|
+
*/
|
|
29
|
+
import type { ForgeStorageEnv } from './storage-types.js';
|
|
30
|
+
import type { ForgeModelClient } from './model-types.js';
|
|
31
|
+
import type { ForgeTransport } from './transport-types.js';
|
|
32
|
+
import type { ForgeWorldJsonSource } from './world-json-source.js';
|
|
33
|
+
import type { CatalogEntry } from './asset-catalog.js';
|
|
34
|
+
/**
|
|
35
|
+
* The logging surface the pipeline needs. Structurally satisfied by a pino
|
|
36
|
+
* logger (game-play-agent passes its own `createLogger(...)` result
|
|
37
|
+
* directly), same pattern as `shared/asset-core`'s `CoreLogger`. The
|
|
38
|
+
* object-first overload matters for errors: `warn({ err }, msg)` serializes
|
|
39
|
+
* the Error with its stack as a structured field, where
|
|
40
|
+
* `warn(\`${err.message}\`)` throws the stack away.
|
|
41
|
+
*/
|
|
42
|
+
export interface ForgeLogger {
|
|
43
|
+
info(message: string, ...rest: unknown[]): void;
|
|
44
|
+
info(context: Record<string, unknown>, message: string): void;
|
|
45
|
+
warn(message: string, ...rest: unknown[]): void;
|
|
46
|
+
warn(context: Record<string, unknown>, message: string): void;
|
|
47
|
+
error(message: string, ...rest: unknown[]): void;
|
|
48
|
+
error(context: Record<string, unknown>, message: string): void;
|
|
49
|
+
}
|
|
50
|
+
export interface ForgeDeps {
|
|
51
|
+
storage: ForgeStorageEnv;
|
|
52
|
+
model: ForgeModelClient;
|
|
53
|
+
transport: ForgeTransport;
|
|
54
|
+
worldJson: ForgeWorldJsonSource;
|
|
55
|
+
/** Existing library assets the designer may reuse. Absent/empty = no reuse
|
|
56
|
+
* candidates (best-effort — this must never fail the job). */
|
|
57
|
+
assetCatalog?: CatalogEntry[];
|
|
58
|
+
/**
|
|
59
|
+
* Asset ids with an HQ regeneration currently in flight, whose placeholder
|
|
60
|
+
* assets step 5's purge must therefore spare. Omitted = none.
|
|
61
|
+
*
|
|
62
|
+
* A FUNCTION, for the same reason `worldJson` above is one: the purge runs at
|
|
63
|
+
* the very end of a 30-60 minute forge (an LLM design, a geometry build, the
|
|
64
|
+
* archetype bakes and a level bake that alone may take 20 minutes), and HQ
|
|
65
|
+
* jobs both start and finish inside that window. A set captured when the host
|
|
66
|
+
* built these deps would miss every job STARTED since — purging the exact
|
|
67
|
+
* placeholders that protection exists for.
|
|
68
|
+
*/
|
|
69
|
+
hqAssetIds?: () => Promise<Set<string>>;
|
|
70
|
+
logger: ForgeLogger;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=deps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/pipeline/deps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IAChD,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;IACjD,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAChE;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,gBAAgB,CAAC;IACxB,SAAS,EAAE,cAAc,CAAC;IAC1B,SAAS,EAAE,oBAAoB,CAAC;IAChC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,YAAY,EAAE,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACxC,MAAM,EAAE,WAAW,CAAC;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deps.js","sourceRoot":"","sources":["../../src/pipeline/deps.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pipeline step failed. Carries `glbUrl` when the source GLB already exists
|
|
3
|
+
* so the caller can tell the user it can be voxelized manually from the
|
|
4
|
+
* Assets tab (the original tool's timeout affordance).
|
|
5
|
+
*/
|
|
6
|
+
export declare class ForgeStepError extends Error {
|
|
7
|
+
readonly glbUrl?: string;
|
|
8
|
+
constructor(message: string, glbUrl?: string);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/pipeline/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;CAK7C"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A pipeline step failed. Carries `glbUrl` when the source GLB already exists
|
|
3
|
+
* so the caller can tell the user it can be voxelized manually from the
|
|
4
|
+
* Assets tab (the original tool's timeout affordance).
|
|
5
|
+
*/
|
|
6
|
+
export class ForgeStepError extends Error {
|
|
7
|
+
constructor(message, glbUrl) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = 'ForgeStepError';
|
|
10
|
+
this.glbUrl = glbUrl;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.js.map
|