/** * What an object voxel sculpt has to write into world.json. * * The engine bakes the edited object, uploads the new `.vxl` itself, and posts `VOXEL_ASSET_SAVED` * with the asset record. It mutates only its own in-memory copy — `EditorManager` says why at the * post site: "Mutating gameData alone would leave the new url in memory only, and the next load * would fetch the legacy file again as if nothing had happened." Persisting it is the host's job, * and this builds the modifications that do it. * * Pure and filesystem-free, like `save.ts`: the shapes here come off a postMessage from another * origin, so they are treated as untrusted input and tested without a project on disk. * * ── Why an updater rather than a straight upsert ───────────────────────────────────────────── * * The Creator replaces the whole asset record, and that is safe THERE: its in-memory `gameData` is * the document. Here it is not. `bitmagic dev`'s defining condition is that the creator's agent * edits `src/work/world.json` concurrently, and the engine's copy was read once at `LOAD_GAME` — * so a field the agent added since (a `light`, a `collision`, a regenerated `fitBox`) is absent * from the record coming back, and writing that record wholesale would silently revert it. Exactly * the hazard `game/docs/editor-host-contract.md` gives for keeping `saveModifications` off the * host interface. So an existing asset is MERGED field by field, and only a genuinely new one is * pushed whole. */ import type { WorldJsonModification } from '@bitmagic/world-forger/pipeline/index.js'; import { type JsonObject } from '../project/world-json.js'; /** The engine's `VOXEL_ASSET_SAVED`, as far as this cares. */ export interface VoxelSavePayload { /** The asset record the engine just wrote its new url onto. Useless without an id. */ asset?: unknown; /** Instances the save created — a make-unique produces one. */ environmentObjects?: unknown; } export interface VoxelSavePlan { modifications: WorldJsonModification[]; assetId: string; assetName: string; /** True when the asset was already in world.json, so its other fields were merged rather than lost. */ merged: boolean; } /** * Returns null when there is nothing safe to write. * * An asset with no id is the one shape that must never be guessed at: the engine has already * uploaded the bytes and reported success, so a host that shrugs here produces a save that "worked" * and reaches no file. The caller turns this into a loud refusal instead. */ export declare function buildVoxelSaveModifications(payload: VoxelSavePayload, world: JsonObject): VoxelSavePlan | null;