@idosgames/mcp 0.1.13 → 0.1.14
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/package.json +1 -1
- package/registry/host.json +6 -6
- package/registry/index.json +95 -23
- package/registry/modules/board-game.json +2 -2
- package/registry/modules/game-hud.json +2 -2
- package/registry/modules/idle-rpg.json +2 -2
- package/registry/modules/voxelcraft.json +1399 -79
- package/registry/modules/workshop.json +66 -0
- package/registry/skills/ai-generation-system.json +1 -1
- package/registry/skills/analytics-events.json +6 -0
- package/registry/skills/cloud-code.json +2 -2
- package/registry/skills/data-collections.json +6 -0
- package/registry/skills/experiments-system.json +6 -0
- package/registry/skills/idosgames-module-contract.json +2 -2
- package/registry/skills/idosgames-project-structure.json +1 -1
- package/registry/skills/voxelcraft-worlds.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "idosgames-project-structure",
|
|
3
3
|
"description": "The layout standard for an iDosGames game project (host shell + composable modules) and how it grows without turning into a mess: where a new feature goes (a catalog module, an existing module, or a new module), the folder layout inside a module, module boundaries (no cross-module imports), the game's shared code in src/shared/, module.meta.json, src/modules.ts being platform-generated, idos.modules.lock.json and customized catalog modules, file size, and project documentation (IDOS.md vs docs/feature-history/). Use this BEFORE creating a module, creating or changing src/shared/, moving code between files or folders, adding a system that spans several modules, or writing project documentation.",
|
|
4
|
-
"content": "---\nname: idosgames-project-structure\ndescription: >-\n The layout standard for an iDosGames game project (host shell + composable modules) and how it\n grows without turning into a mess: where a new feature goes (a catalog module, an existing module,\n or a new module), the folder layout inside a module, module boundaries (no cross-module imports),\n the game's shared code in src/shared/, module.meta.json, src/modules.ts being platform-generated,\n idos.modules.lock.json and customized catalog modules, file size, and project documentation\n (IDOS.md vs docs/feature-history/). Use this BEFORE creating a module, creating or changing\n src/shared/, moving code between files or folders, adding a system that spans several modules, or\n writing project documentation.\n---\n\n# Project structure (iDosGames game projects)\n\n## The project at a glance\n\n```\nIDOS.md project guide every agent reads first — short, evergreen\nAGENTS.md, CLAUDE.md pointers to IDOS.md for external tools (Cursor, Codex, Claude Code…)\nidos.modules.lock.json platform-owned: catalog modules, their versions and file fingerprints\ndocs/feature-history/ one file per game system + README.md index\npackage.json · vite.config.ts · tsconfig.json · index.html\nsrc/\n main.tsx the host: ONE SDK client + mountHost(...)\n modules.ts the composition list — generated by the platform\n idos.title.ts the project's identity — generated, never edit\n config.ts · env.ts title / build-key resolution\n LoginScreen.tsx the login screen — restyle freely\n shared/ optional: code two or more of THIS game's modules need\n ui/ types/ utils/\n modules/\n <id>/ one folder per module: catalog templates, catalog features, the game's own\n```\n\nEverything the game does lives in a module. Ready-made catalog modules and the game's own modules\nshare the one `src/modules/` folder — where a module came from is recorded in\n`idos.modules.lock.json`, not in its path, because a catalog template becomes the creator's code the\nmoment they start changing it. `src/shared/` is the one place for code several of the game's own\nmodules use; there is no project-level `utils/` or `components/` besides it.\n\n## Where does a new feature go?\n\nDecide in this order:\n\n1. **The catalog already has it** → install that module (the AI editor's InstallModule, or\n `get_module` over MCP) and adapt it. Don't rebuild what exists.\n2. **It extends an existing module's gameplay** (a new tile type in the board game, a new enemy in\n the idle RPG) → change that module, inside its folder.\n3. **It is its own mode, screen or system** (a shop, a clan screen, a mini-game, a quest board) → a\n **new module** `src/modules/<feature-id>/`.\n4. **It is chrome for every mode.** Balances, the wallet, the status line, Log out / player ID are\n shared-UI roles → a module that **provides** them (`sharedUi: { provides: [...] }` in module.ts):\n install `game-hud` from the catalog, or change it; templates hide their own copies by\n themselves. Other UI every mode shows (a global menu) → a panel with `activeOnly: false` in a\n no-scene, no-route module (`type: \"app\"`, `engine: \"dom\"`). See **idosgames-compose-modules**.\n5. **It is code two or more of the game's modules need** (a shared type, the game's UI kit, a\n formatting helper) → `src/shared/` (see below).\n\nBetween 2 and 3: if it would get its own nav tab, or could be switched off on its own, it is its own\nmodule.\n\n## Inside a module\n\n```\nsrc/modules/<id>/\n index.ts export { <camelCaseId>Module } from \"./module\";\n module.ts defineModule({ id, meta, setup(ctx) { … } })\n module.meta.json manifest: type, summary, provides, tags, version, author\n components/ React panels and UI pieces\n game/ engine and simulation (engine subfolders are fine: game/phaser/, game/three/)\n data/ static tables and tuning (levels, tiles, item lists)\n react/ hooks, contexts, the scene↔panel controller bridge\n```\n\n- The folder id is kebab-case `[a-z0-9-]`, at most 64 characters. The export name is derived from it\n — `daily-quests` → `dailyQuestsModule` — and the platform registers the module by that exact name.\n- Create only the folders you need; a small module can be `index.ts` + `module.ts` + one component.\n- `voxelcraft` is a ported vanilla game with its own layout — leave it as it is. New modules follow\n the layout above.\n\n### module.meta.json\n\n```json\n{\n \"id\": \"daily-quests\",\n \"type\": \"feature\",\n \"summary\": \"One-line pitch shown in the Modules dialog.\",\n \"description\": \"What it does, in a few sentences.\",\n \"provides\": [\"daily quest board\", \"streak rewards\"],\n \"tags\": [\"quests\", \"retention\"],\n \"version\": \"0.1.0\",\n \"author\": { \"name\": \"…\" },\n \"events\": {\n \"emits\": [\n {\n \"topic\": \"daily-quests:quest-completed@1\",\n \"when\": \"a quest's reward was claimed\",\n \"payload\": { \"questId\": \"string\" }\n }\n ],\n \"listens\": [\n {\n \"topic\": \"idle-rpg:character-upgraded@1\",\n \"why\": \"progress 'level a hero' quests\"\n }\n ]\n }\n}\n```\n\n`type` is `template` (a complete game to start from) or `feature` (a capability added to a game).\n`events` declares every `defineTopic(...)` the module uses — its own topics under `emits` (with the\nsame payload descriptor it passes to `shape()`), other modules' under `listens`; omit it when the\nmodule has none.\n`provides` is what an agent matches a request against — keep it accurate. Every module carries this\nfile, the game's own included: it is how a module shows up in the Modules dialog, and what lets it be\npublished to the shared catalog for other creators later.\n\n### Register it\n\n`src/modules.ts` has exactly this shape — imports plus the array, nothing else:\n\n```ts\nimport type { Module } from \"@idosgames/module-sdk\";\nimport { boardGameModule } from \"./modules/board-game\";\nimport { dailyQuestsModule } from \"./modules/daily-quests\";\n\nexport const modules: Module[] = [boardGameModule, dailyQuestsModule];\n```\n\nThe platform regenerates this file whenever modules are installed or removed; any other code in it is\nlost. Array order is mount order (and nav order).\n\n## Module boundaries\n\nA module imports only:\n\n- files inside its own folder;\n- `src/shared/` — the game's shared code;\n- npm packages (`@idosgames/*`, `react`, `three`, `phaser`, …).\n\nNever another module's files (`../board-game/…`) and never host files (`../../main`, `../../config`).\nModules are mixed into different games — a module that reaches into its neighbours breaks the moment\none of them is removed or replaced. Cooperate instead through:\n\n- **Durable shared state** (currency, inventory, characters, progress) → the ONE SDK client every\n module receives as `ctx.client`. Gold earned in one mode is spendable in another with no plumbing.\n- **Live signals** → `ctx.events` with typed topics `<module-id>:<event>@<major>`\n (`defineTopic(\"idle-rpg:character-upgraded@1\", shape({ … }))`), declared in `module.meta.json`.\n The emitter doesn't know who listens; a listener keeps its own copy of the topic. Topics shared by\n several of THIS game's own modules can live in `src/shared/events/` and be imported on both sides.\n- **Code several modules need** → `src/shared/`.\n- **Logic that must be shared and trusted** → it belongs on the server (SDK services, CloudCode),\n not in a client file.\n\n## src/shared/ — the game's shared code\n\nFor code that two or more of THIS game's own modules need.\n\n- **Created on demand.** Code only one module needs stays inside that module; move it to\n `src/shared/` when a second module needs it, not in advance.\n- **One-way dependency.** Modules import from `src/shared/`; `src/shared/` NEVER imports a module.\n Otherwise every module using the shared code silently drags another module in with it.\n- **No game state, no game logic.** Types, constants, the game's UI components and theme, pure\n helpers. Progress and data go through the SDK client, signals through `ctx.events`.\n- **Organised by purpose:** `src/shared/ui/`, `src/shared/types/`, `src/shared/utils/` — not one pile.\n- **Catalog modules never depend on it.** A module that ships in the catalog must install into any\n game, so it is fully self-contained. A game's own module that imports `src/shared/` is tied to this\n game — fine for your own game; when you publish such a module, its shared code is copied into it\n (the Modules dialog marks these modules \"uses shared code\").\n\n## Catalog modules you change\n\nA module installed from the catalog is source in your project — change it freely. At install the\nplatform records its file fingerprints in `idos.modules.lock.json`; once any file differs, the module\ncounts as **customized**, and updating it from the catalog is refused until the user confirms\noverwriting their changes in the Modules dialog. An agent never forces that overwrite. Write down\nnon-obvious customizations in `docs/feature-history/` so the next person knows why the module differs\nfrom the catalog.\n\n## Files\n\n- Keep files focused. When a change adds a new responsibility to a file past ~400 lines, move that\n part into its own file — as part of that change, not as a separate drive-by refactor.\n- `src/idos.title.ts` and `idos.modules.lock.json` are platform-owned: never edit them.\n\n## Documentation\n\n- **IDOS.md** — the always-loaded guide: what the game is (\"This game\"), layout, conventions,\n \"never do X\" constraints, lasting user preferences. Short and evergreen; change a line when a fact\n changes. The \"Installed modules\" block is maintained by the platform.\n- **docs/feature-history/<slug>.md** — one file per game system or feature: what was built, why,\n and the decisions and constraints the code does not show. Update the system's file when it\n changes, and give every file ONE line in `docs/feature-history/README.md`:\n `- [Title](slug.md) — one-line gist`.\n- Never append a feature write-up to IDOS.md. It is loaded on every run, so every paragraph there is\n paid for by all future work — that is exactly how instruction files bloat.\n- Feature-history entries are not loaded automatically: open the relevant one before changing that\n system.\n",
|
|
4
|
+
"content": "---\nname: idosgames-project-structure\ndescription: >-\n The layout standard for an iDosGames game project (host shell + composable modules) and how it\n grows without turning into a mess: where a new feature goes (a catalog module, an existing module,\n or a new module), the folder layout inside a module, module boundaries (no cross-module imports),\n the game's shared code in src/shared/, module.meta.json, src/modules.ts being platform-generated,\n idos.modules.lock.json and customized catalog modules, file size, and project documentation\n (IDOS.md vs docs/feature-history/). Use this BEFORE creating a module, creating or changing\n src/shared/, moving code between files or folders, adding a system that spans several modules, or\n writing project documentation.\n---\n\n# Project structure (iDosGames game projects)\n\n## The project at a glance\n\n```\nIDOS.md project guide every agent reads first — short, evergreen\nAGENTS.md, CLAUDE.md pointers to IDOS.md for external tools (Cursor, Codex, Claude Code…)\nidos.modules.lock.json platform-owned: catalog modules, their versions and file fingerprints\ndocs/feature-history/ one file per game system + README.md index\npackage.json · vite.config.ts · tsconfig.json · index.html\nsrc/\n main.tsx the host: ONE SDK client + mountHost(...)\n modules.ts the composition list — generated by the platform\n idos.title.ts the project's identity — generated, never edit\n config.ts title / environment resolution — platform-owned, never edit\n env.ts build-time values (.env.local)\n LoginScreen.tsx the login screen — restyle freely\n shared/ optional: code two or more of THIS game's modules need\n ui/ types/ utils/\n modules/\n <id>/ one folder per module: catalog templates, catalog features, the game's own\n```\n\nEverything the game does lives in a module. Ready-made catalog modules and the game's own modules\nshare the one `src/modules/` folder — where a module came from is recorded in\n`idos.modules.lock.json`, not in its path, because a catalog template becomes the creator's code the\nmoment they start changing it. `src/shared/` is the one place for code several of the game's own\nmodules use; there is no project-level `utils/` or `components/` besides it.\n\n## Where does a new feature go?\n\nDecide in this order:\n\n1. **The catalog already has it** → install that module (the AI editor's InstallModule, or\n `get_module` over MCP) and adapt it. Don't rebuild what exists.\n2. **It extends an existing module's gameplay** (a new tile type in the board game, a new enemy in\n the idle RPG) → change that module, inside its folder.\n3. **It is its own mode, screen or system** (a shop, a clan screen, a mini-game, a quest board) → a\n **new module** `src/modules/<feature-id>/`.\n4. **It is chrome for every mode.** Balances, the wallet, the status line, Log out / player ID are\n shared-UI roles → a module that **provides** them (`sharedUi: { provides: [...] }` in module.ts):\n install `game-hud` from the catalog, or change it; templates hide their own copies by\n themselves. Other UI every mode shows (a global menu) → a panel with `activeOnly: false` in a\n no-scene, no-route module (`type: \"app\"`, `engine: \"dom\"`). See **idosgames-compose-modules**.\n5. **It is code two or more of the game's modules need** (a shared type, the game's UI kit, a\n formatting helper) → `src/shared/` (see below).\n\nBetween 2 and 3: if it would get its own nav tab, or could be switched off on its own, it is its own\nmodule.\n\n## Inside a module\n\n```\nsrc/modules/<id>/\n index.ts export { <camelCaseId>Module } from \"./module\";\n module.ts defineModule({ id, meta, setup(ctx) { … } })\n module.meta.json manifest: type, summary, provides, tags, version, author\n components/ React panels and UI pieces\n game/ engine and simulation (engine subfolders are fine: game/phaser/, game/three/)\n data/ static tables and tuning (levels, tiles, item lists)\n react/ hooks, contexts, the scene↔panel controller bridge\n```\n\n- The folder id is kebab-case `[a-z0-9-]`, at most 64 characters. The export name is derived from it\n — `daily-quests` → `dailyQuestsModule` — and the platform registers the module by that exact name.\n- Create only the folders you need; a small module can be `index.ts` + `module.ts` + one component.\n- `voxelcraft` is a ported vanilla game with its own layout — leave it as it is. New modules follow\n the layout above.\n\n### module.meta.json\n\n```json\n{\n \"id\": \"daily-quests\",\n \"type\": \"feature\",\n \"summary\": \"One-line pitch shown in the Modules dialog.\",\n \"description\": \"What it does, in a few sentences.\",\n \"provides\": [\"daily quest board\", \"streak rewards\"],\n \"tags\": [\"quests\", \"retention\"],\n \"version\": \"0.1.0\",\n \"author\": { \"name\": \"…\" },\n \"events\": {\n \"emits\": [\n {\n \"topic\": \"daily-quests:quest-completed@1\",\n \"when\": \"a quest's reward was claimed\",\n \"payload\": { \"questId\": \"string\" }\n }\n ],\n \"listens\": [\n {\n \"topic\": \"idle-rpg:character-upgraded@1\",\n \"why\": \"progress 'level a hero' quests\"\n }\n ]\n }\n}\n```\n\n`type` is `template` (a complete game to start from) or `feature` (a capability added to a game).\n`events` declares every `defineTopic(...)` the module uses — its own topics under `emits` (with the\nsame payload descriptor it passes to `shape()`), other modules' under `listens`; omit it when the\nmodule has none.\n`provides` is what an agent matches a request against — keep it accurate. Every module carries this\nfile, the game's own included: it is how a module shows up in the Modules dialog, and what lets it be\npublished to the shared catalog for other creators later.\n\n### Register it\n\n`src/modules.ts` has exactly this shape — imports plus the array, nothing else:\n\n```ts\nimport type { Module } from \"@idosgames/module-sdk\";\nimport { boardGameModule } from \"./modules/board-game\";\nimport { dailyQuestsModule } from \"./modules/daily-quests\";\n\nexport const modules: Module[] = [boardGameModule, dailyQuestsModule];\n```\n\nThe platform regenerates this file whenever modules are installed or removed; any other code in it is\nlost. Array order is mount order (and nav order).\n\n## Module boundaries\n\nA module imports only:\n\n- files inside its own folder;\n- `src/shared/` — the game's shared code;\n- npm packages (`@idosgames/*`, `react`, `three`, `phaser`, …).\n\nNever another module's files (`../board-game/…`) and never host files (`../../main`, `../../config`).\nModules are mixed into different games — a module that reaches into its neighbours breaks the moment\none of them is removed or replaced. Cooperate instead through:\n\n- **Durable shared state** (currency, inventory, characters, progress) → the ONE SDK client every\n module receives as `ctx.client`. Gold earned in one mode is spendable in another with no plumbing.\n- **Live signals** → `ctx.events` with typed topics `<module-id>:<event>@<major>`\n (`defineTopic(\"idle-rpg:character-upgraded@1\", shape({ … }))`), declared in `module.meta.json`.\n The emitter doesn't know who listens; a listener keeps its own copy of the topic. Topics shared by\n several of THIS game's own modules can live in `src/shared/events/` and be imported on both sides.\n- **Code several modules need** → `src/shared/`.\n- **Logic that must be shared and trusted** → it belongs on the server (SDK services, CloudCode),\n not in a client file.\n\n## src/shared/ — the game's shared code\n\nFor code that two or more of THIS game's own modules need.\n\n- **Created on demand.** Code only one module needs stays inside that module; move it to\n `src/shared/` when a second module needs it, not in advance.\n- **One-way dependency.** Modules import from `src/shared/`; `src/shared/` NEVER imports a module.\n Otherwise every module using the shared code silently drags another module in with it.\n- **No game state, no game logic.** Types, constants, the game's UI components and theme, pure\n helpers. Progress and data go through the SDK client, signals through `ctx.events`.\n- **Organised by purpose:** `src/shared/ui/`, `src/shared/types/`, `src/shared/utils/` — not one pile.\n- **Catalog modules never depend on it.** A module that ships in the catalog must install into any\n game, so it is fully self-contained. A game's own module that imports `src/shared/` is tied to this\n game — fine for your own game; when you publish such a module, its shared code is copied into it\n (the Modules dialog marks these modules \"uses shared code\").\n\n## Catalog modules you change\n\nA module installed from the catalog is source in your project — change it freely. At install the\nplatform records its file fingerprints in `idos.modules.lock.json`; once any file differs, the module\ncounts as **customized**, and updating it from the catalog is refused until the user confirms\noverwriting their changes in the Modules dialog. An agent never forces that overwrite. Write down\nnon-obvious customizations in `docs/feature-history/` so the next person knows why the module differs\nfrom the catalog.\n\n## Files\n\n- Keep files focused. When a change adds a new responsibility to a file past ~400 lines, move that\n part into its own file — as part of that change, not as a separate drive-by refactor.\n- `src/idos.title.ts`, `src/config.ts` and `idos.modules.lock.json` are platform-owned: never edit\n them. The platform's build packs its own `idos.title.ts` and `config.ts` over the project's, so\n an edit there never reaches the game anyway.\n- Never read the title or the environment from the URL: a link is editable by anyone. Use\n `client.titleID` (or `ctx.titleId` in a module's `setup()`).\n\n## Documentation\n\n- **IDOS.md** — the always-loaded guide: what the game is (\"This game\"), layout, conventions,\n \"never do X\" constraints, lasting user preferences. Short and evergreen; change a line when a fact\n changes. The \"Installed modules\" block is maintained by the platform.\n- **docs/feature-history/<slug>.md** — one file per game system or feature: what was built, why,\n and the decisions and constraints the code does not show. Update the system's file when it\n changes, and give every file ONE line in `docs/feature-history/README.md`:\n `- [Title](slug.md) — one-line gist`.\n- Never append a feature write-up to IDOS.md. It is loaded on every run, so every paragraph there is\n paid for by all future work — that is exactly how instruction files bloat.\n- Feature-history entries are not loaded automatically: open the relevant one before changing that\n system.\n",
|
|
5
5
|
"references": []
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voxelcraft-worlds",
|
|
3
3
|
"description": "Build VoxelCraft worlds from a recipe (WorldSpec): from up to 4 reference pictures and a text description, with InApp AI (client.ai, title feature \"voxel-world\") or without it, or written by hand / by an agent. The AI can recreate WHATEVER the pictures show — build objects out of coloured blocks (a plane, a car, a ship, a statue: models made of boxes, cylinders, cones, spheres, lines, with mirror symmetry), rebuild landscapes (a hand-drawn terrain sketch, rivers, biome zones) and combine them into scenes. Covers the WorldSpec JSON format, setting a project's start world (src/worlds/startWorld.ts), the \"Create world\" screen and \"My worlds\" autosave, configuring the \"voxel-world\" AI feature for a title (system prompt, vision model, images, limits, price), and the agent actions generateWorld / loadWorldSpec. Use this whenever the user works on the voxelcraft module and wants a world from a picture, an object from a photo built in blocks, a landscape recreated, a generated / custom / themed voxel map, AI world generation, a start world for their game, or touches WorldSpec, SpecGenerator, VoxelModel, rasterizeModel, parseWorldSpec, buildSpecWithoutAI, CreateWorldUI, createClientWorldAI or VOXEL_WORLD_SYSTEM_PROMPT — even if they don't name the module. Also covers sharing worlds through the Workshop (content type \"voxelcraft.world\").",
|
|
4
|
-
"content": "---\nname: voxelcraft-worlds\ndescription: >-\n Build VoxelCraft worlds from a recipe (WorldSpec): from up to 4 reference\n pictures and a text description, with InApp AI (client.ai, title feature\n \"voxel-world\") or without it, or written by hand / by an agent. The AI can\n recreate WHATEVER the pictures show — build objects out of coloured blocks\n (a plane, a car, a ship, a statue: models made of boxes, cylinders, cones,\n spheres, lines, with mirror symmetry), rebuild landscapes (a hand-drawn\n terrain sketch, rivers, biome zones) and combine them into scenes. Covers the\n WorldSpec JSON format, setting a project's start world\n (src/worlds/startWorld.ts), the \"Create world\" screen and \"My worlds\"\n autosave, configuring the \"voxel-world\" AI feature for a title (system\n prompt, vision model, images, limits, price), and the agent actions\n generateWorld / loadWorldSpec. Use this whenever the user works on the\n voxelcraft module and wants a world from a picture, an object from a photo\n built in blocks, a landscape recreated, a generated / custom / themed voxel\n map, AI world generation, a start world for their game, or touches\n WorldSpec, SpecGenerator, VoxelModel, rasterizeModel, parseWorldSpec,\n buildSpecWithoutAI, CreateWorldUI, createClientWorldAI or\n VOXEL_WORLD_SYSTEM_PROMPT — even if they don't name the module. Also covers\n sharing worlds through the Workshop (content type \"voxelcraft.world\").\n---\n\n# VoxelCraft worlds (WorldSpec)\n\nA VoxelCraft world is built by a **deterministic generator** (`SpecGenerator`)\nfrom a compact JSON **recipe** — the WorldSpec. The same recipe always gives the\nsame world, block for block. Nothing writes the world's blocks directly;\neverything that makes a world writes a recipe:\n\n| Path | Where | What it can do |\n| ------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |\n| **AI** — pictures + description | `createClientWorldAI(client)` → `client.ai.generateText(\"voxel-world\", …)` | anything the pictures show: objects built from blocks, landscapes, scenes |\n| **Without AI** | `buildSpecWithoutAI` — first picture read in the browser (`ImageAnalyzer`), text by keywords (ru/en) | terrain from a top-down map, style from a mood picture, prefab structures by keyword — **no objects from pictures** |\n| **By hand / by an agent** | write the JSON yourself | everything the format allows |\n\nAll paths meet in `parseWorldSpec()` — the only door into the game. It fills\ndefaults, clamps numbers, drops unknown structures, shapes and blocks. A recipe\nnever crashes the generator, wherever it came from.\n\n## The WorldSpec format\n\nWorld coordinates `x`, `z`, `x2`, `z2`, `radius`, river points are **fractions\nof the world (0..1)**: `x=0` west, `x=1` east, `z=0` north, `z=1` south. Every\nfield is optional.\n\n```jsonc\n{\n \"version\": 1,\n \"name\": \"Frost Keep\", // <= 60 chars\n \"description\": \"…\",\n \"seed\": 2026, // number or string; omitted = hash of name\n \"size\": { \"mode\": \"island\", \"widthChunks\": 16, \"depthChunks\": 16 },\n // mode: island | bounded (land to the edges, sea outside) | infinite\n // 4..48 chunks per side (16 blocks each); default island 16×16 = 256×256 blocks\n \"terrain\": {\n \"shape\": \"mountains\", // island | archipelago | continent | valley | mountains | plateau | flat\n \"baseHeight\": 30, // 6..48 — average land height (the world is 64 blocks high)\n \"amplitude\": 20,\n \"roughness\": 0.6,\n \"waterLevel\": 25,\n \"sketch\": { \"rows\": [\"0012345\", \"…\"] },\n // optional TOP-DOWN elevation map drawn by the AI: 4..32 rows (north→south) of 4..32 digits\n // (west→east): 0 deep water, 1 shallow, 2 shore, 3 lowland, 4 plain, 5 hills, 6 high hills,\n // 7 mountains, 8 high mountains, 9 peaks. Smoothed + natural detail; with a sketch the recipe\n // draws its own coastline (no island mask). A top-down PICTURE's heightmap wins over it.\n // heightmap / surfaceMap — filled by the game from a top-down picture; don't write by hand\n },\n \"zones\": [\n // <= 12; each point belongs to the nearest zone (weighted by radius)\n { \"x\": 0.5, \"z\": 0.5, \"radius\": 0.4, \"biome\": \"snow\" },\n // biome: meadow | forest | birch_forest | taiga | snow | desert | badlands | beach |\n // swamp | rocky | savanna | jungle\n // overrides: surface, subsurface (any solid block — even wool), trees 0..1,\n // treeType oak|birch|spruce|palm|dead|none, grass 0..1, flowers 0..1, height -20..20\n ],\n \"structures\": [\n // <= 40\n { \"type\": \"castle\", \"x\": 0.5, \"z\": 0.5, \"size\": \"large\", \"rotation\": 180 },\n // house | tower | castle | village | wall | road | bridge | ruins | pond | rock |\n // tree_cluster | pyramid | well | farm | model | river\n // size small|medium|large; rotation 0|90|180|270 (buildings: door north|east|south|west)\n { \"type\": \"road\", \"x\": 0.25, \"z\": 0.72, \"x2\": 0.5, \"z2\": 0.5 }, // wall/road/bridge: x2,z2\n {\n \"type\": \"river\",\n \"path\": [\n [0.8, 0.2],\n [0.5, 0.5],\n [0.1, 0.95],\n ],\n }, // 2..16 points, source → mouth\n {\n \"type\": \"model\",\n \"model\": \"airplane\",\n \"x\": 0.5,\n \"z\": 0.55,\n \"rotation\": 90,\n \"elevation\": 0,\n },\n ],\n \"models\": { \"airplane\": {/* see below */} }, // <= 4 models, each can be placed many times\n \"npcs\": [\n { \"name\": \"Björn\", \"role\": \"smith\", \"x\": 0.26, \"z\": 0.7, \"persona\": \"…\" },\n ],\n \"palette\": {\n \"wall\": \"planks\",\n \"roof\": \"dark_planks\",\n \"floor\": \"planks\",\n \"path\": \"dirt_path\",\n \"accent\": \"stone_bricks\",\n },\n \"ambience\": { \"timeOfDay\": \"dawn\" }, // dawn | day | sunset | night\n}\n```\n\n### Models — objects built from blocks\n\nThe AI builds an object (a plane, a car, a ship, a statue, a creature) from\n**shapes**, not block by block — a language model reasons well about\n\"fuselage = cylinder, wings = flat boxes, symmetric\", and a shape list stays\nshort. `rasterizeModel()` turns it into blocks.\n\n```jsonc\n\"airplane\": {\n \"size\": [40, 12, 33], // [length x, height y, width z], <= 64 × 40 × 64\n // model coords: x from the FRONT (x=0, the nose) back, y UP (y=0 on the ground), z left→right;\n // the middle plane is z = (width - 1) / 2. Later shapes overwrite earlier; \"air\" carves.\n \"parts\": [ // <= 200\n { \"shape\": \"cylinder\", \"axis\": \"x\", \"center\": [4, 4, 16], \"radius\": 2.5, \"length\": 30, \"block\": \"white_wool\" },\n // center = middle of the START cap; runs `length` blocks along +axis (negative = −axis)\n { \"shape\": \"cone\", \"axis\": \"x\", \"center\": [3, 4, 16], \"radius\": 2.5, \"radius2\": 0.6, \"length\": -4, \"block\": \"white_wool\" },\n { \"shape\": \"box\", \"from\": [14, 3, 1], \"to\": [19, 3, 13], \"block\": \"white_wool\", \"mirror\": \"z\" },\n // \"mirror\": \"x\" | \"z\" also draws the mirror copy across the middle — wings, wheels, lights\n { \"shape\": \"sphere\", \"center\": [x, y, z], \"radii\": [rx, ry, rz], \"block\": \"…\", \"hollow\": true },\n { \"shape\": \"line\", \"from\": [x, y, z], \"to\": [x, y, z], \"radius\": 0, \"block\": \"…\" },\n { \"shape\": \"box\", \"from\": [33, 9, 16], \"to\": [34, 11, 16], \"block\": \"air\" }\n ],\n \"voxels\": [[9, 5, 14, \"light_blue_wool\"]] // <= 400 single blocks for details\n}\n```\n\nPlaced by a `model` structure: centred at `x, z`, turned by `rotation`, its\nbottom on the ground under the centre (on water — on the surface), `elevation`\nlifts it (a plane in flight). The footprint is cleared (trees and bumps don't\ngrow through it). Model blocks written with Minecraft names are forgiven:\n`red_concrete`, `cyan_terracotta`, `minecraft:lime_wool` → our wool of that colour;\n`*_stained_glass` → glass. Anything unknown drops that shape.\n\n### Blocks\n\ngrass, dirt, stone, cobblestone, mossy_cobblestone, sand, red_sand, gravel,\nclay, snow, ice, sandstone, terracotta, stone_bricks, bricks, planks,\ndark_planks, oak_log, spruce_log, birch_log, glass, dirt_path, leaves,\nspruce_leaves, birch_leaves, and wool in 12 colours (white, red, orange,\nyellow, green, blue, light_blue, purple, pink, brown, gray, black — `red_wool`…).\n\nGood to know: a **village** already has houses facing a well, paths and (large)\na farm; a **bridge** needs water under it (`valley` shape, a river, a pond);\na **river** always flows downhill from its first point and cuts its bed through\nhills; keep structures inside `0.2..0.8` on an island; the player spawns on dry\nland nearest the centre, away from structures.\n\n## The project's start world\n\n`src/worlds/startWorld.ts` (in a project: `src/modules/voxelcraft/src/worlds/startWorld.ts`):\n\n```ts\nexport const START_WORLD: unknown = { name: \"Frost Keep\", terrain: { shape: \"mountains\" }, … };\n```\n\n`null` (default) = the classic seed-1337 noise world. A player with a saved\nworld continues it (\"My worlds\"). Easiest way to get a recipe: in the game,\n**Create world → Download recipe**, then paste the JSON as an object.\n\n## In the game\n\n- **Create world** (pause overlay): up to **4 pictures** (drop, click, Ctrl+V —\n e.g. one car from several angles), description, size (island 256 / large 512\n / infinite / custom), how to read the pictures:\n - **Образец для ИИ** (default) — the AI decides from the description what the\n pictures are (an object to build, a landscape to recreate, a style);\n - **Карта сверху** — the first picture is a top-down map: blue = water, land\n rises from the shore, pixel colour → nearest block (works without AI too);\n - **Настроение** — only climate, colours, materials.\n Then with or without AI, a **mini-map preview** (red dot = spawn), play /\n download / load recipe.\n- **My worlds**: every world autosaves to IndexedDB (every 30 s of play, on\n pause, before switching worlds); the last one reopens on the next launch.\n\n## AI: the \"voxel-world\" feature of the title\n\nThe AI option appears only when the title has a Text feature with key\n`voxel-world` (`client.ai.getDefinitions()`). Configure it in the dashboard\n(InApp AI page) or via the title-config MCP `save_ai`:\n\n- `Modality: \"Text\"` (every generation is a queued job the game polls — a world\n with models is a long answer, minutes; there is no Async switch any more).\n- `Model`: from `GetInAppAIModels`, with **image** among its input modalities\n (otherwise image requests are refused before any charge). Model quality =\n object quality: a strong vision model builds far better planes and cars.\n- `Behavior.SystemInstructions` = **`VOXEL_WORLD_SYSTEM_PROMPT`** from\n `src/ai/worldSpecPrompt.ts`, verbatim (a test keeps it in sync with the\n format). `HistoryMessages: 0`. **`MaxTokens` ≈ 48000** — models are the long part, and a\n reasoning model (GLM 5.3 Flash) spends part of the limit on thinking: at 16000 the\n first live run was cut off, the second took ~18000 output tokens (~1.6 credits).\n- `AllowImages: true`, **`MaxInputImages: 4`** or more — the game sends as many\n JPEGs (≤ 1568 px) as the feature allows; there is no platform cap, only what\n the model accepts.\n- `Safety.LockBehavior: true`, `MaxPromptChars: 1000` (the game trims the description).\n- `Limits` (`DailyCap`, `CooldownSeconds`) and `PriceOptions`.\n\n⚠ No JSON mode in the engine, and an answer cut off by `MaxTokens` comes back\n`completed` **and is charged**. The game detects a cut answer and builds the\nworld without AI instead — keep `MaxTokens` generous.\n\n`createClientWorldAI(client)` (`src/ai/worldAi.ts`): status from definitions,\none `relatedEntityID` per click (a dropped connection is retried with the same\nkey — no double charge), polling of the job, a job abandoned by closing\nthe screen is resumed next time, refusals mapped to player-facing messages, the\nplayer's chosen size enforced over the model's.\n\n## Sharing worlds in the Workshop\n\nVoxelCraft registers the content type **`voxelcraft.world`** (`src/workshop.ts`,\n`ctx.content.registerType` in `module.ts`). With the `workshop` module installed, players publish\nworlds from \"My worlds\" and open other players' worlds; the Workshop switches to the `voxelcraft`\nmode after opening.\n\n- `capture` publishes the world's **SaveData JSON** (role `main`, `application/json`) — the source\n plus the player's diffs, not the blocks — and a **webp preview** for recipe worlds (noise worlds\n have none). Metadata: `kind`, `seed`.\n- `open` parses the save, opens it in the running game (`openSharedWorld`) or stores it as a new\n local world.\n- Title config (dashboard → LiveOps → Workshop → Content types, or the title-config MCP):\n `ContentTypes[\"voxelcraft.world\"] = { DisplayName, Files: { main: { AllowedMimeTypes:\n[\"application/json\"], MaxBytes: … } } }`. Don't set `ThumbnailRequired` — noise worlds can't\n capture a preview. How to charge / gate access: **workshop-system**.\n\n## Agent debug surface\n\n`state().world`: `id`, `name`, `source` (`noise` | `spec`), `seed`, `size`,\n`shape`, `biomes`, `structures`, `fromPicture`, `spawnHint`; `createWorldOpen`.\nActions: `generateWorld { text, size?, seed? }` (without AI),\n`loadWorldSpec { spec }` (from a recipe object; returns `warnings`). The screen\nitself is plain DOM — read it and click it like a player.\n\n## Don't break\n\n- **Generators are part of the save format.** A save is `source + diffs`; the\n world is regenerated and the diffs land on it. Changing the output of\n `TerrainGenerator`, `SpecGenerator`, the structure builders or\n `rasterizeModel` corrupts every saved world — and every world **published in\n the Workshop**, which is the same SaveData. Both generators have golden\n fingerprint tests — a red one means \"you broke saves\", not \"update the numbers\".\n- **Block ids are append-only** (`registry/Blocks.ts`); new tiles go to the END\n of `TILE_ORDER` (`gfx/TextureAtlas.ts`).\n- The module takes **only types** from `@idosgames/core`; the client comes from\n the host (`ctx.client`).\n",
|
|
4
|
+
"content": "---\nname: voxelcraft-worlds\ndescription: >-\n Build VoxelCraft worlds from a recipe (WorldSpec): from up to 4 reference\n pictures and a text description, with InApp AI (client.ai, title feature\n \"voxel-world\") or without it, or written by hand / by an agent. The AI can\n recreate WHATEVER the pictures show — build objects out of coloured blocks\n (a plane, a car, a ship, a statue: models made of boxes, cylinders, cones,\n spheres, lines, with mirror symmetry), rebuild landscapes (a hand-drawn\n terrain sketch, rivers, biome zones) and combine them into scenes. Covers the\n WorldSpec JSON format, setting a project's start world\n (src/worlds/startWorld.ts), the \"Create world\" screen and \"My worlds\"\n autosave, configuring the \"voxel-world\" AI feature for a title (system\n prompt, vision model, images, limits, price), and the agent actions\n generateWorld / loadWorldSpec. Use this whenever the user works on the\n voxelcraft module and wants a world from a picture, an object from a photo\n built in blocks, a landscape recreated, a generated / custom / themed voxel\n map, AI world generation, a start world for their game, or touches\n WorldSpec, SpecGenerator, VoxelModel, rasterizeModel, parseWorldSpec,\n buildSpecWithoutAI, CreateWorldUI, createClientWorldAI or\n VOXEL_WORLD_SYSTEM_PROMPT — even if they don't name the module. Also covers\n sharing worlds through the Workshop (content type \"voxelcraft.world\").\n---\n\n# VoxelCraft worlds (WorldSpec)\n\nA VoxelCraft world is built by a **deterministic generator** (`SpecGenerator`)\nfrom a compact JSON **recipe** — the WorldSpec. The same recipe always gives the\nsame world, block for block. Nothing writes the world's blocks directly;\neverything that makes a world writes a recipe:\n\n| Path | Where | What it can do |\n| ------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |\n| **AI** — pictures + description | `createClientWorldAI(client)` → `client.ai.generateText(\"voxel-world\", …)` | anything the pictures show: objects built from blocks, landscapes, scenes |\n| **Without AI** | `buildSpecWithoutAI` — first picture read in the browser (`ImageAnalyzer`), text by keywords (ru/en) | terrain from a top-down map, style from a mood picture, prefab structures by keyword — **no objects from pictures** |\n| **By hand / by an agent** | write the JSON yourself | everything the format allows |\n\nAll paths meet in `parseWorldSpec()` — the only door into the game. It fills\ndefaults, clamps numbers, drops unknown structures, shapes and blocks. A recipe\nnever crashes the generator, wherever it came from.\n\n## The WorldSpec format\n\nWorld coordinates `x`, `z`, `x2`, `z2`, `radius`, river points are **fractions\nof the world (0..1)**: `x=0` west, `x=1` east, `z=0` north, `z=1` south. Every\nfield is optional.\n\n```jsonc\n{\n \"version\": 1,\n \"name\": \"Frost Keep\", // <= 60 chars\n \"description\": \"…\",\n \"seed\": 2026, // number or string; omitted = hash of name\n \"size\": { \"mode\": \"island\", \"widthChunks\": 16, \"depthChunks\": 16 },\n // mode: island | bounded (land to the edges, sea outside) | infinite\n // 4..48 chunks per side (16 blocks each); default island 16×16 = 256×256 blocks\n \"terrain\": {\n \"shape\": \"mountains\", // island | archipelago | continent | valley | mountains | plateau | flat\n \"baseHeight\": 30, // 6..48 — average land height (the world is 64 blocks high)\n \"amplitude\": 20,\n \"roughness\": 0.6,\n \"waterLevel\": 25,\n \"sketch\": { \"rows\": [\"0012345\", \"…\"] },\n // optional TOP-DOWN elevation map drawn by the AI: 4..32 rows (north→south) of 4..32 digits\n // (west→east): 0 deep water, 1 shallow, 2 shore, 3 lowland, 4 plain, 5 hills, 6 high hills,\n // 7 mountains, 8 high mountains, 9 peaks. Smoothed + natural detail; with a sketch the recipe\n // draws its own coastline (no island mask). A top-down PICTURE's heightmap wins over it.\n // heightmap / surfaceMap — filled by the game from a top-down picture; don't write by hand\n },\n \"zones\": [\n // <= 12; each point belongs to the nearest zone (weighted by radius)\n { \"x\": 0.5, \"z\": 0.5, \"radius\": 0.4, \"biome\": \"snow\" },\n // biome: meadow | forest | birch_forest | taiga | snow | desert | badlands | beach |\n // swamp | rocky | savanna | jungle\n // overrides: surface, subsurface (any solid block — even wool), trees 0..1,\n // treeType oak|birch|spruce|palm|dead|none, grass 0..1, flowers 0..1, height -20..20\n ],\n \"structures\": [\n // <= 40\n { \"type\": \"castle\", \"x\": 0.5, \"z\": 0.5, \"size\": \"large\", \"rotation\": 180 },\n // house | tower | castle | village | wall | road | bridge | ruins | pond | rock |\n // tree_cluster | pyramid | well | farm | model | river\n // size small|medium|large; rotation 0|90|180|270 (buildings: door north|east|south|west)\n { \"type\": \"road\", \"x\": 0.25, \"z\": 0.72, \"x2\": 0.5, \"z2\": 0.5 }, // wall/road/bridge: x2,z2\n {\n \"type\": \"river\",\n \"path\": [\n [0.8, 0.2],\n [0.5, 0.5],\n [0.1, 0.95],\n ],\n }, // 2..16 points, source → mouth\n {\n \"type\": \"model\",\n \"model\": \"airplane\",\n \"x\": 0.5,\n \"z\": 0.55,\n \"rotation\": 90,\n \"elevation\": 0,\n },\n ],\n \"models\": { \"airplane\": {/* see below */} }, // <= 4 models, each can be placed many times\n \"npcs\": [\n { \"name\": \"Björn\", \"role\": \"smith\", \"x\": 0.26, \"z\": 0.7, \"persona\": \"…\" },\n ],\n \"palette\": {\n \"wall\": \"planks\",\n \"roof\": \"dark_planks\",\n \"floor\": \"planks\",\n \"path\": \"dirt_path\",\n \"accent\": \"stone_bricks\",\n },\n \"ambience\": { \"timeOfDay\": \"dawn\" }, // dawn | day | sunset | night\n}\n```\n\n### Models — objects built from blocks\n\nThe AI builds an object (a plane, a car, a ship, a statue, a creature) from\n**shapes**, not block by block — a language model reasons well about\n\"fuselage = cylinder, wings = flat boxes, symmetric\", and a shape list stays\nshort. `rasterizeModel()` turns it into blocks.\n\n```jsonc\n\"airplane\": {\n \"size\": [40, 12, 33], // [length x, height y, width z], <= 64 × 40 × 64\n // model coords: x from the FRONT (x=0, the nose) back, y UP (y=0 on the ground), z left→right;\n // the middle plane is z = (width - 1) / 2. Later shapes overwrite earlier; \"air\" carves.\n \"parts\": [ // <= 200\n { \"shape\": \"cylinder\", \"axis\": \"x\", \"center\": [4, 4, 16], \"radius\": 2.5, \"length\": 30, \"block\": \"white_wool\" },\n // center = middle of the START cap; runs `length` blocks along +axis (negative = −axis)\n { \"shape\": \"cone\", \"axis\": \"x\", \"center\": [3, 4, 16], \"radius\": 2.5, \"radius2\": 0.6, \"length\": -4, \"block\": \"white_wool\" },\n { \"shape\": \"box\", \"from\": [14, 3, 1], \"to\": [19, 3, 13], \"block\": \"white_wool\", \"mirror\": \"z\" },\n // \"mirror\": \"x\" | \"z\" also draws the mirror copy across the middle — wings, wheels, lights\n { \"shape\": \"sphere\", \"center\": [x, y, z], \"radii\": [rx, ry, rz], \"block\": \"…\", \"hollow\": true },\n { \"shape\": \"line\", \"from\": [x, y, z], \"to\": [x, y, z], \"radius\": 0, \"block\": \"…\" },\n { \"shape\": \"box\", \"from\": [33, 9, 16], \"to\": [34, 11, 16], \"block\": \"air\" }\n ],\n \"voxels\": [[9, 5, 14, \"light_blue_wool\"]] // <= 400 single blocks for details\n}\n```\n\nPlaced by a `model` structure: centred at `x, z`, turned by `rotation`, its\nbottom on the ground under the centre (on water — on the surface), `elevation`\nlifts it (a plane in flight). The footprint is cleared (trees and bumps don't\ngrow through it). Model blocks written with Minecraft names are forgiven:\n`red_concrete`, `cyan_terracotta`, `minecraft:lime_wool` → our wool of that colour;\n`*_stained_glass` → glass. Anything unknown drops that shape.\n\n### Blocks\n\ngrass, dirt, stone, cobblestone, mossy_cobblestone, sand, red_sand, gravel,\nclay, snow, ice, sandstone, terracotta, stone_bricks, bricks, planks,\ndark_planks, oak_log, spruce_log, birch_log, glass, dirt_path, leaves,\nspruce_leaves, birch_leaves, and wool in 12 colours (white, red, orange,\nyellow, green, blue, light_blue, purple, pink, brown, gray, black — `red_wool`…).\n\nGood to know: a **village** already has houses facing a well, paths and (large)\na farm; a **bridge** needs water under it (`valley` shape, a river, a pond);\na **river** always flows downhill from its first point and cuts its bed through\nhills; keep structures inside `0.2..0.8` on an island; the player spawns on dry\nland nearest the centre, away from structures.\n\n## The project's start world\n\n`src/worlds/startWorld.ts` (in a project: `src/modules/voxelcraft/src/worlds/startWorld.ts`):\n\n```ts\nexport const START_WORLD: unknown = { name: \"Frost Keep\", terrain: { shape: \"mountains\" }, … };\n```\n\n`null` (default) = the classic seed-1337 noise world. A player with a saved\nworld continues it (\"My worlds\"). Easiest way to get a recipe: in the game,\n**Create world → Download recipe**, then paste the JSON as an object.\n\n## In the game\n\n- **Create world** (pause overlay): up to **4 pictures** (drop, click, Ctrl+V —\n e.g. one car from several angles), description, size (island 256 / large 512\n / infinite / custom), how to read the pictures:\n - **Образец для ИИ** (default) — the AI decides from the description what the\n pictures are (an object to build, a landscape to recreate, a style);\n - **Карта сверху** — the first picture is a top-down map: blue = water, land\n rises from the shore, pixel colour → nearest block (works without AI too);\n - **Настроение** — only climate, colours, materials.\n Then with or without AI, a **mini-map preview** (red dot = spawn), play /\n download / load recipe.\n- **My worlds**: every world autosaves to IndexedDB (every 30 s of play, on\n pause, before switching worlds); the last one reopens on the next launch.\n\n## AI: the \"voxel-world\" feature of the title\n\nThe AI option appears only when the title has a Text feature with key\n`voxel-world` (`client.ai.getDefinitions()`). Configure it in the dashboard\n(InApp AI page) or via the title-config MCP `save_ai`:\n\n- `Modality: \"Text\"` (every generation is a queued job the game polls — a world\n with models is a long answer, minutes; there is no Async switch any more).\n- `Model`: a Text **mode** id from `GetInAppAIModels` (e.g. `standard`) whose `AcceptsImages` is true — never a model name\n (otherwise image requests are refused before any charge). Model quality =\n object quality: a strong vision model builds far better planes and cars.\n- `Behavior.SystemInstructions` = **`VOXEL_WORLD_SYSTEM_PROMPT`** from\n `src/ai/worldSpecPrompt.ts`, verbatim (a test keeps it in sync with the\n format). `HistoryMessages: 0`. **`MaxTokens` ≈ 48000** — models are the long part, and a\n reasoning model (GLM 5.3 Flash) spends part of the limit on thinking: at 16000 the\n first live run was cut off, the second took ~18000 output tokens (~1.6 credits).\n- `AllowImages: true`, **`MaxInputImages: 4`** or more — the game sends as many\n JPEGs (≤ 1568 px) as the feature allows; there is no platform cap, only what\n the model accepts.\n- `Safety.LockBehavior: true`, `MaxPromptChars: 1000` (the game trims the description).\n- `Limits` (`DailyCap`, `CooldownSeconds`) and `PriceOptions`.\n\n⚠ No JSON mode in the engine, and an answer cut off by `MaxTokens` comes back\n`completed` **and is charged**. The game detects a cut answer and builds the\nworld without AI instead — keep `MaxTokens` generous.\n\n`createClientWorldAI(client)` (`src/ai/worldAi.ts`): status from definitions,\none `relatedEntityID` per click (a dropped connection is retried with the same\nkey — no double charge), polling of the job, a job abandoned by closing\nthe screen is resumed next time, refusals mapped to player-facing messages, the\nplayer's chosen size enforced over the model's.\n\n## Sharing worlds in the Workshop\n\nVoxelCraft registers the content type **`voxelcraft.world`** (`src/workshop.ts`,\n`ctx.content.registerType` in `module.ts`). With the `workshop` module installed, players publish\nworlds from \"My worlds\" and open other players' worlds; the Workshop switches to the `voxelcraft`\nmode after opening.\n\n- `capture` publishes the world's **SaveData JSON** (role `main`, `application/json`) — the source\n plus the player's diffs, not the blocks — and a **webp preview** for recipe worlds (noise worlds\n have none). Metadata: `kind`, `seed`.\n- `open` parses the save, opens it in the running game (`openSharedWorld`) or stores it as a new\n local world.\n- Title config (dashboard → LiveOps → Workshop → Content types, or the title-config MCP):\n `ContentTypes[\"voxelcraft.world\"] = { DisplayName, Files: { main: { AllowedMimeTypes:\n[\"application/json\"], MaxBytes: … } } }`. Don't set `ThumbnailRequired` — noise worlds can't\n capture a preview. How to charge / gate access: **workshop-system**.\n\n## Agent debug surface\n\n`state().world`: `id`, `name`, `source` (`noise` | `spec`), `seed`, `size`,\n`shape`, `biomes`, `structures`, `fromPicture`, `spawnHint`; `createWorldOpen`.\nActions: `generateWorld { text, size?, seed? }` (without AI),\n`loadWorldSpec { spec }` (from a recipe object; returns `warnings`). The screen\nitself is plain DOM — read it and click it like a player.\n\n## Don't break\n\n- **Generators are part of the save format.** A save is `source + diffs`; the\n world is regenerated and the diffs land on it. Changing the output of\n `TerrainGenerator`, `SpecGenerator`, the structure builders or\n `rasterizeModel` corrupts every saved world — and every world **published in\n the Workshop**, which is the same SaveData. Both generators have golden\n fingerprint tests — a red one means \"you broke saves\", not \"update the numbers\".\n- **Block ids are append-only** (`registry/Blocks.ts`); new tiles go to the END\n of `TILE_ORDER` (`gfx/TextureAtlas.ts`).\n- The module takes **only types** from `@idosgames/core`; the client comes from\n the host (`ctx.client`).\n",
|
|
5
5
|
"references": []
|
|
6
6
|
}
|