@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
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The starter template's scattered trees/rocks a forged level orphans, and the
|
|
3
|
+
* plan for stripping them.
|
|
4
|
+
*
|
|
5
|
+
* Moved here from game-play-agent's `utils/voxel-default-assets.ts` — which
|
|
6
|
+
* MINTS these ids and now re-exports this planner — because the world-forger's
|
|
7
|
+
* persist step is its only caller and that step no longer lives in
|
|
8
|
+
* game-play-agent. The generator and the matcher still share one definition of
|
|
9
|
+
* the id shapes; they are just imported across the seam instead of adjacent.
|
|
10
|
+
*/
|
|
11
|
+
/** Ids of the starter scatter to strip from a world.json. */
|
|
12
|
+
export interface StarterScatterRemoval {
|
|
13
|
+
/** `assets[].id` values to drop. */
|
|
14
|
+
assetIds: string[];
|
|
15
|
+
/** `environmentObjects[].id` values to drop. */
|
|
16
|
+
objectIds: string[];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Work out which starter trees/rocks a world.json still carries from its template.
|
|
20
|
+
*
|
|
21
|
+
* Every voxel starter template ships the six default tree/rock assets, and two of them
|
|
22
|
+
* (standard-3d, first-person) also ship 23 scattered instances — that's the scene in the
|
|
23
|
+
* template screenshot. When the World-Forger bakes a real level it *replaces* the template's
|
|
24
|
+
* terrain but only upserts its own assets/instances, so the starter scatter survives: the
|
|
25
|
+
* trees get re-snapped onto the forged terrain (they're `placeOnTerrain`) somewhere off in
|
|
26
|
+
* the origin corner, and the six assets stay in the library where the creator's Remove button
|
|
27
|
+
* is hidden for anything still referenced by an instance.
|
|
28
|
+
*
|
|
29
|
+
* Only exact id matches on BOTH the instance id and its assetId count as starter scatter, so
|
|
30
|
+
* anything the AI later placed from these assets is left alone; an asset is dropped only once
|
|
31
|
+
* nothing else references it.
|
|
32
|
+
*/
|
|
33
|
+
export declare function planStarterScatterRemoval(worldData: Record<string, unknown> | null | undefined): StarterScatterRemoval;
|
|
34
|
+
//# sourceMappingURL=starter-scatter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starter-scatter.d.ts","sourceRoot":"","sources":["../../src/pipeline/starter-scatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,6DAA6D;AAC7D,MAAM,WAAW,qBAAqB;IACpC,oCAAoC;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gDAAgD;IAChD,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,GACpD,qBAAqB,CA0BvB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The starter template's scattered trees/rocks a forged level orphans, and the
|
|
3
|
+
* plan for stripping them.
|
|
4
|
+
*
|
|
5
|
+
* Moved here from game-play-agent's `utils/voxel-default-assets.ts` — which
|
|
6
|
+
* MINTS these ids and now re-exports this planner — because the world-forger's
|
|
7
|
+
* persist step is its only caller and that step no longer lives in
|
|
8
|
+
* game-play-agent. The generator and the matcher still share one definition of
|
|
9
|
+
* the id shapes; they are just imported across the seam instead of adjacent.
|
|
10
|
+
*/
|
|
11
|
+
/** Asset ids minted by the default-asset generator (and hardcoded in the starter templates' world.json). */
|
|
12
|
+
const STARTER_ASSET_ID_RE = /^default-(tree|rock)-\d+$/;
|
|
13
|
+
/** Instance ids minted by `generateObjectId` (and hardcoded in the starter templates). */
|
|
14
|
+
const STARTER_OBJECT_ID_RE = /^default_(tree|rock)_\d+$/;
|
|
15
|
+
/**
|
|
16
|
+
* Work out which starter trees/rocks a world.json still carries from its template.
|
|
17
|
+
*
|
|
18
|
+
* Every voxel starter template ships the six default tree/rock assets, and two of them
|
|
19
|
+
* (standard-3d, first-person) also ship 23 scattered instances — that's the scene in the
|
|
20
|
+
* template screenshot. When the World-Forger bakes a real level it *replaces* the template's
|
|
21
|
+
* terrain but only upserts its own assets/instances, so the starter scatter survives: the
|
|
22
|
+
* trees get re-snapped onto the forged terrain (they're `placeOnTerrain`) somewhere off in
|
|
23
|
+
* the origin corner, and the six assets stay in the library where the creator's Remove button
|
|
24
|
+
* is hidden for anything still referenced by an instance.
|
|
25
|
+
*
|
|
26
|
+
* Only exact id matches on BOTH the instance id and its assetId count as starter scatter, so
|
|
27
|
+
* anything the AI later placed from these assets is left alone; an asset is dropped only once
|
|
28
|
+
* nothing else references it.
|
|
29
|
+
*/
|
|
30
|
+
export function planStarterScatterRemoval(worldData) {
|
|
31
|
+
const empty = { assetIds: [], objectIds: [] };
|
|
32
|
+
if (!worldData)
|
|
33
|
+
return empty;
|
|
34
|
+
const assets = Array.isArray(worldData.assets) ? worldData.assets : [];
|
|
35
|
+
const environmentObjects = Array.isArray(worldData.environmentObjects) ? worldData.environmentObjects : [];
|
|
36
|
+
const idOf = (item, key) => String(item?.[key] ?? '');
|
|
37
|
+
const objectIds = environmentObjects
|
|
38
|
+
.filter((obj) => STARTER_OBJECT_ID_RE.test(idOf(obj, 'id')) && STARTER_ASSET_ID_RE.test(idOf(obj, 'assetId')))
|
|
39
|
+
.map((obj) => idOf(obj, 'id'));
|
|
40
|
+
const removedObjectIds = new Set(objectIds);
|
|
41
|
+
const stillReferenced = new Set(environmentObjects
|
|
42
|
+
.filter((obj) => !removedObjectIds.has(idOf(obj, 'id')))
|
|
43
|
+
.map((obj) => idOf(obj, 'assetId')));
|
|
44
|
+
const assetIds = assets
|
|
45
|
+
.map((asset) => idOf(asset, 'id'))
|
|
46
|
+
.filter((id) => STARTER_ASSET_ID_RE.test(id) && !stillReferenced.has(id));
|
|
47
|
+
return { assetIds, objectIds };
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=starter-scatter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"starter-scatter.js","sourceRoot":"","sources":["../../src/pipeline/starter-scatter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4GAA4G;AAC5G,MAAM,mBAAmB,GAAG,2BAA2B,CAAC;AACxD,0FAA0F;AAC1F,MAAM,oBAAoB,GAAG,2BAA2B,CAAC;AAUzD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,yBAAyB,CACvC,SAAqD;IAErD,MAAM,KAAK,GAA0B,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;IACrE,IAAI,CAAC,SAAS;QAAE,OAAO,KAAK,CAAC;IAE7B,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,MAAM,kBAAkB,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;IAE3G,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,GAAqB,EAAU,EAAE,CAC5D,MAAM,CAAE,IAAuC,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAEhE,MAAM,SAAS,GAAG,kBAAkB;SACjC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,IAAI,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC;SAC7G,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;IAEjC,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,kBAAkB;SACf,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;SACvD,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CACtC,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM;SACpB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;SACjC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAE5E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The storage seam — deliberately a NARROW structural subset of the host's
|
|
3
|
+
* real `StorageClient`/`StorageEnv` (game-play-agent's
|
|
4
|
+
* `src/mastra/storage/StorageClient.ts` / `storage-env.ts`), field-for-field
|
|
5
|
+
* on the methods this package actually calls. Because TypeScript interfaces
|
|
6
|
+
* are structural, the host's real S3/GCS/local client satisfies this without
|
|
7
|
+
* any adapter — `getStorageEnv()` can be passed straight through as
|
|
8
|
+
* `ForgeDeps.storage`.
|
|
9
|
+
*
|
|
10
|
+
* Bytes are `Uint8Array`, not node's `Buffer`. `Buffer` would be correct for
|
|
11
|
+
* every host today, but it is a NODE type: putting it in this published
|
|
12
|
+
* package's type surface forces every consumer to install `@types/node` or
|
|
13
|
+
* type-check with `skipLibCheck`, and a consumer without either gets
|
|
14
|
+
* `Cannot find name 'Buffer'` from a .d.ts they never wrote. `Buffer` extends
|
|
15
|
+
* `Uint8Array`, so a host whose real client takes and returns Buffers still
|
|
16
|
+
* satisfies this unchanged.
|
|
17
|
+
*/
|
|
18
|
+
export interface ForgePutObjectParams {
|
|
19
|
+
bucket: string;
|
|
20
|
+
key: string;
|
|
21
|
+
body: string | Uint8Array;
|
|
22
|
+
contentType: string;
|
|
23
|
+
cacheControl: string;
|
|
24
|
+
contentEncoding?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ForgeHeadObjectParams {
|
|
27
|
+
bucket: string;
|
|
28
|
+
key: string;
|
|
29
|
+
}
|
|
30
|
+
export interface ForgeGetObjectParams {
|
|
31
|
+
bucket: string;
|
|
32
|
+
key: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ForgeStorageClient {
|
|
35
|
+
/** Returns the number of bytes actually written (the host may compress). */
|
|
36
|
+
putObject(params: ForgePutObjectParams): Promise<number>;
|
|
37
|
+
headObject(params: ForgeHeadObjectParams): Promise<boolean>;
|
|
38
|
+
getObject(params: ForgeGetObjectParams): Promise<Uint8Array | null>;
|
|
39
|
+
}
|
|
40
|
+
/** `createForgedWorkStore(jobId, env)` already takes this shape as a parameter
|
|
41
|
+
* (see forged-work-store.ts) — this is that SAME seam, not a new one. */
|
|
42
|
+
export interface ForgeStorageEnv {
|
|
43
|
+
storageClient: ForgeStorageClient;
|
|
44
|
+
bucket: string;
|
|
45
|
+
/** Public URL prefix (CDN, or file://) the uploaded key is appended to. */
|
|
46
|
+
publicUrl: string;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=storage-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-types.d.ts","sourceRoot":"","sources":["../../src/pipeline/storage-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,4EAA4E;IAC5E,SAAS,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD,UAAU,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,SAAS,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;CACrE;AAED;0EAC0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,kBAAkB,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;CACnB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The storage seam — deliberately a NARROW structural subset of the host's
|
|
3
|
+
* real `StorageClient`/`StorageEnv` (game-play-agent's
|
|
4
|
+
* `src/mastra/storage/StorageClient.ts` / `storage-env.ts`), field-for-field
|
|
5
|
+
* on the methods this package actually calls. Because TypeScript interfaces
|
|
6
|
+
* are structural, the host's real S3/GCS/local client satisfies this without
|
|
7
|
+
* any adapter — `getStorageEnv()` can be passed straight through as
|
|
8
|
+
* `ForgeDeps.storage`.
|
|
9
|
+
*
|
|
10
|
+
* Bytes are `Uint8Array`, not node's `Buffer`. `Buffer` would be correct for
|
|
11
|
+
* every host today, but it is a NODE type: putting it in this published
|
|
12
|
+
* package's type surface forces every consumer to install `@types/node` or
|
|
13
|
+
* type-check with `skipLibCheck`, and a consumer without either gets
|
|
14
|
+
* `Cannot find name 'Buffer'` from a .d.ts they never wrote. `Buffer` extends
|
|
15
|
+
* `Uint8Array`, so a host whose real client takes and returns Buffers still
|
|
16
|
+
* satisfies this unchanged.
|
|
17
|
+
*/
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=storage-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-types.js","sourceRoot":"","sources":["../../src/pipeline/storage-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The transport seam — replaces game-play-agent's `sendAndWaitForResult`
|
|
3
|
+
* (`utils/voxel-ws.ts`). One method: send a command, await a typed result.
|
|
4
|
+
*
|
|
5
|
+
* Not read by steps 1-2 (design/forge+upload), which is why api-server's host
|
|
6
|
+
* passes a throwing stub. Its three consumers are the browser-driven steps in
|
|
7
|
+
* this package: `create-level-archetypes.ts`, `voxelize-level.ts` and
|
|
8
|
+
* `place-and-persist-level.ts`. The web lane wires a real WebSocket
|
|
9
|
+
* implementation (`game-play-agent`'s `transport-client.ts`); the CLI lane
|
|
10
|
+
* wires one over a headless-Chrome page bridge (`cli/src/forge/transport.ts`).
|
|
11
|
+
*/
|
|
12
|
+
export interface ForgeTransportOptions {
|
|
13
|
+
/** Default 3 (matches the web lane's WS retry policy). Pass 1 for
|
|
14
|
+
* expensive, non-idempotent work (a level bake) where a timed-out attempt
|
|
15
|
+
* must not be silently re-fired. */
|
|
16
|
+
maxRetries?: number;
|
|
17
|
+
/**
|
|
18
|
+
* When set, `timeoutMs` becomes an IDLE timeout: a message of this type
|
|
19
|
+
* (matching the command's requestId, when present) RE-ARMS the timer
|
|
20
|
+
* instead of counting toward it, so a long-but-active job (a chunk-by-chunk
|
|
21
|
+
* level bake that can legitimately run many minutes) is not killed
|
|
22
|
+
* mid-flight — only genuine silence for `timeoutMs` fails the attempt.
|
|
23
|
+
*/
|
|
24
|
+
progressType?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ForgeTransport {
|
|
27
|
+
/**
|
|
28
|
+
* Send `command` and await the first message of type `resultType` (matched
|
|
29
|
+
* on `requestId` when the command carries one). Resolves `null` on timeout
|
|
30
|
+
* or transport failure — never rejects for that case, so callers can tell
|
|
31
|
+
* "no answer" apart from "answered with an error".
|
|
32
|
+
*/
|
|
33
|
+
sendAndWait(command: Record<string, unknown>, resultType: string, timeoutMs: number, options?: ForgeTransportOptions): Promise<Record<string, unknown> | null>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=transport-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-types.d.ts","sourceRoot":"","sources":["../../src/pipeline/transport-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,WAAW,qBAAqB;IACpC;;yCAEqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,WAAW,CACT,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;CAC5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-types.js","sourceRoot":"","sources":["../../src/pipeline/transport-types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Step 4 — bake the level in the browser through the host's transport seam.
|
|
3
|
+
*
|
|
4
|
+
* Moved from game-play-agent's `pipeline.ts` verbatim, with the one
|
|
5
|
+
* `sendAndWaitForResult` call site turned into `deps.transport.sendAndWait`
|
|
6
|
+
* (same timeout, same `{ maxRetries: 1, progressType }` policy) and the logger
|
|
7
|
+
* turned into `deps.logger`.
|
|
8
|
+
*/
|
|
9
|
+
import type { ForgedWorkStore } from './forged-work-store.js';
|
|
10
|
+
import type { ForgeDeps } from './deps.js';
|
|
11
|
+
/**
|
|
12
|
+
* Idle timeout for the level bake: it runs chunk-by-chunk in the browser and
|
|
13
|
+
* emits VOXELIZE_GLB_AS_LEVEL_PROGRESS per chunk, each of which re-arms this
|
|
14
|
+
* timer (see the transport seam's `progressType`). So a slow-but-active bake is
|
|
15
|
+
* never killed mid-flight — only ~20 min of true silence (browser closed, tab
|
|
16
|
+
* frozen) fails the attempt.
|
|
17
|
+
*/
|
|
18
|
+
export declare const VOXELIZE_TIMEOUT_MS: number;
|
|
19
|
+
/**
|
|
20
|
+
* Bake the level. Tagged instance groups are skipped (objectAssetInstances).
|
|
21
|
+
* Single attempt: a timed-out bake must not be silently re-run.
|
|
22
|
+
*/
|
|
23
|
+
export declare function voxelizeLevel(store: ForgedWorkStore, deps: ForgeDeps): Promise<void>;
|
|
24
|
+
//# sourceMappingURL=voxelize-level.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voxelize-level.d.ts","sourceRoot":"","sources":["../../src/pipeline/voxelize-level.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAM3C;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,QAAiB,CAAC;AAIlD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAyD1F"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { requireArtifact } from './artifact-store.js';
|
|
2
|
+
import { ForgeStepError } from './errors.js';
|
|
3
|
+
import { forgeRequestId } from './request-id.js';
|
|
4
|
+
/**
|
|
5
|
+
* Idle timeout for the level bake: it runs chunk-by-chunk in the browser and
|
|
6
|
+
* emits VOXELIZE_GLB_AS_LEVEL_PROGRESS per chunk, each of which re-arms this
|
|
7
|
+
* timer (see the transport seam's `progressType`). So a slow-but-active bake is
|
|
8
|
+
* never killed mid-flight — only ~20 min of true silence (browser closed, tab
|
|
9
|
+
* frozen) fails the attempt.
|
|
10
|
+
*/
|
|
11
|
+
export const VOXELIZE_TIMEOUT_MS = 20 * 60 * 1000;
|
|
12
|
+
/** Progress message the bake emits per chunk; resets the idle timeout above. */
|
|
13
|
+
const VOXELIZE_PROGRESS_TYPE = 'VOXELIZE_GLB_AS_LEVEL_PROGRESS';
|
|
14
|
+
/**
|
|
15
|
+
* Bake the level. Tagged instance groups are skipped (objectAssetInstances).
|
|
16
|
+
* Single attempt: a timed-out bake must not be silently re-run.
|
|
17
|
+
*/
|
|
18
|
+
export async function voxelizeLevel(store, deps) {
|
|
19
|
+
const input = await requireArtifact(store, 'input');
|
|
20
|
+
const forge = await requireArtifact(store, 'forge');
|
|
21
|
+
const requestId = forgeRequestId();
|
|
22
|
+
const result = await deps.transport.sendAndWait({
|
|
23
|
+
type: 'VOXELIZE_GLB_AS_LEVEL',
|
|
24
|
+
requestId,
|
|
25
|
+
glbUrl: forge.glbUrl,
|
|
26
|
+
levelName: input.name,
|
|
27
|
+
// Carries the dungeon's `emissiveByColor` map (deriveVoxelizeOptions put
|
|
28
|
+
// it there): the bake matches those authored colors against the palette
|
|
29
|
+
// it actually produces and writes the level's emissive block.
|
|
30
|
+
options: forge.voxelizeOptions,
|
|
31
|
+
}, 'VOXELIZE_GLB_AS_LEVEL_RESULT', VOXELIZE_TIMEOUT_MS,
|
|
32
|
+
// Single attempt (a timed-out bake must not be silently re-fired), but the
|
|
33
|
+
// timeout is now idle-based: per-chunk progress re-arms it.
|
|
34
|
+
{ maxRetries: 1, progressType: VOXELIZE_PROGRESS_TYPE });
|
|
35
|
+
if (!result) {
|
|
36
|
+
throw new ForgeStepError('Level voxelization timed out. The editor (browser) must be open for baking. The designed GLB was uploaded and can be voxelized manually from the Assets tab.', forge.glbUrl);
|
|
37
|
+
}
|
|
38
|
+
if (!result.success) {
|
|
39
|
+
throw new ForgeStepError(`Level voxelization failed: ${result.error || result.message || 'Unknown error'}`, forge.glbUrl);
|
|
40
|
+
}
|
|
41
|
+
// Authored emissive colors the bake's palette never produced: an advisory
|
|
42
|
+
// (the glow simply doesn't appear), never a failure.
|
|
43
|
+
const unmatched = result.emissiveUnmatched ?? [];
|
|
44
|
+
const notes = unmatched.length > 0
|
|
45
|
+
? [`Emissive colors that matched no baked voxel: ${unmatched.join(', ')} — those surfaces do not glow`]
|
|
46
|
+
: [];
|
|
47
|
+
const bake = {
|
|
48
|
+
vwldUrl: result.vwldUrl,
|
|
49
|
+
bakedRecord: {
|
|
50
|
+
...result.assetRecord,
|
|
51
|
+
// The navmesh sidecar was compiled against THIS bake's level transform,
|
|
52
|
+
// so it rides on the level asset the engine installs at level load.
|
|
53
|
+
...(forge.navUrl ? { navUrl: forge.navUrl } : {}),
|
|
54
|
+
},
|
|
55
|
+
nonEmptyChunkCount: result.nonEmptyChunkCount,
|
|
56
|
+
totalLod0Leaves: result.totalLod0Leaves,
|
|
57
|
+
totalTrimeshTriangles: result.totalTrimeshTriangles,
|
|
58
|
+
...(notes.length > 0 ? { notes } : {}),
|
|
59
|
+
};
|
|
60
|
+
await store.put('level-bake', bake);
|
|
61
|
+
deps.logger.info({ jobId: store.jobId, vwldUrl: bake.vwldUrl, chunks: bake.nonEmptyChunkCount, navUrl: forge.navUrl, unmatched }, 'Level baked');
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=voxelize-level.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"voxelize-level.js","sourceRoot":"","sources":["../../src/pipeline/voxelize-level.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The world.json READ seam — the persist step (step 5) plans against the
|
|
3
|
+
* game's current world.json, but this package never opens a file. The host
|
|
4
|
+
* supplies the content; the host also applies the modifications the step
|
|
5
|
+
* returns (see `place-and-persist-level.ts`).
|
|
6
|
+
*
|
|
7
|
+
* ── Why TWO reads, and what each one actually wants ──────────────────────────
|
|
8
|
+
*
|
|
9
|
+
* game-play-agent's step 5 read TWO DIFFERENT files, and the difference is not
|
|
10
|
+
* cosmetic — so this seam preserves it rather than quietly picking one:
|
|
11
|
+
*
|
|
12
|
+
* - the starter-scatter plan read the SESSION copy
|
|
13
|
+
* (`readWorldData(runtimeContext)` → `tempGenrePath/world.json`, the file
|
|
14
|
+
* the agent's writer also writes),
|
|
15
|
+
* - the persist plan read the monorepo WORK copy
|
|
16
|
+
* (`readWorkWorldJson()` → `game/src/work/world.json`, the copy the
|
|
17
|
+
* creator/browser edits and that the session copy is copied wholesale OVER
|
|
18
|
+
* at cleanup — a `copyFileSync` behind one "did temp go completely empty"
|
|
19
|
+
* guard, not a merge, so whichever file a plan was computed against is the
|
|
20
|
+
* one whose content ends up surviving).
|
|
21
|
+
*
|
|
22
|
+
* What each SHOULD want is the same thing: the file the returned modifications
|
|
23
|
+
* will be applied to. Both plans are differential — the scatter plan emits
|
|
24
|
+
* `removeRoot` predicates over ids it saw, the persist plan emits a whole-array
|
|
25
|
+
* `setRoot ['environmentObjects']` computed from the instances it saw plus a
|
|
26
|
+
* purge over the assets it saw — so planning against a file OTHER than the
|
|
27
|
+
* write target either drops work that is only in the target or writes back
|
|
28
|
+
* content the target had already removed.
|
|
29
|
+
*
|
|
30
|
+
* That is an argument about what the code should do, not a licence to change
|
|
31
|
+
* what it does: in game-play-agent's web lane the two files genuinely differ
|
|
32
|
+
* mid-session (the creator writes assets into the work copy while the agent
|
|
33
|
+
* writes the session copy), so collapsing them here would silently change which
|
|
34
|
+
* content survives a forge — outside the scope of this extraction, and exactly
|
|
35
|
+
* the kind of change that would only show up in a shipped level. So the seam
|
|
36
|
+
* keeps both reads, named for what they FEED rather than for which file they
|
|
37
|
+
* happen to hit, and the host decides:
|
|
38
|
+
*
|
|
39
|
+
* - the web lane wires each to the exact file it reads today (no behaviour
|
|
40
|
+
* change),
|
|
41
|
+
* - a CLI project has exactly ONE world.json, so its adapter returns that
|
|
42
|
+
* same file from both — which is what "plan against the write target"
|
|
43
|
+
* means when there is only one target.
|
|
44
|
+
*
|
|
45
|
+
* Both reads DEGRADE deliberately and that degradation is part of the contract
|
|
46
|
+
* (see each method): a game with no world.json, or an unparseable one, must
|
|
47
|
+
* still be able to forge a level — the persist plan then simply plans against
|
|
48
|
+
* an empty world, and the scatter plan finds nothing to strip. Neither is a
|
|
49
|
+
* failure the user could act on, and both cost a paid 4-8 minute forge.
|
|
50
|
+
*
|
|
51
|
+
* Asynchronous on purpose: a host that keeps world.json in object storage (the
|
|
52
|
+
* CLI lane's api-server) must not be forced to pretend the read is local.
|
|
53
|
+
*/
|
|
54
|
+
import type { WorldJsonShape } from './world-json-types.js';
|
|
55
|
+
export interface ForgeWorldJsonSource {
|
|
56
|
+
/**
|
|
57
|
+
* The world.json the persist plan is computed against — the level registry,
|
|
58
|
+
* the existing instances the level-scoped replace preserves, and the assets
|
|
59
|
+
* the placeholder purge considers.
|
|
60
|
+
*
|
|
61
|
+
* Returns `{}` when the game has no world.json or it cannot be parsed: every
|
|
62
|
+
* consumer downstream has a sane empty-world path (no levels registry, no
|
|
63
|
+
* instances to keep, nothing to purge), so an unreadable file costs the user
|
|
64
|
+
* nothing but a fresh start.
|
|
65
|
+
*/
|
|
66
|
+
readForPersistPlan(): Promise<WorldJsonShape>;
|
|
67
|
+
/**
|
|
68
|
+
* The world.json the starter-scatter removal is planned against — the
|
|
69
|
+
* template's `default_tree_N` / `default-tree-N` instances and assets the
|
|
70
|
+
* forged level orphans.
|
|
71
|
+
*
|
|
72
|
+
* Returns `null` when unavailable: no read means no ids, which means no
|
|
73
|
+
* removals. Stripping nothing is always safe; the leftovers are cosmetic and
|
|
74
|
+
* removable in the creator afterwards.
|
|
75
|
+
*/
|
|
76
|
+
readForStarterScatterPlan(): Promise<Record<string, unknown> | null>;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=world-json-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-json-source.d.ts","sourceRoot":"","sources":["../../src/pipeline/world-json-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC;;;;;;;;;OASG;IACH,kBAAkB,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC;IAE9C;;;;;;;;OAQG;IACH,yBAAyB,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;CACtE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-json-source.js","sourceRoot":"","sources":["../../src/pipeline/world-json-source.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The world.json shapes the persist step speaks — the MODIFICATION format it
|
|
3
|
+
* returns, and the minimal read shape it plans against.
|
|
4
|
+
*
|
|
5
|
+
* Types only, no runtime: this package never reads or writes world.json. The
|
|
6
|
+
* host owns the file (game-play-agent's `utils/world-json-editor.ts` applies
|
|
7
|
+
* these against the session's copy; a CLI lane applies them against the one
|
|
8
|
+
* file its project has) and re-exports {@link WorldJsonModification} from here
|
|
9
|
+
* so there is exactly one definition of the format on both sides of the seam.
|
|
10
|
+
*
|
|
11
|
+
* Moved verbatim from game-play-agent's `utils/world-json-editor.ts` (the
|
|
12
|
+
* modification union) and `utils/level-registry.ts` (the read shape).
|
|
13
|
+
*/
|
|
14
|
+
/** Base modification type for `worldProfileData` paths. */
|
|
15
|
+
export interface BaseModification {
|
|
16
|
+
/** Path within worldProfileData to modify (e.g. `['skyboxUrl']` or `['animations']`). */
|
|
17
|
+
path: string[];
|
|
18
|
+
}
|
|
19
|
+
/** Set a single property value within worldProfileData. */
|
|
20
|
+
export interface SetModification extends BaseModification {
|
|
21
|
+
type: 'set';
|
|
22
|
+
value: unknown;
|
|
23
|
+
}
|
|
24
|
+
/** Push a value to an array within worldProfileData (creates the array if absent). */
|
|
25
|
+
export interface PushModification extends BaseModification {
|
|
26
|
+
type: 'push';
|
|
27
|
+
value: unknown;
|
|
28
|
+
}
|
|
29
|
+
/** Update an existing array item within worldProfileData. */
|
|
30
|
+
export interface UpdateModification extends BaseModification {
|
|
31
|
+
type: 'update';
|
|
32
|
+
/** Predicate to find the item to update. */
|
|
33
|
+
predicate: (item: unknown) => boolean;
|
|
34
|
+
/** New value to replace the found item, or an updater function. */
|
|
35
|
+
value: unknown | ((item: unknown) => unknown);
|
|
36
|
+
}
|
|
37
|
+
/** Remove items from an array within worldProfileData. */
|
|
38
|
+
export interface RemoveModification extends BaseModification {
|
|
39
|
+
type: 'remove';
|
|
40
|
+
/** Items matching this predicate are removed. */
|
|
41
|
+
predicate: (item: unknown) => boolean;
|
|
42
|
+
}
|
|
43
|
+
/** Upsert (update or insert) an array item within worldProfileData. */
|
|
44
|
+
export interface UpsertModification extends BaseModification {
|
|
45
|
+
type: 'upsert';
|
|
46
|
+
predicate: (item: unknown) => boolean;
|
|
47
|
+
value: unknown;
|
|
48
|
+
}
|
|
49
|
+
/** Base modification type for root-level paths (not within worldProfileData). */
|
|
50
|
+
export interface BaseRootModification {
|
|
51
|
+
/** Path from the JSON root (e.g. `['assets']`). */
|
|
52
|
+
path: string[];
|
|
53
|
+
}
|
|
54
|
+
/** Set a property at root level. */
|
|
55
|
+
export interface SetRootModification extends BaseRootModification {
|
|
56
|
+
type: 'setRoot';
|
|
57
|
+
value: unknown;
|
|
58
|
+
}
|
|
59
|
+
/** Push to an array at root level. */
|
|
60
|
+
export interface PushRootModification extends BaseRootModification {
|
|
61
|
+
type: 'pushRoot';
|
|
62
|
+
value: unknown;
|
|
63
|
+
}
|
|
64
|
+
/** Update an array item at root level. */
|
|
65
|
+
export interface UpdateRootModification extends BaseRootModification {
|
|
66
|
+
type: 'updateRoot';
|
|
67
|
+
predicate: (item: unknown) => boolean;
|
|
68
|
+
value: unknown | ((item: unknown) => unknown);
|
|
69
|
+
}
|
|
70
|
+
/** Remove items from an array at root level. */
|
|
71
|
+
export interface RemoveRootModification extends BaseRootModification {
|
|
72
|
+
type: 'removeRoot';
|
|
73
|
+
predicate: (item: unknown) => boolean;
|
|
74
|
+
}
|
|
75
|
+
/** Upsert (update or insert) an array item at root level. */
|
|
76
|
+
export interface UpsertRootModification extends BaseRootModification {
|
|
77
|
+
type: 'upsertRoot';
|
|
78
|
+
predicate: (item: unknown) => boolean;
|
|
79
|
+
value: unknown;
|
|
80
|
+
}
|
|
81
|
+
export type WorldJsonModification = SetModification | PushModification | UpdateModification | RemoveModification | UpsertModification | SetRootModification | PushRootModification | UpdateRootModification | RemoveRootModification | UpsertRootModification;
|
|
82
|
+
/** One entry of the multi-level registry (`worldProfileData.levels`). */
|
|
83
|
+
export interface LevelEntry {
|
|
84
|
+
id: string;
|
|
85
|
+
name: string;
|
|
86
|
+
vwldAssetId: string;
|
|
87
|
+
spawnPoints?: Array<Record<string, unknown>>;
|
|
88
|
+
overrides?: Record<string, unknown>;
|
|
89
|
+
}
|
|
90
|
+
/** The parts of world.json the persist plan reads. Everything else passes through untouched. */
|
|
91
|
+
export interface WorldJsonShape {
|
|
92
|
+
worldProfileData?: {
|
|
93
|
+
levels?: LevelEntry[];
|
|
94
|
+
startLevelId?: string;
|
|
95
|
+
voxelUrl?: string | null;
|
|
96
|
+
spawnPoints?: Array<Record<string, unknown>>;
|
|
97
|
+
playerSpawnPosition?: Record<string, unknown>;
|
|
98
|
+
[key: string]: unknown;
|
|
99
|
+
};
|
|
100
|
+
assets?: Array<Record<string, unknown>>;
|
|
101
|
+
environmentObjects?: Array<Record<string, unknown>>;
|
|
102
|
+
[key: string]: unknown;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=world-json-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-json-types.d.ts","sourceRoot":"","sources":["../../src/pipeline/world-json-types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The world.json shapes the persist step speaks — the MODIFICATION format it
|
|
3
|
+
* returns, and the minimal read shape it plans against.
|
|
4
|
+
*
|
|
5
|
+
* Types only, no runtime: this package never reads or writes world.json. The
|
|
6
|
+
* host owns the file (game-play-agent's `utils/world-json-editor.ts` applies
|
|
7
|
+
* these against the session's copy; a CLI lane applies them against the one
|
|
8
|
+
* file its project has) and re-exports {@link WorldJsonModification} from here
|
|
9
|
+
* so there is exactly one definition of the format on both sides of the seam.
|
|
10
|
+
*
|
|
11
|
+
* Moved verbatim from game-play-agent's `utils/world-json-editor.ts` (the
|
|
12
|
+
* modification union) and `utils/level-registry.ts` (the read shape).
|
|
13
|
+
*/
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=world-json-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-json-types.js","sourceRoot":"","sources":["../../src/pipeline/world-json-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* World-space terrain height grid — the bridge that lets objects placed AFTER
|
|
3
|
+
* a forge (e.g. collectibles the coding/asset agent scatters via the voxel CLI)
|
|
4
|
+
* sit on the forged surface instead of the world floor.
|
|
5
|
+
*
|
|
6
|
+
* The forge knows each prop's exact ground height because it samples its own
|
|
7
|
+
* GLB-space `Heightfield` and maps the result to world space with the level
|
|
8
|
+
* transform (see `forge-world.ts`). Agent-driven placements happen later, in a
|
|
9
|
+
* separate process, with no access to that in-memory heightfield — and a
|
|
10
|
+
* placement raycast can't help because the freshly-forged terrain only loads
|
|
11
|
+
* after the session reload. So the forge persists THIS grid (a sidecar next to
|
|
12
|
+
* world.json); the CLI samples it to resolve `placeOnTerrain` Y the same way
|
|
13
|
+
* the forge resolved its props.
|
|
14
|
+
*
|
|
15
|
+
* The grid is the carved heightfield expressed in WORLD coordinates. Because the
|
|
16
|
+
* level transform is affine and axis-aligned (uniform scale + translate, no
|
|
17
|
+
* rotation — see `marker-transform.ts`), a GLB-regular grid maps to a
|
|
18
|
+
* world-regular grid, so the conversion is a per-vertex scale/translate.
|
|
19
|
+
*
|
|
20
|
+
* The GRID and its SAMPLER live on the publishable side, because the host that
|
|
21
|
+
* writes the sidecar and the tools that read it are CLIENTS of the forge, not
|
|
22
|
+
* part of it. The producer — `heightfieldToWorldGrid`, which needs the forge's
|
|
23
|
+
* own heightfield and level transform — stays with the geometry, in the private
|
|
24
|
+
* package's module of the same name.
|
|
25
|
+
*/
|
|
26
|
+
/** Sidecar filename written next to world.json. The CLI hardcodes the same name. */
|
|
27
|
+
export declare const FORGED_HEIGHTFIELD_FILE = "forged-heightfield.json";
|
|
28
|
+
export interface ForgedHeightGrid {
|
|
29
|
+
/** World coordinate of vertex (0, 0). */
|
|
30
|
+
minX: number;
|
|
31
|
+
minZ: number;
|
|
32
|
+
/** World meters per cell. */
|
|
33
|
+
cellSize: number;
|
|
34
|
+
/** Cells along X / Z. Vertex counts are nx+1 / nz+1. */
|
|
35
|
+
nx: number;
|
|
36
|
+
nz: number;
|
|
37
|
+
/** Row-major world-space heights, index = j * (nx + 1) + i, length (nx+1)*(nz+1). */
|
|
38
|
+
heights: number[];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Bilinear height sample in world space, clamped to the grid edge. Mirrors
|
|
42
|
+
* `heightfield.ts` `heightAt` so the CLI's sampler and this one agree.
|
|
43
|
+
*/
|
|
44
|
+
export declare function sampleWorldHeight(g: ForgedHeightGrid, x: number, z: number): number;
|
|
45
|
+
//# sourceMappingURL=terrain-height-grid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terrain-height-grid.d.ts","sourceRoot":"","sources":["../src/terrain-height-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,oFAAoF;AACpF,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAEjE,MAAM,WAAW,gBAAgB;IAC/B,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,qFAAqF;IACrF,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBnF"}
|