mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-08-27 04:37:47 +00:00
Docvks
This commit is contained in:
+24
-4
@@ -2,6 +2,22 @@
|
||||
|
||||
Iris is a world generation engine for Minecraft servers and mod loaders. It builds terrain, biomes, caves, structures, objects, and entities from editable JSON packs, exposes an in-game studio authoring workflow, and runs as a Bukkit-family plugin or as a Fabric, Forge, or NeoForge server mod. Cross-platform generation is designed and tested for deterministic parity when artifacts, pack bytes, seeds, and test areas are identical; verify release candidates with GoldenHash. This branch targets Minecraft 26.2; Java 25 is required everywhere.
|
||||
|
||||
## Choose a learning path
|
||||
|
||||
Do not read the documentation as one long reference. Start with the outcome you need and follow that path in order:
|
||||
|
||||
| Outcome | Read and complete |
|
||||
|---|---|
|
||||
| Install Iris and create a world | `01 - Installation & Platforms.md` → `02 - Getting Started.md` → `31 - Operator Runbooks & Smoke Tests.md` |
|
||||
| Build a pack from nothing | `05 - Concepts & Pack Layout.md` → `10 - Studio & VSCode Schemas.md` → `26 - Example - Minimal Dimension.md` |
|
||||
| Design terrain and biomes | `11 - Dimensions.md` → `12 - Regions.md` → `13 - Biomes.md` → `14 - Generators & Noise.md` |
|
||||
| Add caves and surface detail | `15 - Caves & Carving.md` → `16 - Surfaces, Decorators & Deposits.md` → `17 - Trees, Fungi, Coral, Crystals, Formations, Ruins.md` |
|
||||
| Add a structure | `18 - Structures Overview.md`, then `19 - Objects.md` + `20 - Object Placement.md`, `21 - Jigsaw Structures.md`, or `22 - Native Structures & Datapacks.md` |
|
||||
| Prepare a production world | `25 - Pack Management.md` → `06 - Worlds & Lifecycle.md` → `07 - Pregeneration.md` → `31 - Operator Runbooks & Smoke Tests.md` |
|
||||
| Integrate another plugin or mod | `28 - Integrations.md` → `30 - Platform Differences.md`; Java consumers start at `90 - API - Getting Started.md` |
|
||||
|
||||
Each tutorial gives an observable gate. Stop and resolve that gate before layering on the next system; otherwise a missing biome key can look like a cave, decorator, or structure failure later.
|
||||
|
||||
## Platforms
|
||||
|
||||
| Platform | Artifact | Minecraft | Notes |
|
||||
@@ -94,16 +110,18 @@ Docs `00`–`33` are for operators and pack authors in reading order. `85`–`87
|
||||
| `dist/` | Built consumer jars after `buildAllToOut` |
|
||||
| `docs/` | Authoritative product and API documentation |
|
||||
|
||||
## Building
|
||||
## Developer build check
|
||||
|
||||
Requirements: JDK 25 (`JAVA_HOME` set). From the Iris repo root:
|
||||
Set `JAVA_HOME` to JDK 25, then run the repository gate from the Iris root:
|
||||
|
||||
```
|
||||
```text
|
||||
java -version
|
||||
./gradlew build
|
||||
./gradlew test
|
||||
./gradlew buildAllToOut
|
||||
```
|
||||
|
||||
The check passes when `build` completes with no failed tasks and `buildAllToOut` publishes one current jar per supported platform under `dist/`. `build` already runs the test suite; use `./gradlew test` when you need to rerun tests without assembling every artifact.
|
||||
|
||||
`buildAllToOut` writes every platform jar into `dist/`:
|
||||
|
||||
```
|
||||
@@ -126,3 +144,5 @@ Modded adapters are driven with their own project root when developing:
|
||||
`-PincludeModdedAdapters=true` can surface those builds in the root composite for IDE import only; it is off by default because each adapter includes the root build back for `core`/`spi` substitution.
|
||||
|
||||
Current version property: `irisVersion=4.0.0-26.2` in `gradle.properties`.
|
||||
|
||||
If a mod-loader build fails while the root Bukkit/core build passes, rerun that adapter from its own project root and fix the first loader-specific error. Do not treat a Bukkit jar or core test pass as proof that Fabric, Forge, or NeoForge compiled.
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
Iris installs as either a Bukkit-family plugin jar or a self-contained Fabric, Forge, or NeoForge mod jar. Java 25 is required on every platform. On first boot the default `overworld` pack is downloaded when missing; packs live under each platform’s data directory.
|
||||
|
||||
## Installation outcome
|
||||
|
||||
Complete one platform path below. A successful install has all three results:
|
||||
|
||||
1. Iris reaches its enabled/ready state without an exception.
|
||||
2. The platform data directory contains `settings.json` and a loadable `packs/overworld/` directory.
|
||||
3. `/iris` prints help from the server console. On a modded client, the Iris keybind category is an additional client-side check, not a substitute for the server check.
|
||||
|
||||
Keep the previous jar/mod and the entire Iris data directory until the new build passes these checks. Replacing the binary does not update pack snapshots already stored inside worlds.
|
||||
|
||||
## Requirements
|
||||
|
||||
| Requirement | Value |
|
||||
@@ -14,6 +24,15 @@ Iris installs as either a Bukkit-family plugin jar or a self-contained Fabric, F
|
||||
| NeoForge | 26.2.0.12-beta+ |
|
||||
| Network | Outbound HTTPS on first boot for default pack download (GitHub IrisDimensions overworld release / pack install) |
|
||||
|
||||
Before replacing an existing installation:
|
||||
|
||||
1. Run `java -version` and confirm the server is using Java 25, not merely that Java 25 is installed elsewhere.
|
||||
2. Match the jar label to the target platform and Minecraft version.
|
||||
3. Stop the server cleanly.
|
||||
4. Back up the existing Iris jar/mod, Iris data directory, and every Iris world you intend to retain.
|
||||
|
||||
Do not copy multiple Iris platform jars into the same `plugins/` or `mods/` directory.
|
||||
|
||||
## Plugin install (Paper / Purpur / Leaf / Canvas / Folia / Spigot)
|
||||
|
||||
1. Place the CraftBukkit-labelled plugin jar into `plugins/`.
|
||||
@@ -21,10 +40,21 @@ Iris installs as either a Bukkit-family plugin jar or a self-contained Fabric, F
|
||||
3. On first boot Iris provisions the default `overworld` pack into `plugins/Iris/packs/overworld` when missing (source: IrisDimensions overworld `beta` release zip).
|
||||
4. Settings are written at `plugins/Iris/settings.json` if absent (`IrisSettings.read()`).
|
||||
|
||||
Validate the plugin install from the server console:
|
||||
|
||||
```text
|
||||
/iris version
|
||||
/iris pack validate pack=overworld
|
||||
```
|
||||
|
||||
The first command must report the running Iris, platform, and Minecraft versions. The second must resolve the downloaded pack and finish without blocking validation errors. Then complete the disposable-world workflow in `02 - Getting Started.md`; a command response alone does not prove that the generator can create chunks.
|
||||
|
||||
Command root: `/iris` (aliases `/ir`, `/irs`). Explicit permission in the descriptor: `iris.treefeller` (default op). Command access uses the Director permission model rooted at `iris.all` (see `04 - Commands & Permissions.md`).
|
||||
|
||||
Soft dependencies (optional, not bundled): PlaceholderAPI, CraftEngine, Nexo, ItemsAdder, SCore, ExecutableItems, MythicLib, MMOItems, eco, EcoItems, MythicMobs, MythicCrucible, KGenerators, WorldEdit. Multiverse-Core is ordered after Iris so Multiverse sees Iris generators after Iris is up.
|
||||
|
||||
Before creating a real world, run the Bukkit fresh-install smoke in `31 - Operator Runbooks & Smoke Tests.md`. If the first-boot pack download fails, fix network access and restart; do not create an empty directory named `overworld` as a workaround because an incomplete pack is not a usable dimension.
|
||||
|
||||
### Folia note
|
||||
|
||||
`folia-supported: true`. Engine work uses region-safe scheduling. Runtime `/iris create` does **not** hot-create a live world on Folia: Iris stages world files, pack snapshot, and `bukkit.yml` registration, then requires a server restart before the world generates and loads. After restart, use `/iris load` or rely on the registered world entry as appropriate. See `06 - Worlds & Lifecycle.md`.
|
||||
@@ -36,6 +66,15 @@ Soft dependencies (optional, not bundled): PlaceholderAPI, CraftEngine, Nexo, It
|
||||
3. The jar is self-contained: core, SPI, and required Fabric API modules are bundled where applicable. Mod id: `irisworldgen`.
|
||||
4. On first boot, if `config/irisworldgen/modded.json` has `autoDownloadDefaultPack` true (default) and `defaultPack` (default `overworld`) is missing, Iris downloads `IrisDimensions/<pack>` (branch `master` for the auto-prefetch path) into the packs folder before the forced worldgen datapack is written.
|
||||
|
||||
Validate the server-side mod install:
|
||||
|
||||
```text
|
||||
/iris version
|
||||
/iris pack validate overworld
|
||||
```
|
||||
|
||||
The install passes when Iris reports the expected loader/version, `config/irisworldgen/packs/overworld/dimensions/` contains a dimension JSON file, and validation has no blocking errors. Restart once before creating a world if the pack or its generated dimension-type datapack was installed during this boot.
|
||||
|
||||
Packs installed later register custom dimension types (height ranges) and custom biomes through the forced datapack at server start. **Restart once after adding a pack** so worlds get full heights and biomes. Worlds created before that restart run with fallback heights.
|
||||
|
||||
### Singleplayer (modded clients)
|
||||
@@ -106,6 +145,17 @@ Full key list: `03 - Configuration.md`.
|
||||
|
||||
Manual install: `/iris download <pack>` (alias `dl`). Default overworld uses the beta-release path; other packs use `IrisDimensions/<pack>/<branch>` (plugin default branch `stable` for non-default; mod download defaults branch `stable` unless auto-prefetch uses `master` — see `25 - Pack Management.md`).
|
||||
|
||||
## Installation recovery
|
||||
|
||||
| Symptom | Check | Recovery |
|
||||
|---|---|---|
|
||||
| Iris does not appear in `/iris version` | Wrong directory, wrong platform jar, duplicate jar, Java mismatch, or an enable exception | Stop the server, keep only the matching artifact, confirm Java 25, and fix the first Iris exception in the startup log |
|
||||
| `settings.json` exists but `packs/overworld` does not | Default-pack download failed or is still incomplete | Restore outbound HTTPS or install a complete pack, then restart; do not create an empty `overworld` folder |
|
||||
| Pack validates but modded height/biomes use fallbacks | Forced datapack was generated after registries loaded | Restart once with the pack already installed, then create a new disposable world |
|
||||
| Bukkit command is denied for a non-op | `iris.all` is missing | Grant `iris.all`; `iris.treefeller` controls only survival tree felling |
|
||||
| Client HUD is absent but server commands work | Client mod missing, disabled keybind, or server capability not negotiated | Install the matching client mod, reconnect, and verify the Iris keybind category; server generation does not require the client HUD |
|
||||
| Existing world ignores a newly installed pack | Production world is using its stored snapshot | Follow the explicit snapshot update or new-world workflow in `06 - Worlds & Lifecycle.md` and `25 - Pack Management.md` |
|
||||
|
||||
## Native worldgen over Iris terrain
|
||||
|
||||
Iris replaces the chunk generator. Vanilla and mod worldgen only runs where Iris runs it. Identical on every platform:
|
||||
|
||||
@@ -4,6 +4,12 @@ This page walks through creating an Iris world, teleporting into it, running a s
|
||||
|
||||
Full command trees and permissions: `04 - Commands & Permissions.md`. World lifecycle detail: `06 - Worlds & Lifecycle.md`. Studio detail: `10 - Studio & VSCode Schemas.md`.
|
||||
|
||||
## Outcome
|
||||
|
||||
At the end you will have one disposable Iris world created from the `overworld` pack, you will have entered it, generated a small known area, and opened a separate Studio authoring session. Use the fixed seed `1337` until the workflow is proven; changing seeds while diagnosing a pack makes comparisons ambiguous.
|
||||
|
||||
Treat each numbered section as a gate. Confirm the world is loaded before teleporting, confirm ordinary chunks generate before starting pregen, and confirm the Studio world is separate from the production snapshot before editing files.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Iris installed per `01 - Installation & Platforms.md`
|
||||
@@ -49,6 +55,8 @@ Aliases for the create command itself: `c`.
|
||||
/iris create myworld type=overworld seed=1337
|
||||
```
|
||||
|
||||
Run `/iris worlds` after the command. On non-Folia servers, `myworld` must appear as a loaded Iris world. On Folia, success is the staging-and-restart message; restart before continuing.
|
||||
|
||||
### Mod
|
||||
|
||||
```
|
||||
@@ -71,6 +79,8 @@ If the pack is not installed, create starts an async download of `IrisDimensions
|
||||
|
||||
There is no separate “load” step on modded after a successful create.
|
||||
|
||||
Run `/iris world status` and confirm the new dimension uses pack `overworld`. Then run `/iris info irisworldgen:myworld` as a gamemaster and verify seed `1337` before teleporting.
|
||||
|
||||
## 2. Load a world (plugin only)
|
||||
|
||||
```
|
||||
@@ -95,6 +105,8 @@ Aliases: `tp`. Teleports the target (or the executing player) to the world spawn
|
||||
/iris tp myworld
|
||||
```
|
||||
|
||||
Success is a completed teleport followed by normal chunk generation around spawn. If the teleport target is missing, return to the create/load gate instead of retrying pregen.
|
||||
|
||||
### Mod
|
||||
|
||||
```
|
||||
@@ -108,6 +120,8 @@ Dimension is a loaded level argument (tab-completes Iris dimensions). Console mu
|
||||
/iris tp irisworldgen:myworld
|
||||
```
|
||||
|
||||
Success is entry into `irisworldgen:myworld` with `/iris info irisworldgen:myworld` still reporting the expected pack and seed.
|
||||
|
||||
## 4. Pregenerate
|
||||
|
||||
Radius is in **blocks**. One pregeneration job runs server-wide.
|
||||
@@ -140,6 +154,8 @@ Example:
|
||||
/iris pregen start 352 world=myworld center=0,0 gui=false
|
||||
```
|
||||
|
||||
Immediately run `/iris pregen status`. A 352-block radius centered at `0,0` should report a 2,025-chunk job and advance without a growing failed count.
|
||||
|
||||
### Mod
|
||||
|
||||
```
|
||||
@@ -161,6 +177,8 @@ Flags are optional and combinable in any order after the radius/dimension/center
|
||||
/iris pregen start 352 irisworldgen:myworld at 0 0 sync
|
||||
```
|
||||
|
||||
Immediately run `/iris pregen status` and confirm the target dimension, total, and generated count. Use `/iris pregen stop` before retrying with different flags.
|
||||
|
||||
Control: `/iris pregen stop`, `pause` / `resume`, `status`. Progress: client mod HUD when present, otherwise boss bar / console.
|
||||
|
||||
## 5. Studio (first authoring steps)
|
||||
@@ -205,11 +223,22 @@ Default create name is `studio`; if that folder already exists, Iris picks the n
|
||||
|
||||
Some Bukkit studio tools (importvanilla feature capture, loot GUI, profile, etc.) refuse or redirect on modded with an explicit message; capture vanilla features on Bukkit and copy the pack folder if needed.
|
||||
|
||||
```
|
||||
/iris studio open overworld
|
||||
Plugin example:
|
||||
|
||||
```text
|
||||
/iris studio open overworld seed=1337
|
||||
/iris studio vscode dimension=overworld
|
||||
```
|
||||
|
||||
Modded example:
|
||||
|
||||
```text
|
||||
/iris studio open overworld 1337
|
||||
/iris studio vscode overworld
|
||||
```
|
||||
|
||||
The Studio gate passes when the transient Studio world opens, the workspace points at the live `packs/overworld/` tree, and a saved valid JSON change produces a hotload result. Close it with `/iris studio close`; production `myworld` must remain separate.
|
||||
|
||||
## Suggested first-session flow
|
||||
|
||||
1. Confirm pack: ensure `overworld` (or your pack) exists under the platform packs directory.
|
||||
@@ -219,6 +248,8 @@ Some Bukkit studio tools (importvanilla feature capture, loot GUI, profile, etc.
|
||||
5. Optional: `/iris pregen start 352 …` for a small square (~704×704 blocks).
|
||||
6. Optional: `/iris studio open <pack>` to edit live; use VSCode schemas for autocomplete of blocks/items/entities (mod content included on mod loaders).
|
||||
|
||||
The session passes when the production world loads again after a clean restart and generates new chunks from its copied pack snapshot. Remove a disposable world only through the lifecycle command after evacuating players; see `06 - Worlds & Lifecycle.md`.
|
||||
|
||||
## Common pitfalls
|
||||
|
||||
| Pitfall | What happens | What to do |
|
||||
|
||||
@@ -2,6 +2,39 @@
|
||||
|
||||
Iris stores shared runtime settings in `settings.json` under the platform data folder. On first boot Iris writes a full defaults file if missing; every successful load rewrites the file so new keys appear with defaults. See `01 - Installation & Platforms.md` for data paths and `33 - Performance Tuning.md` for tuning guidance.
|
||||
|
||||
## Tutorial: change one setting safely
|
||||
|
||||
1. Start Iris once so it writes the current schema and defaults.
|
||||
2. Copy `settings.json` outside the server directory as a rollback file.
|
||||
3. Change one key only. Keep its JSON type unchanged; quoted numbers and strings such as `"false"` are not booleans.
|
||||
4. Save the file and use `/iris reload`, or wait for the platform hotload interval described below.
|
||||
5. Confirm the console reports the settings reload without a parse exception.
|
||||
6. Exercise the affected feature. For performance or thread-pool settings, restart before judging the result because some values are read when services are constructed.
|
||||
|
||||
If parsing fails, restore the saved file and restart. Do not delete `settings.json` unless resetting every setting to defaults is intentional.
|
||||
|
||||
For example, to change only the server locale, edit the existing `general` object in place:
|
||||
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"language": "de_DE"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This fragment shows the field location; do not replace a populated settings file with the fragment. After `/iris reload`, run `/iris help` and confirm the selected locale is active. Iris rewrites the complete settings file after a successful load, including defaults for fields that were absent.
|
||||
|
||||
### Validation and rollback
|
||||
|
||||
| Result | Meaning | Action |
|
||||
|---|---|---|
|
||||
| Reload succeeds and the affected feature changes | File parsed and the setting reached a live reload path | Keep the backup until the next clean restart |
|
||||
| Reload succeeds but behavior is unchanged | Setting is read only when a service or engine is constructed | Restart, then retest the same workload |
|
||||
| Parse exception or requested locale rejected | JSON shape, type, or locale is invalid | Restore the backup, reload, and make one smaller edit |
|
||||
| File is rewritten with defaults | Missing fields were normalized by `IrisSettings` | Reapply only intentional overrides; do not restore an obsolete full file over new defaults |
|
||||
| Modded and Bukkit paths differ | The wrong data root was edited | Use the path table below and confirm the changed file timestamp before reloading |
|
||||
|
||||
## File locations
|
||||
|
||||
| Platform | Shared settings | Packs root | Modded-only config |
|
||||
@@ -60,7 +93,7 @@ Static helper `IrisSettings.getThreadCount(int c)`: for `c` in `{-1,-2,-4}` retu
|
||||
| `useConsoleCustomColors` | boolean | `true` | Custom colors for console senders |
|
||||
| `useCustomColorsIngame` | boolean | `true` | Custom colors for player senders |
|
||||
| `adjustVanillaHeight` | boolean | `false` | Adjust vanilla height handling |
|
||||
| `autoIngestDatapacks` | boolean | `true` | Auto-ingest configured datapack imports (Bukkit datapack pipeline) |
|
||||
| `autoIngestDatapacks` | boolean | `true` | Auto-ingest configured external datapacks; managed structures remain scoped to declaring Iris dimensions |
|
||||
| `autoImportDatapackStructures` | boolean | `false` | Opt-in bulk write of every registered datapack structure as editable Iris resources; prefer `/iris structure import <dimension>` |
|
||||
| `strictContentKeys` | boolean | `false` | Unresolved pack content keys and bad block-state properties become blocking pack errors; system property `-Diris.strictContent` overrides when set |
|
||||
| `spinh` | int | `-20` | Splash / spin color H |
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
# 04 - Commands & Permissions
|
||||
|
||||
Iris exposes one root command: `/iris` (aliases `/ir`, `/irs`). Bukkit uses VolmLib Director (named parameters, optional `key=value`). Fabric, Forge, and NeoForge register a Brigadier tree with the same root aliases. This is the complete command reference; platform gaps are marked **Bukkit-only** or **modded-only**. See `30 - Platform Differences.md` for a matrix and `03 - Configuration.md` for `/iris reload` targets.
|
||||
Iris exposes one root command, `/iris` (aliases `/ir`, `/irs`), with Bukkit using VolmLib Director for named parameters and optional `key=value` arguments. Fabric, Forge, and NeoForge register a Brigadier tree with the same root aliases. This is the complete command reference; platform gaps are marked **Bukkit-only** or **modded-only**. See `30 - Platform Differences.md` for a matrix and `03 - Configuration.md` for `/iris reload` targets.
|
||||
|
||||
## Common command recipes
|
||||
|
||||
Use these as entry points; follow the linked guide before running destructive or long-running forms.
|
||||
|
||||
| Goal | Bukkit-family | Fabric / Forge / NeoForge | Success check | Detailed guide |
|
||||
|---|---|---|---|---|
|
||||
| Create and enter a disposable world | `/iris create tutorial type=overworld seed=1337`, then `/iris tp tutorial` | `/iris create tutorial overworld 1337`, then `/iris tp irisworldgen:tutorial` | World/dimension appears in `/iris worlds` or `/iris world status`; ordinary chunks generate | `02 - Getting Started.md` |
|
||||
| Validate a pack before world creation | `/iris pack validate pack=overworld` | `/iris pack validate overworld` | No blocking validation errors | `25 - Pack Management.md` |
|
||||
| Open the live authoring pack | `/iris studio open overworld seed=1337` | `/iris studio open overworld 1337` | Transient Studio world opens and a valid save hotloads | `10 - Studio & VSCode Schemas.md` |
|
||||
| Create an in-game jigsaw project | `/iris jigsaw create overworld village/demo` | Not available; author on Bukkit and copy the saved pack | Owned planar, Iris-native graph is created atomically with six 16×16×16 workcells, one variant per archetype, and seed `1337`; edits then autosave | `21 - Jigsaw Structures.md` |
|
||||
| Inspect an Iris jigsaw graph | `/iris structure info overworld <structure>` | `/iris structure info <structure>` while in its Iris dimension | Resolved graph reports pieces and bounds | `21 - Jigsaw Structures.md` |
|
||||
| Pregenerate a small test area | `/iris pregen start 352 world=<world> center=0,0 gui=false` | `/iris pregen start 352 <dimension> at 0 0` | `/iris pregen status` advances with no accumulating failures | `07 - Pregeneration.md` |
|
||||
| Remove a disposable Iris world | Evacuate players, unload, then `/iris remove <world>` | `/iris world delete <dimension>` | Target is absent from world status and its managed data is removed | `06 - Worlds & Lifecycle.md` |
|
||||
|
||||
Do not translate Bukkit `key=value` examples token-for-token to a mod loader. Use the Brigadier forms in the matching command sections.
|
||||
|
||||
If a command fails before doing work, check in this order: platform syntax, permission level, sender requirements (player versus console), exact pack/world key, then lifecycle busy state. A parse error is not evidence that the underlying feature failed.
|
||||
|
||||
## Syntax
|
||||
|
||||
@@ -74,6 +92,7 @@ Tree feller on mod loaders uses platform permission APIs (`irisworldgen:treefell
|
||||
| `pregen` | `pregenerate` | Both | see Pregen | Pregeneration control |
|
||||
| `object` | `o` | Both | see Object | Object tools |
|
||||
| `studio` | `std`, `s` | Both | see Studio | Studio / pack authoring |
|
||||
| `jigsaw` | `jig`, `jgs` | **Bukkit** | see Jigsaw | Transaction-owned planar/spatial Jigsaw Studio |
|
||||
| `pack` | `pk` | Both | see Pack | Validate/cleanup/restore/status |
|
||||
| `structure` | `struct`, `str` | Both | see Structure | Structure index/import/place |
|
||||
| `datapack` | `datapacks`, `dp` | Both | see Datapack | Datapack helpers |
|
||||
@@ -170,8 +189,8 @@ See `07 - Pregeneration.md`.
|
||||
|
||||
| Command | Aliases | Platforms | Params | Description |
|
||||
|---------|---------|-----------|--------|-------------|
|
||||
| `open` | `o` | Both | **Bukkit:** `<dimension> [seed=1337]`. **Modded:** `<pack> [seed]` | Open temporary studio dimension |
|
||||
| `close` | `x` | Both | — | Close studio and discard world |
|
||||
| `open` | `o` | Both | **Bukkit:** `<dimension> [seed=1337]`. **Modded:** `<pack> [seed]` | Open temporary studio dimension; Bukkit refuses to replace an active Jigsaw Studio outside its owner-authorized Jigsaw lifecycle |
|
||||
| `close` | `x` | Both | — | Close studio and discard world; Bukkit requires `/iris jigsaw close` for an active Jigsaw Studio |
|
||||
| `tpstudio` | `stp` | Both | — | Teleport into open studio |
|
||||
| `status` | | **Modded** (Bukkit uses other paths) | — | Show open studio and pack |
|
||||
| `create` | `+` | Both | **Bukkit:** `[name=studio] [template=<dimension>]`. **Modded:** `[name] [template=example]` | Create pack project |
|
||||
@@ -193,6 +212,54 @@ See `10 - Studio & VSCode Schemas.md`.
|
||||
|
||||
---
|
||||
|
||||
## Jigsaw: `/iris jigsaw` (`jig`, `jgs`)
|
||||
|
||||
**Bukkit-only; player origin.** This opens a transient Jigsaw Studio through the same single active Studio lifecycle. Saved Iris jigsaw resources run through the shared core on every platform, but Fabric/Forge/NeoForge do not register this authoring command tree.
|
||||
|
||||
| Command | Params | Description |
|
||||
|---|---|---|
|
||||
| `create` | `<dimension> <key> [mode=planar] [compatibility=iris] [width=16] [height=16] [depth=16] [seed=1337]` | Add-only atomic graph creation followed by open; named `structure=` and `name=` alias `key=`; `mode` completes `planar`/`spatial`, compatibility completes `iris`/`vanilla`; planar X/Z `3..128`, spatial X/Z `1..128`, Y `1..192`, volume `<=2,097,152` |
|
||||
| `convert` | `<dimension> <source> [target=auto] [seed=1337]` | Add-only conversion of one live registered vanilla/datapack jigsaw into an owned Iris graph, then open it; aliases `import`, `import-vanilla` |
|
||||
| `adopt inspect` | `<dimension> <source> [target=auto] [strategy=auto]` | Asynchronously inspect an existing Iris closure and issue a hash-pinned `IN_PLACE`, `CLONE_REQUIRED`, or `BLOCKED` plan; strategy completes `auto`, `in-place`, `clone` |
|
||||
| `adopt apply` | `<planId>` | Revalidate and atomically apply that player's unexpired plan, then open the target at seed `1337`; active/opening Jigsaw Studio is rejected |
|
||||
| `open` | `<dimension> <key> [seed=1337]` | Open an existing graph in compact workcells; aliases `edit`, `reopen`; owner, autosave, and operation barriers protect replacement |
|
||||
| `close` | `[discard=false]` | Close Studio; refuse active autosave/load/graph work or a pending dirty capture unless deliberately discarded |
|
||||
| `status` | — | Show project/workcell state and the current automatic seed-`1337` evaluation, theme, piece count, and diagnostic |
|
||||
| `menu` | — | Open the six-row controls also opened by the generated chest or three sneaks within 1.5 seconds |
|
||||
| `select` | — | Select the workcell containing the player |
|
||||
| `goto` | `<workcell>` | Select and teleport above a stable workcell ID; alias `teleport` |
|
||||
| `particles` | `<visible>` | Toggle player-local bounds and connector particles |
|
||||
| `save` | `[bay=selected]` | Flush the selected dirty workcell's automatic capture now; normal block and container updates already autosave |
|
||||
| `connector channel` | `<channel\|none>` | Look at a saved marker in the active owned workcell within 8 blocks and set/clear its Iris-only channel at the inverse-mapped source position |
|
||||
| `bounds` | `<width> <height> <depth>` | Set the selected workcell capacity without resizing any variant object; every existing variant must fit, and the compact Studio layout requires close/reopen; aliases `cell`, `resize` |
|
||||
| `workcell capacity` | `<width> <height> <depth>` | Explicit nested form of `bounds`; planar capacities are per canonical archetype and spatial capacity is the single project envelope |
|
||||
| `workcell label` | `<displayName>` | Set the selected planar or spatial workcell's author label; quote spaces; solver identity remains canonical |
|
||||
| `workcell label-reset` | — | Reset the selected workcell to its canonical solver label; alias `reset-label` |
|
||||
| `pool create` | `<poolKey> [fallbackPoolKey=none]` | Atomically create an empty owned pool, optionally using an existing owned direct fallback |
|
||||
| `piece create` | `<poolKey> <pieceKey> [weight=1]` | Create and load an owned variant; planar derives connectors from the contextual canonical workcell |
|
||||
| `piece add` | `<poolKey> <pieceKey> [weight=1]` | Re-add and load an existing piece/object already owned by this project |
|
||||
| `piece remove` | `<poolKey>` | Remove the active variant from a pool without deleting its owned resources |
|
||||
| `piece rotatable` | `<true\|false>` | Persist cardinal rotation for the active variant; portable sessions reject `false` |
|
||||
| `piece expand` | — | Resize the active planar or spatial owned variant exactly to workcell capacity; planar canonical sockets move to the new faces |
|
||||
| `variant weight` | `<poolKey> <weight>` | Set the active variant's positive weight in an owned pool |
|
||||
| `variant resize` | `<width> <height> <depth>` | Resize only the active owned variant within its workcell capacity; safe shrink rejects cropped content and the active cell reloads in place |
|
||||
| `variant label` | `<displayName>` | Set the active variant's author label; quote spaces |
|
||||
| `variant label-reset` | — | Reset the active variant to its resource-key fallback; alias `reset-label` |
|
||||
| `variant duplicate` | — | Copy the active variant's object, metadata, and exact pool memberships into one new variant in this workcell |
|
||||
| `variant duplicate-family` | `[themeKey=next]` | Atomically clone every enabled workcell's active owned variant into one coherent Iris family and load the whole family; alias `family` |
|
||||
| `rules limits` | `<maxDepth> <maxSizeChunks>` | Set depth `1..30` and radius `1..32`; `VANILLA_PORTABLE` is restricted to `<=20` and `<=8` |
|
||||
| `rules fallback` | `<poolKey> <fallbackPoolKey\|none>` | Set or clear one owned pool's direct fallback after compiling the complete graph |
|
||||
| `preview goto` | — | Teleport above the permanent seed-`1337` block preview; alias `teleport` |
|
||||
| `preview assemble` | `[seed=1337]` | Compute a deterministic read-only assembly at the player, report its complete piece count, and show in-range bounds as purple particle boxes for 10 seconds within the shared particle budget; places no blocks |
|
||||
| `export` | `[namespace=iris] [output=jigsaw-export] [format=zip] [replace=false]` | Start a background strict Minecraft 26.2 vanilla datapack export as one direct artifact under the Studio packs `exports/` folder |
|
||||
| `delete` | `[confirm=false]` | With `confirm=true`, scan reverse references, close Studio, and hash-pinned-delete the complete owned project; alias `remove` |
|
||||
|
||||
There are no Jigsaw Studio undo, adoption rollback, or mod-loader authoring commands. Planar Studio always has six independently capacitated/enabled canonical workcells, spatial Studio one, and every variant retains its own exact dimensions and optional display label. Workcell and variant rename tools are renamed in an anvil, right-clicked to apply, and sneak-right-clicked to reset. A catalog may contain at most 512 variants. The seed-`1337` assembly is evaluated automatically and rendered as a permanent protected block preview; `preview assemble` is the separate temporary arbitrary-seed particle diagnostic. See `21 - Jigsaw Structures.md` for GUI/toolbox controls, themes/chance/rules/caps, markers, ownership, placement, export, and recovery.
|
||||
|
||||
Bukkit has one global Studio project/world and the Jigsaw session belongs to one owning player. Only that owner can control, load, or mutate it; entering a workcell makes that physical cell the owner's next menu selection. Non-owner edits are cancelled and non-owner commands use a strict informational/communication allowlist. Block and inventory changes in loaded owned workcells autosave after a 40-tick quiet period. Duplicate-one and duplicate-family actions queue once behind pending autosave, expedite it, and continue automatically against the same request and source variants. The chest and live preview are protected; schema-1 or otherwise stale toolbox sticks are rejected. A later mutation after capture starts remains dirty for another capture. Plugins that bypass covered events must call `JigsawStudioService.markDirty(...)` or `markAllDirty(...)`.
|
||||
|
||||
---
|
||||
|
||||
## Pack: `/iris pack` (`pk`)
|
||||
|
||||
| Command | Aliases | Params | Description |
|
||||
@@ -282,6 +349,7 @@ Modded developer group currently implements only `sentry` and `network`/`ip`.
|
||||
| Object expand | — | `/iris object expand` |
|
||||
| Object WE / studio / convert | yes | help stubs only |
|
||||
| Studio loot/profile/spawn/objects/scoreboard/importvanilla | yes | stubs or messages |
|
||||
| Jigsaw Studio create/edit/autosave/export commands and GUI | yes | no; copy a Bukkit-authored Iris pack |
|
||||
| Structure import/capture | yes | messages (run on Bukkit, copy pack) |
|
||||
| Datapack Modrinth ingest/remove | yes | messages |
|
||||
| Datapack status/install (dimension types) | — | yes |
|
||||
@@ -298,6 +366,7 @@ Modded developer group currently implements only `sentry` and `network`/`ip`.
|
||||
- `06 - Worlds & Lifecycle.md`
|
||||
- `07 - Pregeneration.md`
|
||||
- `10 - Studio & VSCode Schemas.md`
|
||||
- `21 - Jigsaw Structures.md`
|
||||
- `25 - Pack Management.md`
|
||||
- `28 - Integrations.md`
|
||||
- `30 - Platform Differences.md`
|
||||
|
||||
@@ -4,6 +4,29 @@ An Iris pack is a directory of JSON, binary objects, and optional assets under t
|
||||
|
||||
See also: `00 - Overview.md`, `01 - Installation & Platforms.md`, `10 - Studio & VSCode Schemas.md`, `11 - Dimensions.md`, `24 - Pack Mods & Snippets.md`, `25 - Pack Management.md`.
|
||||
|
||||
## Tutorial: trace one resource through a pack
|
||||
|
||||
Prerequisites: a loadable pack under the correct platform packs root, command permission, and an editor that preserves JSON syntax. Use this exercise before authoring a large pack:
|
||||
|
||||
1. Validate the pack before editing: `/iris pack validate pack=overworld` on Bukkit, or `/iris pack validate overworld` on a mod loader. Substitute your pack key consistently when tracing another pack.
|
||||
2. Open `dimensions/<dimension>.json` and pick one key from its `regions` array.
|
||||
3. Open `regions/<key>.json` and pick one root key from `landBiomes`.
|
||||
4. Open `biomes/<key>.json` and follow its first generator, object, decorator, or structure reference to the matching registrant folder.
|
||||
5. Confirm every key is the file path relative to that registrant folder with the extension removed.
|
||||
6. Open the pack in Studio and focus that region or biome while editing. Save one valid change, wait for hotload, then rerun pack validation before creating a production snapshot.
|
||||
|
||||
The exercise passes when every reference resolves without guessing a namespace or filename, Studio hotload succeeds, and validation has no blocking errors. If a file exists but never appears, work backward from the dimension graph; unreferenced files are valid but unreachable.
|
||||
|
||||
### Resource-resolution recovery
|
||||
|
||||
| Symptom | Likely cause | Recovery |
|
||||
|---|---|---|
|
||||
| File exists but its key is unresolved | Extension or type-folder prefix was included, path case differs, or the reference starts from the wrong registrant root | Rebuild the key as the exact relative path under the type folder, without extension |
|
||||
| File validates but never generates | It is not reachable from the active dimension → region → biome graph, or its chance/filter excludes it | Trace references from the dimension root and test with Studio focus/buffet modes |
|
||||
| Studio schema does not list a new resource | Workspace schema/resource enums are stale | Run `/iris studio update dimension=<pack>` on Bukkit or `/iris studio update <pack>` on modded |
|
||||
| Two files appear to share a key | Dotted variants or same-base-name candidates are ambiguous | Keep one canonical filename; Iris warns and otherwise selects the sorted first match |
|
||||
| Production world ignores a corrected resource | It is reading its copied snapshot | Validate in Studio, then use the explicit world-update workflow or create a new world |
|
||||
|
||||
## Content model
|
||||
|
||||
| Concept | Role |
|
||||
|
||||
@@ -1,9 +1,46 @@
|
||||
# 06 - Worlds & Lifecycle
|
||||
|
||||
Iris manages world identity, storage paths, pack installation, create/load/unload/remove/evacuate, and main-world promotion through Bukkit-family lifecycle services. Managed Iris worlds live under the level root as `dimensions/iris/<key>/` with namespace `iris`. Non-studio worlds carry a frozen pack at `iris/pack`; studio worlds bind the live packs directory.
|
||||
Iris manages world identity, storage paths, pack installation, creation, persistence, and removal across Bukkit-family servers and the three mod loaders. Bukkit-managed Iris worlds live under the level root as `dimensions/iris/<key>/` with namespace `iris`; modded dimensions persist through `iris-dimensions.json`. Non-Studio worlds carry a frozen pack at `iris/pack`, while Studio worlds bind the live packs directory.
|
||||
|
||||
See also: `04 - Commands & Permissions.md`, `02 - Getting Started.md`, `05 - Concepts & Pack Layout.md`, `07 - Pregeneration.md`, `10 - Studio & VSCode Schemas.md`, `30 - Platform Differences.md`.
|
||||
|
||||
## Tutorial: promote a tested pack to a persistent world
|
||||
|
||||
Prerequisites: a validated pack, a fixed test seed, a current backup, and no active lifecycle or pack-publish operation. The commands below use the installed `overworld` pack and disposable world name `release_candidate`; substitute one pack key consistently when promoting a different pack.
|
||||
|
||||
### Bukkit-family
|
||||
|
||||
1. Validate the live pack: `/iris pack validate pack=overworld`.
|
||||
2. Open it with `/iris studio open overworld seed=1337`, generate representative terrain, then run `/iris studio close` after the final hotload succeeds.
|
||||
3. Create a new world with explicit identity: `/iris create release_candidate type=overworld seed=1337`.
|
||||
4. On Folia, stop and restart after the staging message. On other Bukkit-family servers, continue after `/iris worlds` lists `release_candidate` as loaded.
|
||||
5. Enter it: `/iris tp release_candidate`.
|
||||
6. Generate a bounded baseline: `/iris pregen start 352 world=release_candidate center=0,0 gui=false`.
|
||||
7. Wait for completion, restart cleanly, return with `/iris tp release_candidate`, and generate one new boundary chunk.
|
||||
|
||||
The workflow passes when the world reloads with the same seed and dimension, the pregenerated area loads without generation failures, and new terrain still comes from `<world>/iris/pack`. Never replace or delete that snapshot while its world is loaded. Continued edits under `packs/overworld/` affect Studio only; publish deliberately through `25 - Pack Management.md` or create a new world for breaking height/type changes.
|
||||
|
||||
### Fabric / Forge / NeoForge
|
||||
|
||||
1. Validate the installed pack: `/iris pack validate overworld`.
|
||||
2. Enable a persistent dimension: `/iris world enable irisworldgen:release_candidate overworld 1337`.
|
||||
3. Confirm it in `/iris world status`, then enter it with `/iris tp irisworldgen:release_candidate`.
|
||||
4. Run `/iris pregen start 352 irisworldgen:release_candidate at 0 0` and wait for completion.
|
||||
5. Restart the server. Confirm `/iris world status` restores the same dimension and pack, then run `/iris info irisworldgen:release_candidate` as a gamemaster to verify seed `1337` from `iris-dimensions.json`.
|
||||
|
||||
This workflow passes when the dimension is re-injected after restart and generates normally. `/iris world disable` unloads while retaining persistent data; `/iris world delete` is the destructive removal path.
|
||||
|
||||
### Lifecycle recovery
|
||||
|
||||
| Symptom | Meaning | Recovery |
|
||||
|---|---|---|
|
||||
| Command reports busy | Another `WORLD_MUTATION` or `PACK_MUTATION` lease owns the lifecycle coordinator | Let that operation finish; do not retry concurrent create/remove/update commands |
|
||||
| Folia create succeeds but teleport cannot find the world | Creation staged files and registration only | Restart, then load/teleport as instructed by the staging result |
|
||||
| Bukkit load reports missing or inconsistent data | Managed dimension root, registration, or `iris/pack` snapshot is incomplete | Keep the directory, restore from backup, and reconcile registration before retrying; load never redownloads the snapshot |
|
||||
| Unload reaches its terminal timeout | World, generator, or scheduler work did not settle within 150 seconds | Allow the requested restart; do not force-delete the live directory |
|
||||
| Remove returns `DELETE_QUEUED` | Files were quarantined for startup deletion | Restart and confirm the target is gone before reusing its name |
|
||||
| Modded registry is quarantined as `.broken-<timestamp>` | Whole-file JSON could not be parsed | Keep the backup, recreate or repair each logged id with the original pack/dimension/seed, then verify status |
|
||||
|
||||
## Identity and storage
|
||||
|
||||
| Item | Rule |
|
||||
@@ -90,8 +127,10 @@ Studio uses `IrisCreator.studio(true)`:
|
||||
|
||||
- Does **not** copy the pack into the world folder (except benchmark).
|
||||
- Engine data folder is the live pack path; hotloader starts after engine setup.
|
||||
- Biome Buffet prepares a changed focus before opening the chunk generation session. Its exclusive fair-stage admission downgrades directly to the retained chunk permit, so no other transition can enter between the focus hotload and that chunk.
|
||||
- Studio worlds are transient: unloaded studio worlds are cleaned; `bukkit.yml` studio entries are removed on shutdown cleanup paths.
|
||||
- Studio open/close uses `StudioSVC` transition queue (see `10 - Studio & VSCode Schemas.md`).
|
||||
- Ordinary Studio suppresses native structure starts only while its initial FULL entry chunk is loading, then restores them for later preview chunks. A failed open never unloads or closes the generator while that exact asynchronous entry request remains active; another Studio open is rejected, cleanup begins after it settles, or its transient world is queued for deletion at the next clean startup if it remains active for another 120 seconds.
|
||||
|
||||
## Load
|
||||
|
||||
@@ -112,6 +151,8 @@ Load does not re-download packs; the world must already have `iris/pack` content
|
||||
3. `IrisToolbelt.evacuateAsync` → `WorldLifecycleService.unloadAsync(world, true)` → `generator.closeAsync()`.
|
||||
4. Terminal timeout **150 seconds**: if unload has not settled, marks timeout, requests server restart (`ServerConfigurator.restart`), and fails the future.
|
||||
|
||||
`WorldUnloadEvent` stops Iris engine maintenance immediately, but it is not treated as proof that Paper's chunk scheduler has drained. Generator close waits for the raw world-lifecycle backend to confirm a successful unload, and the 26.2 noise pipeline retains one generation lease through terrain generation and worldgen-heightmap priming.
|
||||
|
||||
## Evacuate
|
||||
|
||||
`/iris evacuate` moves all players out of the Iris world into another loaded world (or kicks if none). Used as a step inside unload and removal.
|
||||
@@ -131,6 +172,8 @@ Load does not re-download packs; the world must already have `iris/pack` content
|
||||
|
||||
Only safe `iris` namespace dimension paths are mutable. Phase timeouts use 120s and can request restart on stuck phases.
|
||||
|
||||
With `delete=true`, Iris records the exact quarantine name in the durable startup queue before moving the world directory. Immediate cleanup and startup retry both snapshot every directory's direct children before deleting them, reject symbolic links and special filesystem entries, and retain the queue entry with the full error when a concurrent writer or filesystem failure leaves content behind.
|
||||
|
||||
## Main world promotion
|
||||
|
||||
When create sets `main=true` (non-Folia), a shutdown hook rewrites `server.properties` `level-name` / `level-seed` and publishes files:
|
||||
|
||||
@@ -4,6 +4,44 @@ Pregeneration walks a rectangular (by default square) block radius around a cent
|
||||
|
||||
See also: `03 - Configuration.md`, `04 - Commands & Permissions.md`, `02 - Getting Started.md`, `06 - Worlds & Lifecycle.md`, `29 - Client HUD & Protocol.md`, `33 - Performance Tuning.md`.
|
||||
|
||||
## Tutorial: run a bounded pregen safely
|
||||
|
||||
Prerequisites: a disposable or backed-up world, enough free disk for the requested area, ordinary on-demand chunk generation already proven, and no other active pregen job. The commands use `release_candidate` from `06 - Worlds & Lifecycle.md`; substitute the exact loaded world or dimension id if yours differs.
|
||||
|
||||
1. Choose a block radius. The standard smoke uses `352` blocks centered at `0,0`, which covers 2,025 chunks.
|
||||
2. Start without the desktop GUI on a headless server.
|
||||
|
||||
Bukkit-family:
|
||||
|
||||
```text
|
||||
/iris pregen start 352 world=release_candidate center=0,0 gui=false serial=false
|
||||
```
|
||||
|
||||
Fabric / Forge / NeoForge:
|
||||
|
||||
```text
|
||||
/iris pregen start 352 irisworldgen:release_candidate at 0 0
|
||||
```
|
||||
|
||||
3. Run `/iris pregen status`. Confirm the target, 2,025 total chunks, generated count, rate, ETA, and failed count.
|
||||
4. Run `/iris pregen pause`, wait for progress to stop, then `/iris pregen resume` and confirm it continues.
|
||||
5. Let the run complete. To test cancellation instead, run `/iris pregen stop` once and wait for in-flight work to close before starting another job.
|
||||
6. Restart the server and visit chunks near the generated boundary.
|
||||
|
||||
The workflow passes when status reaches completion without accumulating failures, no job remains active after restart, and the generated boundary loads normally. Change concurrency, scheduler, or cache settings only after this baseline succeeds; compare one change at a time using `33 - Performance Tuning.md`.
|
||||
|
||||
### Recovery
|
||||
|
||||
| Symptom | Check | Recovery |
|
||||
|---|---|---|
|
||||
| Start reports an active job | One job is already server-wide | Inspect `/iris pregen status`; finish it or stop it and wait for closure before retrying |
|
||||
| Status total is unexpected | Radius is in blocks and center-to-chunk rounding changes bounds | Verify radius and center; use the 352-at-0,0 baseline before larger runs |
|
||||
| Failed count increases | Chunk load timeout, generation exception, disk failure, or lifecycle interruption | Stop the job, fix the first logged failure, verify ordinary generation, then retry the same small area |
|
||||
| `serial=true` is rejected | Strict serial generation is unavailable on this Bukkit platform | Use the normal method or run the diagnostic on a Paper-compatible server |
|
||||
| Desktop GUI does not open | Server is headless or GUI launch is disabled | Use `gui=false` and monitor status, console, boss bar, or client HUD |
|
||||
| Memory pressure repeatedly pauses progress | Effective mantle/heap cap is being reached | Keep the job stopped while tuning; lower residency/in-flight work before increasing heap-sensitive limits |
|
||||
| Restart does not skip completed work | Cache wrapper was disabled, Folia routing disabled it, `nocache` was used, or cache files were removed | Treat the rerun as uncached; do not infer corruption from regeneration alone |
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Behavior |
|
||||
|
||||
@@ -2,6 +2,39 @@
|
||||
|
||||
Iris localizes command, Studio, runtime, HUD, and UI strings through typed Java message catalogs and optional locale overlays. Server locale is selected by `general.language` in `settings.json`. Client keybind labels use Minecraft lang assets under `assets/irisworldgen/lang/`. See also `03 - Configuration.md`, `04 - Commands & Permissions.md`, and `29 - Client HUD & Protocol.md`.
|
||||
|
||||
## Tutorial: select a locale and verify an override
|
||||
|
||||
Prerequisites: write access to the Iris data folder, a backup of `settings.json`, and an operator account that can run `/iris reload`.
|
||||
|
||||
1. Set `general.language` in `settings.json` to an exact bundled id, for example `de_DE`.
|
||||
2. Create `<Iris data folder>/languages/overrides/de_DE.json` with one unmistakable local override:
|
||||
|
||||
```json
|
||||
{
|
||||
"locale": "de_DE",
|
||||
"messages": {
|
||||
"iris.command.unknown": "Lokaler Test: unbekannter Iris-Befehl"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Run `/iris reload` and confirm the response reports `de_DE` as the active locale.
|
||||
4. Run `/iris help`, then run `/iris locale-override-test` to exercise the overridden unknown-command key.
|
||||
5. Confirm the local override appears, other messages come from the bundled German overlay, and any omitted key falls back to canonical English instead of printing a raw identifier.
|
||||
6. Edit the override text, save it, and confirm the hotload path picks up the change. Remove the test override when verification is complete.
|
||||
|
||||
The workflow passes when the selected locale remains active across a clean restart and the partial override wins only for its named key. When authoring a new locale, validate a small command group before translating the full catalog. Server locale files do not change client keybind labels; client assets are a separate surface.
|
||||
|
||||
### Recovery
|
||||
|
||||
| Symptom | Meaning | Recovery |
|
||||
|---|---|---|
|
||||
| Requested locale is rejected | Id is invalid, file id differs, JSON is malformed, or overlay validation failed | Keep the previous locale active, fix the logged validation errors, and reload again |
|
||||
| Raw message key appears | The key is not in the typed catalog or the calling surface bypassed localization | Verify the catalog key first; adding an arbitrary override key cannot create a new message definition |
|
||||
| Override is ignored | Wrong data folder, wrong locale filename/id, or unchanged watched file | Confirm `<data>/languages/overrides/<locale>.json`, update its contents, then run `/iris reload` explicitly |
|
||||
| Formatting or placeholders break | Override changed `{name}` tokens or the value type | Match the English key's placeholders and text/lines/plural shape exactly |
|
||||
| Server text changes but keybind labels do not | Client assets are independent | Update/install the matching `assets/irisworldgen/lang/<mc_code>.json` client resource |
|
||||
|
||||
## English and catalogs
|
||||
|
||||
Canonical English is code-owned in `core/.../localization` (`IrisMessages` and the surface catalogs it assembles). Iris does not ship an English server translation file. English locale id is `en_US` (`VolmitLocales.ENGLISH`).
|
||||
|
||||
@@ -1,6 +1,31 @@
|
||||
# 09 - PlaceholderAPI
|
||||
|
||||
Iris registers the `iris` PlaceholderAPI expansion on Bukkit-family servers when PlaceholderAPI is enabled at Iris enable time. It publishes sixteen keys: seven world-family readings for the player and nine global pregeneration keys. This is an operator board contract, not a Java API; plugins that need the same data with more precision use `90 - API - Getting Started.md`, `91 - API - Terrain.md`, and `92 - API - World Events.md`. PlaceholderAPI is not available on Fabric/Forge/NeoForge. See also `07 - Pregeneration.md` and `28 - Integrations.md`.
|
||||
Iris registers the `iris` PlaceholderAPI expansion on Bukkit-family servers when PlaceholderAPI is enabled at Iris enable time. It publishes sixteen keys: seven world-family readings for the player and nine global pregeneration keys. This is an operator board contract, not a Java API; plugins that need the same data with more precision use `90 - API - Getting Started.md`, `91 - API - Terrain.md`, and `92 - API - World Events.md`. PlaceholderAPI is not available on Fabric/Forge/NeoForge; related runtime and integration details are in `07 - Pregeneration.md` and `28 - Integrations.md`.
|
||||
|
||||
## Tutorial: verify a placeholder before using it in another plugin
|
||||
|
||||
Prerequisites: Bukkit-family Iris, PlaceholderAPI installed before Iris enables, a full server restart, and a player in a loaded Iris world.
|
||||
|
||||
1. Confirm registration: `/papi info iris`. The output must list expansion id `iris` and its published paths.
|
||||
2. Confirm the service: `/papi parse me %iris_available%`. Expect `true` while Iris terrain service is live.
|
||||
3. Confirm player context: `/papi parse me %iris_world.available%`. Expect `true` while the named player is in an Iris world.
|
||||
4. Parse a concrete terrain value: `/papi parse me %iris_world.biome-key%`. Expect a load key such as `desert/hot-dunes`, not `---`.
|
||||
5. While standing in the Iris world, run `/iris pregen start 352 center=0,0 gui=false`, then parse `/papi parse me %iris_pregen.percent%`. Expect a numeric value from `0.00` through `100.00` with no percent sign.
|
||||
6. Stop with `/iris pregen stop` or let the job finish, then run `/papi parse me %iris_pregen.available%`. Expect `false`; other pregen values return `---` after the snapshot clears.
|
||||
7. Copy the exact verified placeholder into the scoreboard, chat, or HUD plugin and test that consumer once more.
|
||||
|
||||
The workflow passes when registration, player-scoped terrain, and global pregen values each produce their documented value shape. Do not debug formatting in the consuming plugin until direct `/papi parse` succeeds.
|
||||
|
||||
### Recovery
|
||||
|
||||
| Symptom | Meaning | Recovery |
|
||||
|---|---|---|
|
||||
| `/papi info iris` has no expansion | PlaceholderAPI was unavailable when Iris scheduled registration | Perform a full restart with both plugins installed; `/papi reload` alone does not trigger Iris registration |
|
||||
| Placeholder remains literal | Path is unknown or uses a removed pre-2.0 name | Copy an exact path from `/papi info iris` or the full table below |
|
||||
| World value is `---` | No online player context, player is outside Iris, or terrain service has no reading | Parse as a named online player after entering a loaded Iris world |
|
||||
| `world.available` is true but biome lags movement | Player view cache is within its one-second TTL | Wait one second or trigger an immediate publish by teleport/world change before diagnosing the consumer |
|
||||
| Pregen value is `---` | No global job snapshot is active | Start a job and wait for its first event; use `pregen.available` as the guard in board templates |
|
||||
| Scoreboard adds `%` twice | `pregen.percent` deliberately omits the suffix | Add one literal `%` in the consumer format, not in the placeholder |
|
||||
|
||||
## Registration
|
||||
|
||||
|
||||
@@ -2,7 +2,44 @@
|
||||
|
||||
Studio is Iris’s live pack-authoring workflow: open a pack as a transient world, edit JSON under `packs/<key>/`, and hotload changes without a full server restart. VSCode (or IntelliJ) gets JSON Schema bindings generated from the Java models so field names, enums, and pack resource keys autocomplete against the real loaders.
|
||||
|
||||
Related: see `04 - Commands & Permissions.md`, `05 - Concepts & Pack Layout.md`, `02 - Getting Started.md`, `25 - Pack Management.md`, `30 - Platform Differences.md`.
|
||||
Related: see `04 - Commands & Permissions.md`, `05 - Concepts & Pack Layout.md`, `02 - Getting Started.md`, `21 - Jigsaw Structures.md`, `25 - Pack Management.md`, `30 - Platform Differences.md`.
|
||||
|
||||
## Tutorial: use the Studio edit loop
|
||||
|
||||
Prerequisites: a writable packs directory, command permission, a fixed seed, and VSCode/Cursor with JSON Schema support or IntelliJ with an existing project. Keep the server console visible while editing.
|
||||
|
||||
### Bukkit-family starter pack
|
||||
|
||||
1. Create the project: `/iris studio create name=tutorial`.
|
||||
2. Open its transient world: `/iris studio open tutorial seed=1337`.
|
||||
3. Generate/open the workspace: `/iris studio vscode dimension=tutorial`.
|
||||
4. Edit `packs/tutorial/biomes/starter.json` and change only its display `name`.
|
||||
5. Save once and wait for the hotload result before making another change.
|
||||
6. In newly generated Studio terrain, run `/iris what biome` and confirm the new display name. Existing blocks are not rewritten by hotload.
|
||||
7. Validate the project: `/iris pack validate pack=tutorial`.
|
||||
8. Close the transient world: `/iris studio close`.
|
||||
|
||||
### Fabric / Forge / NeoForge project
|
||||
|
||||
1. Create from the modded default template explicitly: `/iris studio create tutorial example`.
|
||||
2. Open it: `/iris studio open tutorial 1337`.
|
||||
3. Generate/open schemas: `/iris studio vscode tutorial`.
|
||||
4. Trace the active dimension to one referenced biome, change one low-risk display or palette value, and save once.
|
||||
5. Wait for hotload, enter newly generated terrain, and inspect it with `/iris what biome`.
|
||||
6. Validate with `/iris pack validate tutorial`, then close with `/iris studio close`.
|
||||
|
||||
The loop passes when the editor binds the generated schema, hotload succeeds, pack validation has no blocking errors, and newly generated chunks show the change. Create a separate production world only after this gate. A rejected runtime-contract change such as dimension height requires closing and reopening Studio; it is not evidence that hotload is broken.
|
||||
|
||||
### Recovery
|
||||
|
||||
| Symptom | Meaning | Recovery |
|
||||
|---|---|---|
|
||||
| `open` reports blocking validation errors | Pack graph cannot safely build an engine | Run the platform's `pack validate` form, fix the first blocking error, and retry; do not bypass validation |
|
||||
| Save reports hotload failure | New data/runtime build failed and the previous runtime may remain active | Fix the first console error and save again before making unrelated edits |
|
||||
| Height, logical height, or dimension type change is rejected | Change violates `IrisDimensionRuntimeContract` | Close Studio and reopen; on modded, restart when regenerated dimension-type datapacks require registry reload |
|
||||
| Valid change is invisible | Existing chunks are already materialized or the edited resource is unreachable | Move to new chunks and trace the active dimension graph; use focus/buffet modes for isolation |
|
||||
| Workspace has no autocomplete or stale resource keys | Schemas were not generated/refreshed or the editor did not open the workspace | Run `studio update`, then open the pack's `.code-workspace`; on headless servers open it manually |
|
||||
| Studio world disappears after restart | Studio worlds are intentionally transient and purged | Reopen the pack; content under `packs/<key>/` remains the source of truth |
|
||||
|
||||
## What Studio Is
|
||||
|
||||
@@ -10,7 +47,7 @@ Related: see `04 - Commands & Permissions.md`, `05 - Concepts & Pack Layout.md`,
|
||||
|---------|----------|
|
||||
| Pack workspace | Packs live under the platform data directory folder named `packs` (`StudioSVC.WORKSPACE_NAME`). |
|
||||
| Studio world | Opened from a pack dimension key; uses a studio chunk generator with live file watching. |
|
||||
| Hotload | On studio worlds only: a low-priority looper polls pack files; when content changes, `EngineHotloader` reloads the pack data and rebuilds engine runtime under a lifecycle lock. |
|
||||
| Hotload | On ordinary Studio worlds only: a low-priority looper polls pack files; when content changes, `EngineHotloader` waits for already-admitted top-level Bukkit chunk stages, reloads the pack data, and rebuilds engine runtime under exclusive generator control. Fair stage admission keeps later chunk stages behind the waiting transition, and Studio close uses the same drain boundary. Biome Buffet resolves its chunk focus and completes any required complex hotload under exclusive admission before that noise stage opens a generation session, then downgrades directly to one ordinary stage permit. |
|
||||
| Hotload contract | `IrisDimensionRuntimeContract` refuses hotload if dimension type key, min height, total height, or logical height change. Restart the world after those edits. |
|
||||
| Non-studio worlds | No pack file watcher looper; production worlds keep the pack snapshot installed at create/update time. |
|
||||
|
||||
@@ -49,6 +86,42 @@ Root: `/iris studio` (aliases `std`, `s`). Implemented by `CommandStudio` + `Stu
|
||||
|
||||
Permissions and the full `/iris` tree: see `04 - Commands & Permissions.md`.
|
||||
|
||||
## Jigsaw Studio (Bukkit)
|
||||
|
||||
`/iris jigsaw` opens one selected structure graph through the transient Studio lifecycle, but chooses `JigsawStudioGenerator` for that activation without persisting a special dimension mode. The owner enters in creative. Planar Studio has six rotation-independent workcells in a compact three-column by two-row layout: Blank, End Cap, Hallway, L Junction, T Junction, and Cross Junction. Spatial Studio has one workcell. There is no orientation, permutation, piece, or derived-rotation gallery.
|
||||
|
||||
Each planar floor is light-gray wool with a red canonical topology glyph and sea-lantern caps at its face-center connector positions. Every workcell has an independent width, height, depth, enabled state, and optional author label. Those dimensions are capacity only: changing one never rewrites a variant object, and the complete change is rejected if any existing variant would no longer fit. Each owned variant has its own exact width, height, depth, and optional label, so one End Cap can be a `16×3×3` longhouse while another End Cap in the same workcell remains `3×3×3`. Per-variant growth or lossless shrink preserves in-bounds canonical content and moves canonical connector payloads and sockets to the new face centers; cropped stored content, connector collisions, or shared/read-only objects reject the transaction. Capacity changes require close/reopen to regenerate the compact layout, while resizing the loaded variant reloads that cell in place. A disabled planar workcell remains editable but is excluded from assembly and vanilla export; a full-volume red stained-glass display marks it and is recreated when its origin chunk reloads. Existing planar variants are rotated into the archetype's canonical display orientation and inverse-rotated during capture, while their piece resources, dimensions, labels, and pool entries remain distinct.
|
||||
|
||||
Create a default planar, Iris-native graph with:
|
||||
|
||||
```text
|
||||
/iris jigsaw create <dimension> <key>
|
||||
```
|
||||
|
||||
`key` is the structure's internal resource path: `village/demo` writes `structures/village/demo.json` and becomes the key used by Iris placements and later editing. Named arguments `structure=` and `name=` are aliases for `key=`; they do not select a separate vanilla structure or template. Omitted options default to `mode=planar`, `compatibility=iris`, `width=16`, `height=16`, `depth=16`, and `seed=1337`; `mode=` completes `planar` or `spatial`, while `compatibility=` completes `iris` or `vanilla`.
|
||||
|
||||
New Iris-compatible planar projects contain one owned piece for every archetype, assign every piece to the weighted `variant-1` structure theme, and mark the End piece terminal. New vanilla-compatible projects contain the same six owned pieces but omit Iris theme and terminal-rule metadata. Open an owned graph with `/iris jigsaw open <dimension> <key>` or the equivalent `edit`/`reopen` alias. Existing unowned Iris graphs use `adopt inspect` then `adopt apply`; managed datapack imports must be cloned. Registered vanilla or datapack jigsaws use `convert`, which creates a separate owned Iris graph.
|
||||
|
||||
The owner can open the six-row control GUI by right-clicking its protected chest, running `/iris jigsaw menu`, or starting three sneaks within 1.5 seconds. Walking into a workcell also makes that physical cell the owner's next menu selection. The GUI selects workcells, loads and creates variants, independently resizes variants, changes workcell capacity or enabled state, adjusts exact pool-entry weights and chances, edits theme membership and piece rules, toggles mandatory caps, navigates to the live preview, and deletes inactive variants or the complete project. Destructive actions require a second confirmation within 10 seconds. **New Blank Variant** clones the active owned piece's complete metadata and every exact pool membership but creates an empty object with the same dimensions; **Duplicate This Cell's Variant** preserves the same metadata and memberships while copying only that source object's bytes. **Duplicate All Enabled Cells as Family** atomically clones the loaded owned variant in every enabled workcell and rebinds the complete family together. All duplication uses service-generated keys and requires active owned variants with owned pool memberships. A duplication clicked during dirty or in-flight autosave is queued once, expedites autosave, and continues automatically only while the request, session, and source variants still match. Iris never chooses a first or lexicographically sorted pool as a fallback; use `/iris jigsaw piece create <poolKey> <pieceKey>` for an empty or unassigned workcell.
|
||||
|
||||
The **Toolbox** page gives the player named stick items bound to the current Studio request and the selected workcell, variant, pool entry, or action. Variant/workcell rename sticks are renamed in an anvil, right-clicked to apply the 64-code-point label, and sneak-right-clicked to reset; control characters and section-sign formatting are rejected. Right-clicking another valid tool performs its action or opens the exact GUI context needed for capacity, per-variant size, themes, or rules. Bound tools use schema `2`; schema-`1` tools and sticks from a replaced or closed Studio are rejected. The active variant uses a jigsaw-block icon, valid evaluation uses an emerald, and lime dye is reserved for the explicitly labeled theme-membership toggle. Destructive stick tools also require a second right-click within 10 seconds.
|
||||
|
||||
Building, marker, container, and machine changes inside a loaded owned workcell autosave after a 40-tick quiet period. Fresh untouched workcells report **Autosaved**. Later edits replace the pending capture identity, and a busy autosave retries until the current save/load/graph barrier permits it. Opening Mojang's jigsaw-block UI starts a persistent owning-region NBT watch; changed tile data marks the workcell dirty, and commands, tools, teleport/world changes, quit, graph operations, **Flush Autosave Now**, close, and enabled-world unload request a final tile snapshot before proceeding. Tracked events include block placement, breakage, buckets, growth, fluids, pistons, redstone, explosions, block-state interactions, recognized mutating commands, inventory click/drag/close plus internal move/pickup, and furnace, brewing-stand, dispenser, and crafter activity. **Flush Autosave Now** and `/iris jigsaw save` only request an immediate flush; if a barrier or scheduler prevents capture from starting, the same pending autosave remains queued for retry. They are not a required authoring step. Paper drains pending work synchronously during disable. A forced Folia plugin disable occurs after Folia rejects new region tasks, so close Studio or wait for `status` to report no pending autosave before a reload or server stop; that late disable hook cannot guarantee a new final cross-region capture. An external integration that bypasses Bukkit events must call `JigsawStudioService.markDirty(...)` or `markAllDirty(...)`.
|
||||
|
||||
Each committed graph is compiled and assembled automatically with seed `1337`. The GUI reports `PENDING`, `VALID`, `WARNING`, `INVALID`, or `STALE`, the selected theme, piece count, and current detail. Iris keeps the assembled blocks on the negative-X side for the active Studio session, replaces them after later commits, and protects the complete preview bounds from edits, fluids, pistons, fire, growth, explosions, entities, and redstone. The live renderer accepts at most 250,000 explicit blocks; a larger result becomes `INVALID` with the render-limit diagnostic. **Go to Preview** or `/iris jigsaw preview goto` teleports above it. This live block preview is separate from `/iris jigsaw preview assemble`, which remains a temporary player-local particle diagnostic for an arbitrary seed.
|
||||
|
||||
Structure themes select one weighted family before assembly. **Duplicate All Enabled Cells as Family** allocates the next `variant-<n>` theme by default, clones the currently loaded owned variant from every enabled workcell with its exact object size and label, duplicates their pool memberships, assigns the new pieces to that family, and atomically loads the new family across those workcells. Individual loaded owned variants can join one or more declared themes; an empty theme list makes a piece available to every selected theme. Pool membership `chance` is an independent `0..1` eligibility gate applied before its positive relative weight. Piece rules constrain minimum/maximum depth, minimum/maximum placements, and terminal status. With mandatory caps enabled, an unresolved open connector must use its direct fallback to place a compatible terminal piece; failure rejects that assembly. Themes, chance gates, piece rules, and mandatory caps are Iris-only and block `VANILLA_PORTABLE` compilation or export when used.
|
||||
|
||||
Mojang's jigsaw UI owns pool, name, target, joint, final state, and both signed priorities; `/iris jigsaw connector channel <channel|none>` changes the saved Iris-only channel for the exact targeted connector. Aqua particles mark the occupied workcell, dark gray marks nearby valid bounds, red marks invalid bounds or incomplete connector identity, lime marks a valid connector without a channel, and a channel receives a deterministic color. The Iris scoreboard switches automatically to Jigsaw context and shows the structure, author workcell label, canonical solver role when the label differs, loaded variant label, state, and `Triple-sneak for controls`; `/iris studio scoreboard` retains its session-only toggle behavior.
|
||||
|
||||
Bukkit has one global Studio project/world and one owning Jigsaw session. Only that owner can control or mutate it. Non-owner edits are cancelled, non-owner commands use a strict informational/communication allowlist, and the control chest plus live preview are protected. Autosave, variant switching, graph changes, opening, closing, and deletion share operation barriers. Close waits for clean state unless `discard=true`; discard is only for deliberately losing pending work.
|
||||
|
||||
Dimensions are capped at 128 blocks on X/Z, 192 on Y, and 2,097,152 blocks in total; planar variant and capacity width/depth must each be at least 3. A workcell capacity change persists only structure metadata, verifies every variant fits, leaves all object bytes unchanged, and requires close/reopen before the physical cage moves. `variant resize` and the **Variant Size** screen change only the selected owned object; lossless growth and shrink preserve in-bounds canonical content, reject cropped explicit air/blocks/tiles/connectors, and relocate planar canonical sockets. The loaded variant reloads in place; inactive variants remain untouched. **Resize to Capacity** or `/iris jigsaw piece expand` is a convenience for setting one selected object exactly to its current capacity. On Folia, each intersecting object chunk is read on its owning region and one graph write begins only after the full snapshot validates.
|
||||
|
||||
Deleting a variant is limited to an owned, inactive variant when another variant remains in that workcell. Project deletion first verifies ownership hashes and scans the pack for external JSON or ownership-manifest references; any reverse reference blocks deletion. A clear result closes Studio and removes the complete owned resource set through a hash-pinned transaction. If the post-close delete fails, the project files remain on disk for recovery.
|
||||
|
||||
This command tree is Bukkit-only. Saved `PLANAR_JIGSAW` and `SPATIAL_JIGSAW` pack resources run in the shared core on Fabric, Forge, and NeoForge, and strict `VANILLA_PORTABLE` graphs can be exported as Minecraft 26.2 datapacks. The complete workflow, commands, marker rules, portability blockers, and recovery steps are in `21 - Jigsaw Structures.md`.
|
||||
|
||||
## Commands (Modded)
|
||||
|
||||
`/iris studio` on Fabric/Forge/NeoForge is implemented by `ModdedStudioCommands`. Supported: `create`/`+`, `open`/`o`, `close`/`x`, `tpstudio`/`stp`, `status`, `vscode`/`vsc`, `update`, `version`, `package`/`pkg`, `regions`, `noise`/`nmap`, `map`/`render`.
|
||||
@@ -93,6 +166,16 @@ With a template: `/iris studio create name=mypack template=overworld` copies tha
|
||||
5. Optional VSCode launch when `studio.openVSCode` is true.
|
||||
6. Datapack install may require restart after create; message tells you to re-run `open` after restart when needed.
|
||||
|
||||
Both ordinary and Jigsaw Studio reuse Iris's startup-loaded datapack runtime only while its pinned compiler-input fingerprint still matches every live `dimensions`, `biomes`, and `snippet` JSON input, the compiler build, and the vanilla-height policy. A changed input, unavailable registry, failed startup recovery, or changed/failed external datapack ingest or removal invalidates reuse and falls back to recovery, compilation, publication, and the existing restart gate; a verified no-change ingest or recovery check restores the prior pin. Object, structure, jigsaw, pool, and ownership edits do not affect generated dimension types or custom biomes and therefore do not force that fallback.
|
||||
|
||||
Ordinary Studio still resolves and teleports through its standard safe entry, may launch the pack workspace, prepares the complete mantle radius, and preserves native structures for generation previews. On Paper 26.2, WorldInit publishes the filtered native-structure placement state once but leaves it uninitialized while native starts, locates, and object-collision volume queries are gated; after the exact FULL entry-chunk request and retention ticket settle and the standard safe-entry teleport step succeeds when applicable, the global scheduler claims that exact level, chunk map, generator, and state, starts its placement initialization, registers the exact concentric-ring futures, enables collision-volume queries, and then lowers the structure gate. The ring searches finish in the background rather than delaying entry or extending Studio ready time, while normal close, full hotload, and complex hotload wait up to 120 seconds for their exact aggregate before mutating or sealing the engine; a synchronous partial-start failure permanently rejects those transitions for that engine because a complete drain cannot be proven.
|
||||
|
||||
Jigsaw Studio publishes an initialized empty native-structure state even when no managed datapack scope exists, never retains or activates the filtered full state, and keeps starts, references, locates, and native collision-volume queries disabled. Its dedicated open kind also skips the standard-entry teleport, workspace launch, procedural generation-cache warm, complete mantle-radius preparation, and ordinary pack-file hotloader before sending the owner once through the selected workcell destination. Jigsaw graph transactions directly invalidate, reload, evaluate, and rematerialize their owned resources; close and reopen Jigsaw Studio to apply unrelated external pack edits.
|
||||
|
||||
Paper-family entry chunks are requested through the urgent asynchronous chunk API before Iris retains them with a plugin ticket; Folia retains its nonblocking ticket bootstrap and confirms the owning region before entry resolution. If an open fails while that exact request remains active, Iris reports the failure without unloading or closing the generator, rejects another Studio open until cleanup succeeds, and queues the transient world for deletion at the next clean startup if it remains active for another 120 seconds. Closing a Studio stops Iris engine maintenance at `WorldUnloadEvent`, waits for the raw backend unload completion and any tracked native ring preparation before sealing the generator, and the 26.2 noise pipeline keeps its generation lease through terrain and heightmap completion. Forced process termination cannot drain in-process ring futures.
|
||||
|
||||
Every Bukkit Studio open writes one `[Studio timing]` line per lifecycle phase with the transient world, `standard` or `jigsaw` kind, phase duration, and cumulative duration where available. The measured phases separate loaded-runtime reuse, external-datapack recovery, compiler-input fingerprinting, datapack compilation/publication, generator preparation, Bukkit world creation, entry-chunk loading, safe-entry resolution, standard teleport, and finalization. Studio engine timing additionally separates prefetch loading, runtime construction, and the generation-cache warm or its Jigsaw-only skip so a slow open can be correlated with a profiler capture.
|
||||
|
||||
## Hotload Details
|
||||
|
||||
- Watcher runs only when `PlatformChunkGenerator.isStudio()` is true (`BukkitChunkGenerator` looper).
|
||||
@@ -169,6 +252,8 @@ Dimension field `studioMode` (`StudioMode` enum) can force special studio genera
|
||||
|
||||
These are dimension JSON fields for studio testing, not production world modes (production engine mode is `mode.type`; see `11 - Dimensions.md`).
|
||||
|
||||
Jigsaw Studio does not add a `studioMode` enum value. `/iris jigsaw open` and `create` select its generator transiently for that one Studio activation.
|
||||
|
||||
## Platform Notes
|
||||
|
||||
| Platform | Studio |
|
||||
@@ -176,4 +261,6 @@ These are dimension JSON fields for studio testing, not production world modes (
|
||||
| Paper/Purpur/Folia (Bukkit plugin) | Full `CommandStudio` + file-watch hotload on studio worlds |
|
||||
| Fabric / Forge / NeoForge | Studio open/create/workspace/package; subset of tooling; no Bukkit-only importers/GUIs that need Bukkit inventory |
|
||||
|
||||
Jigsaw Studio authoring commands are part of the Bukkit row only. Cross-loader pack runtime remains shared; see `21 - Jigsaw Structures.md` and `30 - Platform Differences.md`.
|
||||
|
||||
Pack JSON contracts are shared across platforms. Schemas are built from the same core models.
|
||||
|
||||
+33
-10
@@ -4,6 +4,24 @@ A dimension is the root pack object for a world type. File location is `dimensio
|
||||
|
||||
Related: see `05 - Concepts & Pack Layout.md`, `10 - Studio & VSCode Schemas.md`, `12 - Regions.md`, `14 - Generators & Noise.md`, `15 - Caves & Carving.md`, `22 - Native Structures & Datapacks.md`, `26 - Example - Minimal Dimension.md`.
|
||||
|
||||
## Tutorial outcome
|
||||
|
||||
Build the smallest dimension graph first: one dimension, one region, one biome, and one generator. Open that graph in Studio and confirm solid terrain before enabling caves, structures, deposits, or external datapacks. The complete four-file example is in `26 - Example - Minimal Dimension.md`; the field reference below explains how to extend it.
|
||||
|
||||
Dimension height and environment are world contracts, not ordinary visual tuning. Decide them before creating a production world and use a disposable Studio world for revisions.
|
||||
|
||||
### Prerequisites and file placement
|
||||
|
||||
Start with a writable pack directory and operator or gamemaster access. Place the root object at `plugins/Iris/packs/mypack/dimensions/mypack.json` on Bukkit, or `config/irisworldgen/packs/mypack/dimensions/mypack.json` on a mod loader. The minimal complete graph also needs `regions/starter.json`, `biomes/starter.json`, and `generators/flat.json`; copy the exact four files from `26 - Example - Minimal Dimension.md` before loading the pack.
|
||||
|
||||
### Build and verify
|
||||
|
||||
1. Set the dimension file to the minimal JSON under **Minimal Dimension JSON** below. Keep its dimension load key (`mypack`) equal to the file name.
|
||||
2. Create the three referenced resources from the minimal walkthrough and validate: Bukkit `/iris pack validate pack=mypack`; modded `/iris pack validate mypack`.
|
||||
3. Open the live authoring pack with `/iris studio open mypack seed=1337` on Bukkit or `/iris studio open mypack 1337` on modded.
|
||||
4. Generate new chunks and inspect the build floor, terrain top, fluid level, sky, and biome. A successful baseline has solid terrain, the `starter` biome, and no unresolved resource keys in validation or the console.
|
||||
5. If the Studio world is empty, confirm all four file paths and exact keys before changing noise. If height or environment changes are rejected, close and reopen Studio; those values are bound to the running dimension contract.
|
||||
|
||||
## Role in the Pack Graph
|
||||
|
||||
```
|
||||
@@ -149,13 +167,15 @@ If mode construction fails, the engine logs a warning and falls back to `OVERWOR
|
||||
|
||||
| Field | Type | Default | Notes |
|
||||
|-------|------|---------|-------|
|
||||
| `structures` | `IrisStructurePlacement[]` | empty | Dimension-level Iris structure placements |
|
||||
| `structures` | `IrisStructurePlacement[]` | empty | Dimension-level Iris/native structure placements; editable Iris structures support surface, height-band, and cave anchors |
|
||||
| `importedStructures` | `IrisImportedStructureControl` | default | Vanilla/mod/datapack structure allow/deny and adjustments |
|
||||
| `importedFeatures` | `IrisImportedFeatureControl` | default off | Optional vanilla feature decoration pass |
|
||||
| `datapackImports` | string[] | empty | External datapack URLs requested by this pack |
|
||||
| `datapackImports` | string[] | empty | External datapack URLs owned by this dimension; managed structures generate and locate only in dimensions declaring the source |
|
||||
|
||||
Structure placement and native control details: see `18 - Structures Overview.md`, `22 - Native Structures & Datapacks.md`.
|
||||
|
||||
Dimension-level placements are considered throughout the dimension. `anchor: LEGACY` preserves the historical `underground` switch; explicit `SURFACE`, `HEIGHT_BAND`, `CAVE_FLOOR`, `CAVE_CEILING`, `CAVE_CENTER`, and `CAVE_ANY` make the vertical contract unambiguous. Cave anchors use Iris carved-space data and apply only to editable `structures`, not the `nativeStructures` backend; see `15 - Caves & Carving.md` and `21 - Jigsaw Structures.md`.
|
||||
|
||||
## Loot, Spawns, Drops, Studio Debug
|
||||
|
||||
| Field | Type | Default | Notes |
|
||||
@@ -237,14 +257,17 @@ Matches studio starter plus an explicit mode (recommended):
|
||||
|
||||
## How To: Make a Dimension
|
||||
|
||||
1. Create a pack (`/iris studio create name=mypack`) or copy a template.
|
||||
2. Edit `dimensions/<key>.json`: set `name`, `regions`, `dimensionHeight`, `fluidHeight`, `mode.type`.
|
||||
3. Ensure every region key exists under `regions/` (see `12 - Regions.md`).
|
||||
4. Set land/sea styles and zooms only after basic terrain generates.
|
||||
5. Add `ores` / `deposits` / `caveProfile` / structures after biomes render correctly.
|
||||
6. Open studio: `/iris studio open mypack`. Iterate with hotload.
|
||||
7. For isolation: set `"focusRegion": "starter"` or `"focus": "starter"` while authoring one biome.
|
||||
8. When ready for a permanent world, create a world from the pack key (`06 - Worlds & Lifecycle.md`). Do not change height/logicalHeight without recreating the world dimension type.
|
||||
1. Create a starter pack with `/iris studio create name=mypack` or copy a known-good template.
|
||||
2. Open `dimensions/mypack.json`. Set `name`, `regions`, `dimensionHeight`, `logicalHeight`, `fluidHeight`, `environment`, and `mode.type` explicitly.
|
||||
3. Create every referenced region under `regions/`; start with one region and one land biome (`12 - Regions.md`).
|
||||
4. Validate the pack, then open Studio with `/iris studio open mypack seed=1337`.
|
||||
5. Confirm the build floor, fluid level, sky/environment behavior, and solid terrain in newly generated chunks. Fix this baseline before changing zoom or adding content.
|
||||
6. Set `"focusRegion": "starter"` or `"focus": "starter"` while isolating one region or biome. Remove the focus field before packaging.
|
||||
7. Tune land/sea styles and zooms. Add caves, ores, deposits, and structures one subsystem at a time, validating after each new resource edge.
|
||||
8. Close and reopen Studio after changing height, logical height, environment-derived dimension type, or another runtime-contract field.
|
||||
9. Create a permanent world only after the Studio baseline and pack validation pass (`06 - Worlds & Lifecycle.md`). Recreate the production world rather than changing its dimension type or height contract in place.
|
||||
|
||||
Success means a clean Studio open, no unresolved region/biome/generator keys, and repeatable terrain on seed `1337` after reopening the project.
|
||||
|
||||
## Common Author Mistakes
|
||||
|
||||
|
||||
+30
-8
@@ -4,6 +4,23 @@ A region is a mid-level spatial unit inside a dimension. File location is `regio
|
||||
|
||||
Related: see `05 - Concepts & Pack Layout.md`, `11 - Dimensions.md`, `13 - Biomes.md`, `16 - Surfaces, Decorators & Deposits.md`, `20 - Object Placement.md`, `15 - Caves & Carving.md`.
|
||||
|
||||
## Tutorial outcome
|
||||
|
||||
Add one region to an already working dimension and make one biome fill that region. Keep sea, shore, cave, object, and structure lists empty until the land path resolves; this separates region selection problems from content-placement problems.
|
||||
|
||||
### Prerequisites and file placement
|
||||
|
||||
Use a pack whose dimension and one biome already validate. Create `regions/tutorial.json` with the complete **Minimal Region JSON** below, replace `starter` with the exact load key of the existing biome, and add `"tutorial"` to the dimension's `regions` array.
|
||||
|
||||
### Build and verify
|
||||
|
||||
1. Keep `rarity` at `1` and put one root biome in `landBiomes`; do not list a child biome here.
|
||||
2. Temporarily set `"focusRegion": "tutorial"` on the dimension.
|
||||
3. Validate the pack, then open Studio on seed `1337` using the platform command in `10 - Studio & VSCode Schemas.md`.
|
||||
4. Generate new chunks and run `/iris what region` while standing in them. Success is a consistent `tutorial` region whose biome resolves without warnings.
|
||||
5. If another region appears, verify the dimension reference and the `focusRegion` spelling. If terrain is missing, verify the biome key and its generator; region rarity and zoom cannot repair a broken resource edge.
|
||||
6. Remove `focusRegion`, reopen Studio, and sample new chunks before adding sea, shore, or cave lists.
|
||||
|
||||
## Role
|
||||
|
||||
Dimensions pick regions by noise (`regionStyle` / `regionZoom` / region `rarity`). Within a region, land/sea/shore/cave biome lists pick biomes (also rarity-weighted). Child biomes are **not** listed on the region; only root parents go in the region arrays. Children are declared on the parent biome (`children` field).
|
||||
@@ -65,7 +82,7 @@ Keys are biome load keys under `biomes/` (e.g. `temperate/plains`, `carving/drip
|
||||
|-------|------|---------|-------|
|
||||
| `objects` | `IrisObjectPlacement[]` | empty | Region-wide `.iob` placements |
|
||||
| `proceduralObjects` | `IrisProceduralObjects` | empty | Trees/ruins/formations/coral/fungi/crystals generated procedurally |
|
||||
| `structures` | `IrisStructurePlacement[]` | empty | Jigsaw / native structure placements |
|
||||
| `structures` | `IrisStructurePlacement[]` | empty | Region-scoped jigsaw/native placements; editable Iris structures may use explicit cave anchors |
|
||||
| `entitySpawners` | string[] | empty | `IrisSpawner` load keys |
|
||||
| `effects` | `IrisEffect[]` | empty | Packet ambient effects (potions, sounds, particles) |
|
||||
| `loot` | `IrisLootReference` | empty | Region loot |
|
||||
@@ -77,6 +94,8 @@ Keys are biome load keys under `biomes/` (e.g. `temperate/plains`, `carving/drip
|
||||
|
||||
Deposit precedence (documented on fields): biome variants → region variants → dimension variants; first match wins at each tier.
|
||||
|
||||
Region `structures[]` is evaluated where that region owns the start chunk center. A cave anchor searches existing carved-space mantle data inside that chunk and can further restrict the actual anchor with `caveBiomes`; it does not require the placement to be duplicated on every cave biome. See `21 - Jigsaw Structures.md` for distribution and anchor fields.
|
||||
|
||||
## Overworld Sample: Temperate
|
||||
|
||||
Path: `…/packs/overworld/regions/temperate.json`
|
||||
@@ -127,13 +146,16 @@ Land-only dimension (no ocean shoreline generated):
|
||||
|
||||
## How To: Make a Region
|
||||
|
||||
1. Create `regions/<key>.json`.
|
||||
2. Set `name` and `rarity`.
|
||||
3. List **root** biomes only under `landBiomes` (and sea/shore/cave as needed). Keys must match files under `biomes/` (subfolders become path segments in the key).
|
||||
4. Add the region key to the dimension’s `regions` array.
|
||||
5. Tune `landBiomeZoom` / `seaBiomeZoom` / `shoreBiomeZoom` after biomes look right.
|
||||
6. Optionally add regional `deposits`, `ores`, `objects`, `structures`, `caveProfile`.
|
||||
7. Studio: set dimension `"focusRegion": "<key>"` to generate only that region while authoring.
|
||||
1. Create `regions/<key>.json` from the minimal region above.
|
||||
2. Set `name`, keep `rarity: 1`, and list one existing **root** biome under `landBiomes`.
|
||||
3. Add the region key to the dimension's `regions` array.
|
||||
4. Set dimension `"focusRegion": "<key>"`, validate, and open Studio on seed `1337`.
|
||||
5. Generate new chunks until the biome appears consistently. If it does not, verify the exact biome file path before tuning rarity or noise.
|
||||
6. Add sea and shore biomes together, then cave biomes, validating each path separately.
|
||||
7. Remove `focusRegion`; add a second region and only then tune rarity and region zoom while sampling broad new areas.
|
||||
8. Add regional deposits, ores, objects, structures, and cave profiles after selection is proven.
|
||||
|
||||
The tutorial passes when the focused region generates, the unfocused dimension selects it among peers, and validation reports no missing biome keys.
|
||||
|
||||
## Resolution Notes
|
||||
|
||||
|
||||
+27
-6
@@ -4,6 +4,22 @@ A biome is the primary surface/authoring unit for terrain height, block layers,
|
||||
|
||||
Related: see `12 - Regions.md`, `14 - Generators & Noise.md`, `16 - Surfaces, Decorators & Deposits.md`, `17 - Trees, Fungi, Coral, Crystals, Formations, Ruins.md`, `19 - Objects.md`, `20 - Object Placement.md`, `23 - Loot, Entities, Spawners, Markers.md`.
|
||||
|
||||
## Tutorial outcome
|
||||
|
||||
Create a visible, selectable biome with a known surface and height before adding variants or decoration. Use the minimal JSON near the end of this guide, attach it to one focused region, and keep the seed fixed while testing.
|
||||
|
||||
### Prerequisites and file placement
|
||||
|
||||
Use a validating dimension, a region listed by that dimension, and `generators/flat.json` from `26 - Example - Minimal Dimension.md`. Save the complete **Minimal Biome JSON** below as `biomes/tutorial/meadow.json`, reference `tutorial/meadow` from the region's `landBiomes`, and temporarily set dimension `focus` to the same key.
|
||||
|
||||
### Build and verify
|
||||
|
||||
1. Keep both derivative fields at `minecraft:plains`, one grass surface layer, and the flat generator link until the resource graph works.
|
||||
2. Validate the pack and open Studio on seed `1337`.
|
||||
3. Generate new chunks, then run `/iris what biome`. Success is the `tutorial/meadow` load key, a grass surface, and a constant terrain height with no unresolved generator warnings.
|
||||
4. If the biome does not appear, compare the region entry, biome path, and dimension `focus` character-for-character. If the biome appears over void terrain, validate `generators/flat.json` and its link before changing height values.
|
||||
5. Remove `focus`, reopen Studio, and confirm the biome can be selected naturally. Add children, decorators, objects, and custom derivatives only after this baseline passes.
|
||||
|
||||
## Role
|
||||
|
||||
| Layer | Responsibility |
|
||||
@@ -166,7 +182,7 @@ Spawn groups: `MONSTER`, `CREATURE`, `AMBIENT`, `AXOLOTLS`, `UNDERGROUND_WATER_C
|
||||
| `decorators` | `IrisDecorator[]` | Tall grass, cactus, kelp-style placements (see `16 - Surfaces, Decorators & Deposits.md`) |
|
||||
| `objects` | `IrisObjectPlacement[]` | `.iob` placements |
|
||||
| `proceduralObjects` | `IrisProceduralObjects` | Procedural trees/coral/etc. |
|
||||
| `structures` | `IrisStructurePlacement[]` | Jigsaw / native structures |
|
||||
| `structures` | `IrisStructurePlacement[]` | Jigsaw/native placements; cave-biome lists contribute only editable Iris placements using a resolved cave anchor |
|
||||
| `floatingChildBiomes` | `IrisFloatingChildBiomes[]` | Floating islands using another biome’s visuals |
|
||||
| `mergeFloatingChildBiomes` | boolean | When true, all floating entries sample independently |
|
||||
| `deposits` | `IrisDepositGenerator[]` | Biome deposits |
|
||||
@@ -180,6 +196,8 @@ Spawn groups: `MONSTER`, `CREATURE`, `AMBIENT`, `AXOLOTLS`, `UNDERGROUND_WATER_C
|
||||
| `blockDrops` | `IrisBlockDrops[]` | Custom drops |
|
||||
| `caveProfile` | `IrisCaveProfile` | Biome cave profile override |
|
||||
|
||||
A surface biome contributes all of its `structures[]` placements when it owns the start chunk center. The cave biome sampled at that center contributes only placements whose resolved anchor is `CAVE_FLOOR`, `CAVE_CEILING`, `CAVE_CENTER`, or `CAVE_ANY`; surface/height-band placements in cave-biome JSON are ignored. `caveBiomes` on the placement is an additional allowlist rechecked against the cave/mantle biome at each actual anchor candidate. See `15 - Caves & Carving.md` and `21 - Jigsaw Structures.md`.
|
||||
|
||||
## Floating child biomes (`IrisFloatingChildBiomes`)
|
||||
|
||||
`floatingChildBiomes` builds floating terrain above columns owned by the parent biome. Each entry can reuse the parent or reference another biome for its generators, layers, derivative, decorators, and surface objects. With `mergeFloatingChildBiomes: false` (default), `pickerStyle` and `rarity` select one entry per column; with it true, every entry samples independently and islands may overlap.
|
||||
@@ -358,11 +376,14 @@ Requires a matching generator file under `generators/` (starter uses `generators
|
||||
2. Set `name`, `derivative`, `vanillaDerivative`.
|
||||
3. Add at least one `generators` link and a generator JSON under `generators/`.
|
||||
4. Define `layers` from topsoil down (grass → dirt → stone blend).
|
||||
5. Optionally set `wall` for cliffs, `decorators` for grass, `objects` for trees/clutter.
|
||||
6. For variants inside a parent, create a child biome file and list its key in the parent’s `children`.
|
||||
7. For custom colors/tags/mobs, add `customDerivitives` with a unique `id` and `category`.
|
||||
8. Attach the biome to a region: land → `landBiomes`, ocean floor → `seaBiomes`, beach → `shoreBiomes`, cave → `caveBiomes`.
|
||||
9. Studio test: dimension `"focus": "temperate/plains"` forces only that biome.
|
||||
5. Attach the biome to exactly one region role: land → `landBiomes`, ocean floor → `seaBiomes`, beach → `shoreBiomes`, cave → `caveBiomes`.
|
||||
6. Set dimension `"focus": "<biome-key>"`, validate, open Studio, and inspect newly generated chunks. Confirm surface blocks, terrain Y, fluid relationship, and vanilla structure eligibility.
|
||||
7. Optionally set `wall` for cliffs, then add decorators and objects one group at a time.
|
||||
8. For variants inside a parent, create a child biome file and list its key in the parent's `children`; do not also list the child as a region root.
|
||||
9. For custom colors, tags, or mobs, add `customDerivitives` with a unique `id` and `category`, then reopen the world if the generated biome registry changed.
|
||||
10. Remove `focus` and verify the biome appears through ordinary region selection.
|
||||
|
||||
Success means the biome is visible through Iris inspection tools, uses the intended derivative, and appears both focused and naturally selected without unresolved keys.
|
||||
|
||||
## Generator Link How-To
|
||||
|
||||
|
||||
@@ -24,13 +24,43 @@ Biome JSON does not embed generators. It references them by key:
|
||||
|
||||
`IrisBiomeGeneratorLink` loads `generators/<generator>.json`, samples height in 0..1, then lerps to `min`..`max` relative to fluid height. Negative ranges produce ocean floors.
|
||||
|
||||
## Authoring workflow
|
||||
## Tutorial: add and tune a height generator
|
||||
|
||||
1. Create `generators/<name>.json` with `seed`, `interpolator`, and at least one `composite` entry.
|
||||
2. Reference that key from every biome that should share the shape (`generators[].generator`).
|
||||
3. Tune `min`/`max` per biome for local relief; leave the generator file for global shape and frequency.
|
||||
4. Hotload in studio; regenerate nearby chunks to verify blending across biome edges (`interpolator.horizontalScale`).
|
||||
5. Optional: replace a style's built-in `NoiseStyle` with `expression` or `imageMap` for custom fields.
|
||||
Prerequisites are a validating pack, one biome that can be focused, and a fixed test seed. Save this complete baseline as `generators/tutorial-hills.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"interpolator": { "function": "NONE", "horizontalScale": 1 },
|
||||
"seed": 310,
|
||||
"composite": [
|
||||
{
|
||||
"seed": 310,
|
||||
"style": { "style": "FLAT" }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Reference it from the focused biome; this is a field excerpt, not a second file:
|
||||
|
||||
```json
|
||||
{
|
||||
"generators": [
|
||||
{ "generator": "tutorial-hills", "min": 16, "max": 48 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. Validate the pack and open it in Studio on seed `1337`.
|
||||
2. Generate new chunks. The first result should be a flat surface because `FLAT` produces a constant signal; this proves the file path, biome link, and height range.
|
||||
3. Change only `style.style` from `FLAT` to `IRIS`, then generate another new area. Keep generator seed and biome `min`/`max` fixed so the new relief is attributable to the style.
|
||||
4. Adjust generator `zoom` for feature scale, then biome `min`/`max` for relief. Do not change both in the same comparison.
|
||||
5. Add a second neighboring biome using the same generator and inspect the boundary. Tune `interpolator.horizontalScale` only after both isolated biomes look correct.
|
||||
6. Add composite layers, fracture, expressions, or image maps one at a time. Recheck performance after nested fracture or expensive interpolation.
|
||||
|
||||
The tutorial passes when seed `1337` reproduces the same shape after Studio reopen, neighboring biomes blend as intended, and pack validation resolves every generator/expression/image key.
|
||||
|
||||
If the result is flat after switching to `IRIS`, confirm that the biome references `tutorial-hills` and that new chunks were generated. If terrain becomes void, restore the complete baseline above and check validation for a missing generator or invalid style before changing noise values again.
|
||||
|
||||
## Generator file (`IrisGenerator`)
|
||||
|
||||
|
||||
@@ -4,6 +4,29 @@ Iris carves caves itself during mantle generation via `MantleCarvingComponent` a
|
||||
|
||||
Related: `11 - Dimensions.md`, `12 - Regions.md`, `13 - Biomes.md`, `14 - Generators & Noise.md`, `16 - Surfaces, Decorators & Deposits.md`, `17 - Trees, Fungi, Coral, Crystals, Formations, Ruins.md`, `20 - Object Placement.md`, `22 - Native Structures & Datapacks.md`.
|
||||
|
||||
## Tutorial: carve a controlled test volume
|
||||
|
||||
Start with a validating `OVERWORLD` pack whose surface and fluid height are already correct. Add this complete field set to the root object in `dimensions/<key>.json`; it uses the production defaults for density but confines the first test and prevents surface or liquid openings:
|
||||
|
||||
```json
|
||||
{
|
||||
"carvingEnabled": true,
|
||||
"caveProfile": {
|
||||
"enabled": true,
|
||||
"verticalRange": { "min": 0, "max": 64 },
|
||||
"allowSurfaceBreak": false,
|
||||
"allowWater": false,
|
||||
"allowLava": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Record seed `1337` and surface coordinates in a Studio world before enabling the profile.
|
||||
2. Add the fields above to the existing dimension JSON, validate the pack, and reopen Studio if the change is not accepted by the running engine.
|
||||
3. Generate new chunks and inspect below the surface. Success is carved air within the configured vertical range, an intact surface, and no water or lava placed by the cave profile.
|
||||
4. If no caves appear, confirm dimension `mode.type` is `OVERWORLD`, `useMantle` and `carvingEnabled` are true, and the effective biome or region profile is not overriding this dimension profile. Test only fresh chunks.
|
||||
5. Once the void shape is proven, add one biome key to a region's `caveBiomes`, then add cave layers and decorators. Enable water, lava, or surface breaks one setting at a time so each change remains observable.
|
||||
|
||||
## Architecture (author-relevant)
|
||||
|
||||
1. Dimension `carvingEnabled` must be true (default).
|
||||
@@ -122,6 +145,64 @@ Cave biomes are normal biome JSON used only underground:
|
||||
|
||||
Surface biomes still provide height generators; cave biomes typically omit height generators or use fillers—the carve step removes solid first.
|
||||
|
||||
## Cave-anchored jigsaw structures
|
||||
|
||||
Editable Iris jigsaws can resolve starts against the carved-space mantle instead of the surface or a blind Y band. Put the placement in `structures[]` on a dimension, region, surface biome, or cave biome and use one of the explicit cave anchors; cave-biome `structures[]` ignores non-cave placements.
|
||||
|
||||
```json
|
||||
{
|
||||
"structures": [
|
||||
{
|
||||
"structures": ["stronghold/demo"],
|
||||
"placementId": "stronghold-demo-cave-floor",
|
||||
"distribution": "RANDOM_SPREAD",
|
||||
"spacing": 24,
|
||||
"separation": 8,
|
||||
"salt": 984211,
|
||||
"anchor": "CAVE_FLOOR",
|
||||
"minHeight": -48,
|
||||
"maxHeight": 80,
|
||||
"caveBiomes": ["carving/deep"],
|
||||
"caveAnchorAttempts": 12,
|
||||
"caveAnchorScanStep": 1,
|
||||
"caveMinimumClearance": 5,
|
||||
"terrain": {"mode": "PRESERVE"}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Default | Runtime behavior |
|
||||
|---|---|---|
|
||||
| `anchor` | `LEGACY` | Use `CAVE_FLOOR`, `CAVE_CEILING`, `CAVE_CENTER`, or `CAVE_ANY` to search carved cells |
|
||||
| `minHeight` / `maxHeight` | `-2032` / `2032` | Inclusive world-Y scan band, clipped inside the dimension's usable height |
|
||||
| `caveBiomes` | empty | Optional allowlist checked against the cave/mantle biome at the candidate anchor; keys are trimmed, case-normalized, and may include or omit the namespace |
|
||||
| `caveAnchorAttempts` | `8` | Deterministic unique X/Z columns tested inside the selected start chunk; runtime clamps to `1..64` |
|
||||
| `caveAnchorScanStep` | `1` | Vertical scan increment; runtime clamps to `1..16`; values above one can skip valid one-block anchors |
|
||||
| `caveMinimumClearance` | `3` | Required contiguous vertical carved run; runtime clamps to `1..64` |
|
||||
| `underwater` | `false` | For cave anchors, require a dry cavern cell: ordinary cavern air must be above `caveLavaHeight`, explicit water/lava is rejected, and forced-air cavern matter remains dry below that threshold; `true` permits fluid cavern cells |
|
||||
|
||||
Geometry and alignment are exact:
|
||||
|
||||
| Anchor | Candidate test | Alignment after assembly |
|
||||
|---|---|---|
|
||||
| `CAVE_FLOOR` | Candidate is carved, cell below is not carved, and clearance continues upward | Lowest structure bound is shifted to anchor Y |
|
||||
| `CAVE_CEILING` | Candidate is carved, cell above is not carved, and clearance continues downward | Highest structure bound is shifted to anchor Y |
|
||||
| `CAVE_CENTER` | Candidate is the actual midpoint of its contiguous carved cavern run, and that run meets the clearance requirement | Bounding-box midpoint is shifted to anchor Y |
|
||||
| `CAVE_ANY` | A clearance-sized carved run is centered around the candidate | Bounding-box midpoint is shifted to anchor Y |
|
||||
|
||||
Selection is deterministic for the world seed, placement identity, and start chunk. Iris visits at most 64 unique columns from the chunk's 256 columns, stops at the first column with matches, and chooses deterministically among every valid anchor found in that column. When no candidate passes, the placement is skipped; Iris does not fall back to a surface or height-band start.
|
||||
|
||||
The cave-anchor `underwater` gate reads `MatterCavern` at the actual anchor, not ocean height at the surface. A null or non-cavern cell is never an anchor. With `underwater: false`, explicit cave water/lava and ordinary cavern air at or below the dimension's `caveLavaHeight` are rejected, while forced-air cavern matter is accepted even below that threshold. With `underwater: true`, fluid cavern cells are allowed but the cell must still be carved cavern matter.
|
||||
|
||||
The test reads one vertical `MatterCavern` column. It proves local clearance only, not that the complete assembled footprint fits the cave. `SOURCE` and `PRESERVE` can therefore leave pieces intersecting surrounding walls. Use `BORE` or `FORCE_CARVE` when the structure must make room, or inspect the complete volume in gameplay when preserving the cavern.
|
||||
|
||||
Scope is decided at chunk center: the surface-biome, cave-biome, region, and dimension lists available there contribute candidate placements, but a cave-biome list contributes cave anchors only. A non-empty placement `caveBiomes` allowlist is then rechecked at each actual X/Y/Z anchor candidate. Cave lookup requires already materialized Iris mantle data; a locator cannot resolve an ungenerated distant cave anchor until terrain generation has produced that mantle.
|
||||
|
||||
Cave anchors are treated as underground placement. Iris does not perform the separate surface-burial shift or clear intersecting surface trees. Piece placement normally resolves to `STRUCTURE_PIECE` underground, except authored `ORGANIC_STILT` and `CEILING_HANG` modes retain their special behavior. Use `terrain.mode: PRESERVE` to keep the cave, `BORE` for a rectangular clearance envelope, or `FORCE_CARVE` for the configured box/rounded/eroded envelope.
|
||||
|
||||
The anchor field is rejected for `nativeStructures`; it applies to editable Iris `structures` only. Complete jigsaw placement and authoring behavior is in `21 - Jigsaw Structures.md`.
|
||||
|
||||
## Overworld examples
|
||||
|
||||
Dimension switch and deepdark band (`dimensions/overworld.json`):
|
||||
@@ -198,15 +279,16 @@ Region cave pool (`regions/temperate.json`):
|
||||
|
||||
Cave biome content (`biomes/carving/amethyst.json` excerpt): floor/wall amethyst, floor buds, ceiling-facing clusters via `"partOf": "CEILING"`, `caveCeilingLayers` for roof materials.
|
||||
|
||||
## Authoring workflow
|
||||
## Extend the controlled cave test
|
||||
|
||||
1. Enable dimension `caveProfile` with a vertical range covering playable Y.
|
||||
2. Add `modules` for tunnels/rooms instead of raising `detailWeight` alone.
|
||||
3. List themed biomes under each region's `caveBiomes` (and optional dimension `carving` bands).
|
||||
4. Paint cave biomes with `layers`, `caveCeilingLayers`, `wall`, ceiling/floor decorators, and cave-only objects.
|
||||
5. For surface sinkholes, keep `allowSurfaceBreak` true and tune `surfaceBreak*` noise; for sealed caves raise `surfaceClearance` and disable surface break.
|
||||
6. Place cave objects with `carvingSupport: CARVING_ONLY` and stilt place modes (`FAST_MIN_STILT` / `ORGANIC_STILT`) to avoid floating props.
|
||||
7. Verify: studio regen, check openings, waterfalls (`waterRequiresFloor`), and lava depth.
|
||||
1. Record a fixed seed and coordinates where the surface, fluid level, and bedrock are already correct.
|
||||
2. Enable the dimension `caveProfile` with a narrow vertical range inside playable Y and no cave-biome decoration yet.
|
||||
3. Add one tunnel or room module. Generate new chunks and verify void shape, surface clearance, water handling, and lava depth.
|
||||
4. Add modules for other shapes instead of raising `detailWeight` alone. Change one density or threshold value per comparison.
|
||||
5. List one themed biome under one region's `caveBiomes`; paint its floor, ceiling, and walls before adding objects.
|
||||
6. Add cave-only objects with `carvingSupport: CARVING_ONLY` and an appropriate stilt mode so props do not float.
|
||||
7. Decide explicitly whether caves may break the surface. Tune `surfaceBreak*` for openings or disable surface break and raise clearance for sealed caves.
|
||||
8. Revisit the recorded surface coordinates and generate fresh cave areas. The tutorial passes when surface terrain is unchanged outside intentional openings and cave content remains inside carved space.
|
||||
|
||||
## Tuning knobs (quick)
|
||||
|
||||
|
||||
@@ -4,6 +4,56 @@ Surface composition is layered block palettes on biomes (and default rock/fluid
|
||||
|
||||
Related: `11 - Dimensions.md`, `12 - Regions.md`, `13 - Biomes.md`, `14 - Generators & Noise.md`, `15 - Caves & Carving.md`, `17 - Trees, Fungi, Coral, Crystals, Formations, Ruins.md`, `20 - Object Placement.md`, `24 - Pack Mods & Snippets.md`.
|
||||
|
||||
## Tutorial: build a surface, flower scatter, and deposit
|
||||
|
||||
Start with the flat generator from `26 - Example - Minimal Dimension.md`. Save the following complete biome as `biomes/tutorial/surface-test.json`, list `tutorial/surface-test` in one region's `landBiomes`, and temporarily set the dimension `focus` to the same key:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Surface Test",
|
||||
"derivative": "minecraft:plains",
|
||||
"vanillaDerivative": "minecraft:plains",
|
||||
"generators": [
|
||||
{ "generator": "flat", "min": 16, "max": 16 }
|
||||
],
|
||||
"layers": [
|
||||
{
|
||||
"minHeight": 1,
|
||||
"maxHeight": 1,
|
||||
"palette": [{ "block": "minecraft:grass_block" }]
|
||||
},
|
||||
{
|
||||
"minHeight": 3,
|
||||
"maxHeight": 3,
|
||||
"palette": [{ "block": "minecraft:dirt" }]
|
||||
}
|
||||
],
|
||||
"decorators": [
|
||||
{
|
||||
"chance": 0.05,
|
||||
"palette": [{ "block": "minecraft:dandelion" }]
|
||||
}
|
||||
],
|
||||
"deposits": [
|
||||
{
|
||||
"minHeight": 0,
|
||||
"maxHeight": 96,
|
||||
"minSize": 3,
|
||||
"maxSize": 6,
|
||||
"minPerChunk": 1,
|
||||
"maxPerChunk": 2,
|
||||
"palette": [{ "block": "minecraft:coal_ore" }],
|
||||
"varience": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. Validate and open Studio on seed `1337`.
|
||||
2. Generate new chunks and inspect a cross-section. Success is one grass block over three dirt blocks, dandelions scattered above valid surfaces, and coal clumps only inside the configured absolute Y band.
|
||||
3. If the surface is wrong, remove `decorators` and `deposits` and verify `layers` first. If flowers do not appear, raise `chance` temporarily and confirm `decorate` remains true on the dimension. If deposits do not appear, verify the absolute Y range intersects the generated terrain and keep the code-authoritative spelling `varience`.
|
||||
4. Remove dimension focus after the biome works, then tune each subsystem independently using the full tables below.
|
||||
|
||||
## Surfaces and material layers
|
||||
|
||||
### Biome palette layer (`IrisBiomePaletteLayer`)
|
||||
@@ -306,13 +356,16 @@ Deepslate remap (`dimensions/overworld.json`):
|
||||
}
|
||||
```
|
||||
|
||||
## Authoring workflows
|
||||
## Tune each surface-detail subsystem
|
||||
|
||||
Start from a focused biome that already produces correct height. Complete and verify each stage before adding the next; this makes a wrong palette, placement filter, or deposit range independently visible.
|
||||
|
||||
### Surface
|
||||
|
||||
1. Define 1–3 `layers` from top soil to subsoil; leave stone to `rockPalette`.
|
||||
2. Set `wall` for cliff biomes; set `seaLayers` for oceans.
|
||||
3. Use `lockLayers` only for mesa stripes.
|
||||
4. Generate fresh Studio chunks and inspect flat ground, slopes, exposed walls, and underwater columns before continuing.
|
||||
|
||||
### Decorators
|
||||
|
||||
@@ -320,6 +373,7 @@ Deepslate remap (`dimensions/overworld.json`):
|
||||
2. Use `partOf` for shore/sea/ceiling-only content.
|
||||
3. For cactus/bamboo, set `stackMin`/`stackMax` and optional `topPalette`.
|
||||
4. Extract repeated decorators into `snippet/decorator/*.json` and reference via pack snippets.
|
||||
5. Verify both places where the decorator should appear and nearby places where its filter should reject it.
|
||||
|
||||
### Deposits
|
||||
|
||||
@@ -327,6 +381,7 @@ Deepslate remap (`dimensions/overworld.json`):
|
||||
2. Region/biome deposits add local minerals.
|
||||
3. Use `depositVariants` for deepslate or mod ore remaps by Y.
|
||||
4. Tune `varience` for clump shape diversity; keep sizes moderate for performance.
|
||||
5. Inspect multiple Y bands in new chunks. The workflow passes when the surface palette is stable, decorators honor their part/filter rules, and deposits remain inside their configured material and height targets.
|
||||
|
||||
## Practical notes
|
||||
|
||||
|
||||
@@ -4,6 +4,46 @@ Procedural objects are baked from JSON settings into deterministic block blobs a
|
||||
|
||||
Related: `13 - Biomes.md`, `12 - Regions.md`, `15 - Caves & Carving.md`, `16 - Surfaces, Decorators & Deposits.md`, `18 - Structures Overview.md`, `19 - Objects.md`, `20 - Object Placement.md`.
|
||||
|
||||
## Tutorial: generate a procedural tree from biome JSON
|
||||
|
||||
Use a validating `OVERWORLD` pack with mantle and decoration enabled. Save this complete test biome as `biomes/tutorial/tree-test.json`, list `tutorial/tree-test` as a region land biome, and temporarily focus the dimension on it:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Procedural Tree Test",
|
||||
"derivative": "minecraft:plains",
|
||||
"vanillaDerivative": "minecraft:plains",
|
||||
"layers": [
|
||||
{ "palette": [{ "block": "minecraft:grass_block" }] }
|
||||
],
|
||||
"generators": [
|
||||
{ "generator": "flat", "min": 16, "max": 16 }
|
||||
],
|
||||
"proceduralObjects": {
|
||||
"trees": [
|
||||
{
|
||||
"name": "tutorial-oak",
|
||||
"chance": 1,
|
||||
"density": 2,
|
||||
"variants": 4,
|
||||
"seed": 9001,
|
||||
"trunk": "minecraft:oak_log",
|
||||
"leaves": "minecraft:oak_leaves",
|
||||
"profile": "OAK",
|
||||
"heightMin": 7,
|
||||
"heightMax": 11,
|
||||
"plausible": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Reuse `generators/flat.json` from the minimal dimension walkthrough, validate the pack, and open Studio on seed `1337`.
|
||||
2. Generate several new chunks. Success is a repeatable set of oak variants anchored to the grass surface, with leaves that use normal decay-distance data because `plausible` is true.
|
||||
3. If no trees appear, confirm the biome is focused, new chunks were generated, dimension `decorate` and `useMantle` are true, and the console did not reject every baked variant. Keep `chance: 1` until placement is proven.
|
||||
4. Tune height, profile, trunk shape, and canopy before lowering chance or increasing density. Remove focus and reduce chance only after the same seed reproduces the shapes across a Studio reopen.
|
||||
|
||||
## Container (`IrisProceduralObjects`)
|
||||
|
||||
| Field | Type | Contents |
|
||||
@@ -298,15 +338,17 @@ Natural landmarks. Extra field `surfaceSupportBuffer` (default 3) for foundation
|
||||
| `chance` | `0.4` | Per candidate |
|
||||
| `scatterRadius` | `2` | BASE_SCATTER extent |
|
||||
|
||||
## Authoring workflow
|
||||
## Extend to other procedural families
|
||||
|
||||
1. Choose system: trees for forests, fungi for mushroom biomes, coral for warm oceans, crystals for cave biomes, formations for deserts/coasts, ruins for sparse land.
|
||||
2. Set `chance`/`density` low first; raise after silhouette looks correct.
|
||||
3. Keep `variants` 4–12; each variant is baked at first use/cache warm.
|
||||
4. Use palettes for material mix; single `block` strings for simple packs.
|
||||
5. Match `carvingSupport` to environment; crystals and cave props use `CARVING_ONLY`.
|
||||
6. For cave props, prefer stilt place modes and profile `defaultObjectPlaceMode`.
|
||||
7. Prefer procedural systems for infinite variety; use `.iob` objects when you need hand-authored geometry (overworld trees mostly use objects today).
|
||||
2. Add one entry using the minimal tree example above or the smallest equivalent for that family. Use one material, low `chance`, low `density`, and a fixed seed.
|
||||
3. Focus the target biome, validate, and generate enough new chunks to observe both placements and empty space.
|
||||
4. Tune dimensions and silhouette before raising density. Keep `variants` at 4–12; variants are baked during first use/cache warmup.
|
||||
5. Add palettes and decorators after shape is stable. Match `carvingSupport` to the environment; crystals and cave props use `CARVING_ONLY`.
|
||||
6. For cave props, prefer stilt place modes and the cave profile's `defaultObjectPlaceMode`.
|
||||
7. Remove biome focus and verify the family remains limited to its intended biome/region scope.
|
||||
|
||||
The tutorial passes when the same seed reproduces the family, placement density leaves the intended negative space, and no invalid variant is skipped in the log. Prefer `.iob` objects instead when exact hand-authored geometry matters more than variation.
|
||||
|
||||
## Practical notes
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Iris places built content through three systems: single `.iob` objects, multi-piece Iris jigsaw assemblies, and native (vanilla/datapack/mod) structures. They share pack folders and some placement JSON, but each system has different fidelity, terrain tools, and commands. This page chooses the system; the linked guides are the field and workflow references.
|
||||
|
||||
Command listings assume the Bukkit/Paper plugin. Fabric/Forge/NeoForge expose a reduced command surface.
|
||||
Command listings assume the Bukkit/Paper plugin. Fabric/Forge/NeoForge run saved Iris jigsaws through the shared assembler but do not expose the Bukkit-only Jigsaw Studio command tree.
|
||||
|
||||
| Guide | Covers |
|
||||
|---|---|
|
||||
@@ -11,13 +11,23 @@ Command listings assume the Bukkit/Paper plugin. Fabric/Forge/NeoForge expose a
|
||||
| `21 - Jigsaw Structures.md` | Iris multi-piece structures: pieces, pools, connectors, grids |
|
||||
| `22 - Native Structures & Datapacks.md` | Vanilla/datapack structures, ingest, adjustments, `nativeStructures` |
|
||||
|
||||
## Tutorial: choose and prove one structure path
|
||||
|
||||
1. Describe the intended result in one sentence: one repeated build, an assembled graph, or a registered Minecraft structure.
|
||||
2. Choose the matching row in **Which system** below. Do not begin by converting assets between systems.
|
||||
3. Complete that guide's smallest worked example in a disposable Studio or test world.
|
||||
4. Validate the pack, place or locate one instance, then generate a natural instance in new chunks.
|
||||
5. Restart and repeat the locate/generation check before adding terrain adaptation, loot, markers, or suppression.
|
||||
|
||||
The proof requires both direct inspection and natural placement. `/iris structure place` proves geometry only; it does not prove spacing, biome eligibility, height gates, or native registry scope.
|
||||
|
||||
## The three systems
|
||||
|
||||
**1. Objects** (`objects/*.iob` + `objects[]` on a biome or region).
|
||||
A single build stamped by chance and density: trees, rocks, ruins, camps. Object placements have the richest terrain-fitting tools (stilts, vacuum, paint, cave anchors, snow, edit, loot, markers). Dimensions do not carry `objects[]`.
|
||||
|
||||
**2. Iris jigsaw structures** (`structures/`, `jigsaw-pools/`, `jigsaw-pieces/` + `structures[]` placement).
|
||||
Multi-piece assemblies in the style of villages: pieces connect through named connectors drawn from weighted pools, on a deterministic placement grid. Every piece is an Iris object, so the assembly is fully editable.
|
||||
Multi-piece assemblies in the style of villages and strongholds. `PLANAR_JIGSAW` classifies north/east/south/west connectors into six rotation-independent archetypes: blank, end, straight, corner, T-junction, and cross. Each planar archetype has its own width, height, depth, enabled state, and variants; the dimensions do not need to match the other workcells or form a square. `SPATIAL_JIGSAW` accepts freeform horizontal and vertical connectors in one shared workcell. Bukkit Jigsaw Studio provides automatic capture, a permanent seed-`1337` generated preview, dynamic graph evaluation, coherent theme sets, chance and piece rules, mandatory terminal caps, a control-chest GUI, and bound stick tools.
|
||||
|
||||
**3. Native structures** (vanilla, datapack, and mod structures controlled from the dimension file).
|
||||
Generated by Minecraft's machinery with full native fidelity (processors, entities, spawners, loot). Iris can disable them, adjust them, ingest datapacks, and place specific registered keys on Iris grids.
|
||||
@@ -29,15 +39,20 @@ Generated by Minecraft's machinery with full native fidelity (processors, entiti
|
||||
| Scatter a build across a biome (trees, rocks, ruins, clutter) | Object + `objects[]` placement |
|
||||
| Pack loot tables on chests | Object placement `loot` / `vanillaLoot` |
|
||||
| Procedural multi-room structure you fully author | Iris jigsaw structure |
|
||||
| Planar village roads, halls, corners, tees, and caps with independent archetype sizes | `PLANAR_JIGSAW` through `/iris jigsaw` |
|
||||
| Freeform rooms, stairs, shafts, towers, or strongholds | `SPATIAL_JIGSAW` through `/iris jigsaw` |
|
||||
| One authored graph that must also ship as a vanilla 26.2 datapack | `VANILLA_PORTABLE` Iris jigsaw, then strict `/iris jigsaw export` |
|
||||
| Move/stilt/encase vanilla structures for Iris terrain | `importedStructures.adjustments` |
|
||||
| Remove vanilla villages or other families | `importedStructures.disabled` |
|
||||
| Datapack structures generating natively | `datapackImports` + ingest |
|
||||
| Datapack structures only where you choose | Disable namespace + `nativeStructures` placement |
|
||||
| Replace a vanilla structure with Iris-positioned native starts | Dimension placement with `nativeSuppression: REPLACE_SOURCE` |
|
||||
| Edit vanilla/datapack blocks, pieces, or pools | `/iris structure import`, then edit Iris copies |
|
||||
| Edit one registered vanilla/datapack jigsaw | `/iris jigsaw convert`, then edit the owned Iris copy |
|
||||
| Bulk-import registered structures or convert non-jigsaw templates | `/iris structure import`, then inspect the Iris copies |
|
||||
| Edit an existing unowned Iris graph | `/iris jigsaw adopt inspect`, then apply the reviewed in-place or clone plan |
|
||||
| Builds from vanilla structure blocks | Ship `.nbt` in a datapack, or import as objects — see `22 - Native Structures & Datapacks.md` |
|
||||
|
||||
Systems compose. Shipping packs commonly use objects for decoration, Iris or imported jigsaws for multi-piece content, and native placements with suppression for selected vanilla keys.
|
||||
Systems compose. Shipping packs commonly use objects for decoration, Iris or imported jigsaws for multi-piece content, and native placements with suppression for selected vanilla keys. Vanilla export is a strict subset: Iris channels, structure edits/loot, fixed piece rotation, custom blocks, and tile/block-entity NBT are rejected rather than silently dropped.
|
||||
|
||||
## How the pieces relate
|
||||
|
||||
@@ -46,10 +61,16 @@ build in world ──wand──> object (.iob) ──objects[] placement──>
|
||||
│
|
||||
└──jigsaw-piece JSON──> pool ──> structure ──structures[] placement──> assembled in world
|
||||
|
||||
/iris jigsaw create ──> transaction-owned objects + pieces + pools + structure
|
||||
│
|
||||
├──six planar workcells / one spatial workcell──> variant load + marker capture + automatic atomic save
|
||||
├──seed-1337 preview + dynamic evaluation──> generated read-only assembly
|
||||
└──VANILLA_PORTABLE export──> Minecraft 26.2 datapack
|
||||
|
||||
registered structure (vanilla / datapack / mod)
|
||||
│ generates natively, controlled by importedStructures (disabled / adjustments)
|
||||
├──nativeStructures placement──> vanilla machinery at Iris-chosen points
|
||||
└──/iris structure import──> objects + pieces + pools + structure (editable Iris copies)
|
||||
└──/iris jigsaw convert or /iris structure import──> editable Iris copies
|
||||
```
|
||||
|
||||
## Shared rules
|
||||
@@ -62,4 +83,6 @@ registered structure (vanilla / datapack / mod)
|
||||
|
||||
**Only new chunks change.** Placement and structure-control edits affect chunks generated after the config existed.
|
||||
|
||||
**Placement scope is explicit.** Iris jigsaws can be attached at dimension, region, surface-biome, or cave-biome scope. Scope is sampled at the start chunk center; cave-biome placements participate only when their resolved anchor is `CAVE_FLOOR`, `CAVE_CEILING`, `CAVE_CENTER`, or `CAVE_ANY`, and an optional placement `caveBiomes` list is rechecked at the actual anchor. See `15 - Caves & Carving.md`.
|
||||
|
||||
**Validate before shipping.** On Bukkit, `/iris pack validate pack=<pack>` runs full pack validation, including jigsaw-graph and structure placement checks. Modded uses `/iris pack validate <pack>`.
|
||||
|
||||
@@ -2,6 +2,27 @@
|
||||
|
||||
An Iris object is a sparse voxel volume (block states plus block-entity data) stored as `.iob` under a pack's `objects/` folder. This guide covers creating, importing, and editing objects. Generation wiring is `20 - Object Placement.md`; jigsaw pieces are `21 - Jigsaw Structures.md`.
|
||||
|
||||
## Tutorial outcome
|
||||
|
||||
Build a small object in Studio, select its exact bounds, save it under a stable key, paste it once for geometry verification, and then wire it into generation in `20 - Object Placement.md`. Use a disposable object key until the bounds and origin are correct; overwriting an object has no automatic backup and does not rewrite copies already placed in chunks.
|
||||
|
||||
The object tutorial is complete when `/iris object analyze <key>` reports the expected dimensions and block count, `/iris object paste <key> edit=true` aligns correctly at the target, and a save/reopen cycle preserves block states and block-entity data.
|
||||
|
||||
Prerequisites: a writable pack, operator access on a Bukkit-family server, and a finished test build. A Studio world is the shortest path:
|
||||
|
||||
```text
|
||||
/iris studio open <pack> seed=1337
|
||||
/iris object wand
|
||||
```
|
||||
|
||||
1. Left-click one selection corner and right-click the opposite corner. Run `/iris object x+y` if the rough selection should tighten around the build while keeping its base.
|
||||
2. Save a stable path with `/iris object save tutorial/lookout overwrite=true`. Outside an Iris world, use `/iris object save dimension=<pack> tutorial/lookout overwrite=true`.
|
||||
3. Confirm that `objects/tutorial/lookout.iob` exists and run `/iris object analyze tutorial/lookout`.
|
||||
4. Run `/iris object paste tutorial/lookout edit=true`; inspect alignment and block-entity data, make any edits, and save the same key with `overwrite=true`.
|
||||
5. Close and reopen Studio, paste the object again, then complete the natural-placement tutorial in `20 - Object Placement.md`.
|
||||
|
||||
If the save cannot resolve a pack, pass `dimension=<pack>`. If the paste is offset, inspect air padding inside the selection because the object origin is the bounding-box center. If a converted schematic has empty chests, signs, or spawners, use the live paste-and-wand route in section 3.2.
|
||||
|
||||
## 1. What an object is
|
||||
|
||||
An object stores bounding box (`w × h × d`), a sparse block map, and a sparse tile-data map. Origin is always the **center** of the bounding box (`w/2, h/2, d/2`, integer division) — derived from dimensions, never stored, recomputed on load.
|
||||
|
||||
@@ -2,6 +2,36 @@
|
||||
|
||||
Object placements wire a saved object (`objects/<key>.iob`) into biome or region JSON so the generator stamps it. Creating objects is `19 - Objects.md`; multi-piece assemblies are `21 - Jigsaw Structures.md`.
|
||||
|
||||
## Tutorial: place one object before tuning a library
|
||||
|
||||
Prerequisites: a saved object such as `objects/tutorial/lookout.iob`, a biome or region used by the target dimension, and a Studio or disposable test world. Merge this complete `objects` fragment into one focused biome for the first test; keep the resource's other fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"objects": [
|
||||
{
|
||||
"place": ["tutorial/lookout"],
|
||||
"chance": 1,
|
||||
"density": 1,
|
||||
"mode": "CENTER_HEIGHT",
|
||||
"rotation": { "enabled": false }
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. Paste the saved object directly with `/iris object paste tutorial/lookout` and verify its geometry and origin first.
|
||||
2. Add the fragment above and run `/iris pack validate pack=<pack>` on Bukkit or `/iris pack validate <pack>` on a modded loader.
|
||||
3. Open or hotload Studio and generate fresh chunks whose center uses the edited biome. Confirm the object appears in the intended surface scope.
|
||||
4. Run `/iris find object tutorial/lookout`, or obtain `/iris object dust` and right-click a placed block. Confirm Iris reports the expected object key.
|
||||
5. Select the terrain mode that solves the observed problem: `PAINT` for ground-hugging clutter, a stilt mode for support, `CEILING_HANG` for roofs, or a vacuum mode for flattened foundations.
|
||||
6. Test negative cases: slopes, water, cave openings, and neighboring biomes where the object should not place.
|
||||
7. Reduce chance/density to production values, validate again, and generate another fresh area.
|
||||
|
||||
The placement passes when direct paste and natural generation agree on orientation, the terrain interaction is stable, and the object remains absent outside its configured scope. Existing chunks are not a valid iteration target.
|
||||
|
||||
If validation cannot resolve the object, compare its key with the path under `objects/`. If no placement appears, check the chunk-center biome first, then water, slope, surface-support, height, collision, and carving gates. If a non-Studio world still uses the old placement, update its pack snapshot and restart as described in `18 - Structures Overview.md`.
|
||||
|
||||
## 1. Where placements go
|
||||
|
||||
`objects[]` exists on exactly two resource types:
|
||||
|
||||
+523
-237
@@ -1,309 +1,595 @@
|
||||
# 21 - Jigsaw Structures
|
||||
|
||||
Iris multi-piece structures: pieces connect through named connectors drawn from weighted pools, assembled deterministically per start chunk. Every piece is an Iris object (`.iob`). Authoring pieces, pools, and structures and placing them is covered here. Vanilla/datapack structures and import are `22 - Native Structures & Datapacks.md`.
|
||||
Iris Jigsaw Studio is the Bukkit in-game authoring path for multi-piece structures. It supports planar connector-topology projects for village-like layouts and freeform spatial projects for strongholds, towers, and rooms, while the shared Iris assembler runs the saved resources on every supported platform. Studio-created projects are transaction-owned, dynamically evaluated, and can be exported to a strict Minecraft 26.2 vanilla datapack when they use the `VANILLA_PORTABLE` contract.
|
||||
|
||||
## 0. Resource model
|
||||
This page replaces the former in-game jigsaw instructions. General Studio behavior is in `10 - Studio & VSCode Schemas.md`, placement context is in `18 - Structures Overview.md`, and native/datapack structures are in `22 - Native Structures & Datapacks.md`.
|
||||
|
||||
| Resource | Pack folder | Role |
|
||||
## Tutorial: create a planar village kit
|
||||
|
||||
Prerequisites: a Bukkit-family Iris server, a writable pack under the Iris `packs/` directory, a player with `iris.all`, and no other Studio world being opened or closed. Bukkit has one global Studio project/world lifecycle and one owning Jigsaw player session. Non-owner block edits and recognized mutating commands are cancelled throughout the active Jigsaw Studio world. Use a disposable pack or a version-controlled copy until the complete smoke test passes.
|
||||
|
||||
1. Create a transaction-owned project and open its transient Studio world:
|
||||
|
||||
```text
|
||||
/iris jigsaw create overworld village/demo
|
||||
```
|
||||
|
||||
`village/demo` is the new structure key: it writes `structures/village/demo.json`, identifies the graph in Iris placements, and is the key used to reopen it later. `structure=` and `name=` are named aliases for `key=`, not references to a separate vanilla structure. With no optional arguments, creation defaults to planar mode, Iris-native compatibility, 16×16×16 initial workcells, and Studio seed `1337`. `mode=` tab-completes `planar` or `spatial`; `compatibility=` tab-completes `iris` or `vanilla`. Planar width and depth must each be at least `3`, X/Z cannot exceed `128`, Y must stay within `1..192`, and one workcell cannot exceed `2,097,152` blocks. Width and depth may differ. Creation is add-only: Iris refuses any occupied or conflicting target.
|
||||
|
||||
A planar project begins with one owned variant for each archetype, three owned pools, one `variant-1` theme set, and one ownership manifest:
|
||||
|
||||
```text
|
||||
structures/village/demo.json
|
||||
jigsaw-pools/village/demo/start.json
|
||||
jigsaw-pools/village/demo/pieces.json
|
||||
jigsaw-pools/village/demo/caps.json
|
||||
jigsaw-pieces/village/demo/{blank,end,straight,corner,tee,cross}.json
|
||||
objects/village/demo/{blank,end,straight,corner,tee,cross}.iob
|
||||
.iris/structure-manifests/key-<sha256>.json
|
||||
```
|
||||
|
||||
The start pool selects Cross Junction at weight `1`; the pieces pool contains End Cap, Hallway, L Junction, T Junction, and Cross Junction; its direct fallback is the caps pool; and the caps pool contains End Cap plus an empty termination entry. Their resource keys remain `end`, `straight`, `corner`, `tee`, and `cross`. Every default piece is rotatable and has weight/chance `1` where it is a pool member. In the default Iris-compatible project, every piece belongs to theme `variant-1`, End Cap is terminal, mandatory caps are off, and unresolved optional branches fail the assembly. A vanilla-compatible project omits both Iris theme and terminal-rule metadata and terminates only the unresolved optional branch. Studio opens with the player in creative above Blank, and all six workcells have their default variant loaded.
|
||||
|
||||
Jigsaw Studio does not run the ordinary Studio entry teleport or open a VSCode workspace before entering the workcell. It reuses the startup-loaded datapack runtime only while the pinned compiler-input fingerprint still matches all dimension, biome, and snippet JSON plus the compiler build and height policy. Jigsaw's structure, pool, piece, object, and ownership writes do not alter those generated registries; a relevant input edit, unavailable registry, or changed/failed external datapack ingest or removal invalidates reuse and falls back to the normal recovery and installation check, while a verified no-change check restores the prior pin. Its dedicated synthetic generator skips the procedural generation-cache warm, complete mantle-radius preparation, and native structure-start generation, Paper requests the one entry chunk urgently and asynchronously, and the owner is teleported once through the Jigsaw destination path after that chunk is retained and ready.
|
||||
|
||||
2. Inspect the compact Studio and its context displays:
|
||||
|
||||
```text
|
||||
/iris jigsaw status
|
||||
/iris jigsaw particles true
|
||||
```
|
||||
|
||||
Planar Studio has exactly six rotation-independent workcells in a three-column by two-row grid: Blank, End Cap, and Hallway on the first row; L Junction, T Junction, and Cross Junction on the second. Their stable IDs remain `blank`, `end`, `straight`, `corner`, `tee`, and `cross`. Neighboring capacities retain two clear blocks even when they differ. Each floor is light-gray wool, the canonical connector path is red wool, and every canonical face-center socket is capped with a sea lantern. There is no orientation, permutation, authored-piece, or derived-rotation gallery.
|
||||
|
||||
Aqua particles outline the workcell containing the player, dark gray outlines nearby valid workcells, and red identifies an invalid workcell. A focused connector is a 1.75-block direction line: lime for complete metadata with no Iris channel, red for incomplete identity metadata, or a deterministic channel color. `/iris jigsaw particles <true|false>` is player-local. The existing Iris scoreboard switches automatically to Jigsaw context and reports the structure, workcell, variant, and Loading, Saving, Disabled, Read-only, Invalid, Unsaved, or Saved state. `/iris studio scoreboard` toggles that sidebar for the current login.
|
||||
|
||||
3. Right-click the generated control chest with the main hand, run `/iris jigsaw menu`, or start three sneaks within 1.5 seconds. The six-row GUI shows the six workcells and pages only the variants belonging to the selected rotational archetype. Entering a workcell selects it for the owner's next menu open. Left-click **Hallway**, then click **New Blank Variant**. Iris clones the active owned piece's complete metadata and every exact pool-entry membership into a service-named owned piece, creates an empty object with the source object's dimensions, closes the GUI while the graph transaction runs, and loads the new variant into Hallway. Reopen the menu after the completion message.
|
||||
|
||||
New keys are deterministic, such as `village/demo/variants/straight/variant-1`. **Rename This Variant** and **Rename This Workcell** use an anvil text input; labels are author-facing only and do not change piece keys, stable workcell IDs, or solver archetypes. **Duplicate This Cell's Variant** clones the same complete piece metadata and every exact membership while copying the source object's bytes and its author-facing label. An End Cap clone therefore keeps both its pieces- and caps-pool entries, and a Cross Junction clone keeps both its start- and pieces-pool entries, including each entry's weight, chance, and other fields. Neither action guesses a first or lexicographically sorted owned pool. Both require an active owned variant with at least one owned membership; for an empty or unassigned workcell, use `/iris jigsaw piece create <poolKey> <pieceKey>` to select the pool explicitly. A non-owned variant cannot be duplicated or mutated.
|
||||
|
||||
4. Enter Hallway and build only inside its aqua particle bounds. The workcell displays the active object's real blocks and overlays every saved connector as a real `minecraft:jigsaw` block. An existing planar piece authored in another direction is rotated automatically into canonical orientation; block states, connectors, positions, and final states rotate with it. Capture applies the inverse rotation so the source resources stay coherent.
|
||||
|
||||
5. Configure each `minecraft:jigsaw` marker through Mojang's block UI. For a newly generated Hallway variant, the north and south markers already use:
|
||||
|
||||
| Mojang field | Generated planar value |
|
||||
|---|---|
|
||||
| Name | `iris:planar` |
|
||||
| Target name | `iris:planar` |
|
||||
| Pool shown in the marker UI | `iris:village/demo/pieces` |
|
||||
| Joint | `ALIGNED` |
|
||||
| Final state | `minecraft:structure_void` |
|
||||
| Selection priority | `0` |
|
||||
| Placement priority | `0` |
|
||||
|
||||
The jigsaw block's `orientation` block state supplies its front and top directions. Studio marker pools must use `iris:<owned-pool-key>` in Mojang's UI; capture verifies that namespace and stores the internal pool key without `iris:`. Do not move a generated planar marker away from its face-center socket. For `VANILLA_PORTABLE`, use vanilla-valid namespaced connector identities and leave the Iris-only channel empty.
|
||||
|
||||
6. Change one block, then wait two seconds without another workcell update. Iris marks the workcell dirty immediately and schedules capture after a 40-tick quiet period. A later change replaces the pending capture identity, and a busy capture retries until the save/load/graph barrier permits it. Container inventory click, drag, and close events, internal inventory move and hopper pickup, and furnace, brewing-stand, dispenser, and crafter activity are captured along with block, fluid, growth, piston, redstone, explosion, interaction, and recognized command changes. Opening Mojang's jigsaw-block UI starts a five-tick owning-region NBT poll; a detected tile change marks the workcell dirty, while a subsequent command, tool use, teleport/world change, quit, graph operation, **Flush Autosave Now**, close, or enabled-world unload first requests a final tile snapshot.
|
||||
|
||||
```text
|
||||
/iris jigsaw status
|
||||
/iris jigsaw save
|
||||
```
|
||||
|
||||
Autosave preserves the authored connector order for every marker that remains at the same source-local position, including markers whose metadata or orientation changed there. Removed markers disappear; new or moved markers append in deterministic X/Y/Z order, and duplicate source or captured positions reject the save instead of changing seeded assembly through world scan order.
|
||||
|
||||
`status` reports whether an autosave is pending. `/iris jigsaw save` and the GUI's **Flush Autosave Now** action request an immediate flush; if the final marker snapshot, another operation, or scheduler availability prevents capture from starting, the same pending ticket is retained and retried. Persistent validation or atomic-writer failures leave that mutation dirty, emit one console report with request, structure, workcell, and piece context, and retry after 2, 4, 8, 16, then at most every 30 seconds. A later edit resets that failure state; a manual flush attempts immediately without discarding it. Pending tickets resolve their workcell by stable ID after every committed graph reload, so one workcell save cannot strand sibling autosaves on replaced layout objects. Neither manual action is required in the normal loop. Fresh untouched workcells report **Autosaved**, not pending. Capture reads the active owned variant across its exact displayed dimensions, converts jigsaw blocks into connector metadata, writes each connector's `final_state` into the object cell, replaces only the piece JSON connector array so omitted defaults and extension fields remain intact, compiles the complete owned graph, then commits the JSON, `.iob`, and manifest together. The Jigsaw service directly invalidates, reloads, evaluates, and rematerializes these graph resources without running ordinary Studio's full-engine pack hotloader. If an object crosses chunks, Iris snapshots every intersection on that chunk's owning region and begins the write only after the complete capture validates. A failed or incomplete capture writes nothing.
|
||||
|
||||
7. Inspect the session-persistent preview. Every committed mutation triggers a background compile and seed-`1337` assembly. The menu reports `PENDING`, `VALID`, `WARNING`, `INVALID`, or `STALE`, plus selected theme, piece count, and the current diagnostic. Iris renders the assembled blocks on the negative-X side of the workcells, keeps them until replacement or Studio close, and updates that read-only area after each later commit. Click **Go to Preview** or run `/iris jigsaw preview goto` to teleport above it. The preview bounds are protected from players, fluids, pistons, explosions, growth, fire, entities, and redstone. The renderer accepts at most 250,000 explicit blocks; a larger assembly becomes `INVALID` with the render-limit diagnostic and is not rendered.
|
||||
|
||||
For an additional arbitrary-seed diagnostic, use:
|
||||
|
||||
```text
|
||||
/iris jigsaw preview assemble seed=4242
|
||||
```
|
||||
|
||||
This separate command places no blocks and draws bounded purple particle boxes for 10 seconds. It does not replace the automatic seed-`1337` evaluation or permanent block preview.
|
||||
|
||||
8. Configure variation from the GUI:
|
||||
|
||||
- Each exact pool membership has a positive relative weight and an independent `0%..100%` eligibility chance. GUI chance adjustments use five-percentage-point steps. Chance is tested before weighted selection.
|
||||
- **Themes & Piece Rules** sets a loaded owned variant's theme membership, allowed depth `0..30`, required/maximum placement count `0..512` (`0` maximum means unbounded), and terminal role.
|
||||
- **Duplicate All Enabled Cells as Family** allocates the next `variant-<n>` family, clones the currently loaded owned variant from every enabled workcell, duplicates their pool memberships, and atomically loads and assigns all clones to that one new theme. The operation either commits and rebinds the complete family or changes nothing. The structure selects one theme per assembly by positive theme weight, so `variant-1` pieces do not mix with `variant-2` pieces unless a piece belongs to both or has an empty theme list.
|
||||
- **Mandatory Caps** requires every unresolved connector pool to use its direct fallback and place a compatible piece marked terminal. The default End piece is terminal and the default pieces pool points directly to the caps pool, so a new Iris-compatible project can enable this rule without first editing End.
|
||||
|
||||
Piece themes, non-default chance, piece rules, and mandatory caps are Iris-only metadata. A graph using them is not `VANILLA_PORTABLE`.
|
||||
|
||||
9. Resize or disable workcells as needed. Open **Workcell Settings** and adjust capacity width, height, or depth by 1 or 8. Every planar workcell persists its own capacity; changing it never rewrites a variant object. A capacity cannot shrink below any variant already assigned to that cell. Open **Variant Size** to give the selected owned variant its own exact width, height, and depth within that capacity. Growth adds air; a safe shrink preserves in-bounds blocks and moves canonical connector payloads and sockets to the new face centers, while cropping or collision rejects the transaction without writes. **Resize This Variant to Capacity** is the one-click exact-size shortcut. A loaded resized variant reloads in place after commit; sibling variants keep their independent dimensions and bytes.
|
||||
|
||||
Disabling a planar workcell removes all pieces of that archetype from assembly and vanilla export but preserves its size and variants for later editing. A red stained-glass block display fills the disabled bounds; Iris removes its tracked display when the origin chunk unloads and recreates it after that chunk loads again. Re-enable the workcell from the same settings page to restore participation.
|
||||
|
||||
10. Use the **Toolbox** page when repeated actions should be available without reopening the chest. Clicking an entry gives a named stick bound to the current Studio request and its workcell, variant, pool entry, or action. Right-click to use it. Resize, themes, and rules sticks open the relevant GUI context; other sticks run their exact bound action. A stick from a closed or replaced Studio is rejected. Destructive sticks require two right-clicks within 10 seconds.
|
||||
|
||||
11. Set the graph's expansion limits through the ownership-aware command:
|
||||
|
||||
```text
|
||||
/iris jigsaw rules limits 12 8
|
||||
```
|
||||
|
||||
Extended graphs accept depth `1..30` and radius `1..32` chunks. A `VANILLA_PORTABLE` Studio session restricts this command to depth at most `20` and radius at most `8` chunks.
|
||||
|
||||
12. Attach the structure to a dimension, region, or biome with a `structures[]` placement, validate the pack, and generate new chunks. A complete placement example is under **Natural placement**.
|
||||
|
||||
13. Close the transient world when authoring is complete:
|
||||
|
||||
```text
|
||||
/iris jigsaw close
|
||||
```
|
||||
|
||||
Wait for autosave, variant load, evaluation, or graph-update messages before replacing or closing Studio. Pending autosave blocks conflicting variant and graph operations. `close` refuses tracked work unless it is clean; `discard=true` deliberately abandons pending edits. Non-owners can use only Iris's strict informational and communication command allowlist. An integration that bypasses Bukkit mutation events must call `JigsawStudioService.markDirty(...)` or `markAllDirty(...)`.
|
||||
|
||||
The tutorial passes when autosave commits the edited object and marker data, the automatic evaluation reaches `VALID` or an understood `WARNING`, the permanent seed-`1337` preview renders the expected family, pack validation succeeds, and a natural instance appears in newly generated chunks.
|
||||
|
||||
### Re-edit an existing Studio jigsaw
|
||||
|
||||
Do not run `create` again; creation is add-only. Reopen a Studio-owned graph by its original dimension and structure key:
|
||||
|
||||
```text
|
||||
/iris jigsaw open overworld village/demo
|
||||
```
|
||||
|
||||
`/iris jigsaw edit overworld village/demo` and `/iris jigsaw reopen overworld village/demo` are aliases. `open`, `edit`, and `reopen` reconstruct workcell capacities and labels, enabled states, variant dimensions and labels, themes, rules, and pool memberships from the saved graph. Changes inside loaded owned variants autosave; **Flush Autosave Now** only requests an immediate recovery flush and leaves blocked work queued for retry. The automatic seed-`1337` evaluation and permanent preview rebuild after each committed change. A loaded variant without editable ownership is visibly Read-only and cannot be changed.
|
||||
|
||||
### Adopt an existing Iris graph
|
||||
|
||||
An existing Iris graph without an ownership manifest must be inspected and claimed before editing:
|
||||
|
||||
```text
|
||||
/iris jigsaw adopt inspect overworld legacy/village target=auto strategy=auto
|
||||
/iris jigsaw adopt apply <plan-uuid>
|
||||
```
|
||||
|
||||
`inspect` reads the complete structure, pool, piece, object, and referenced loot closure asynchronously. It reports a plan UUID, target, resource and byte counts, structured warnings/errors, and one of `IN_PLACE`, `CLONE_REQUIRED`, or `BLOCKED`. The default `auto` strategy claims an exclusive unowned closure in place; if any resource is shared with another structure, it plans a private clone instead. `target=auto` names that clone `<source>-studio`, then tries numbered suffixes without overwriting an existing target. Use `strategy=in-place` to require a claim with no resource-byte rewrites, or `strategy=clone target=<new-key>` to require a specific private copy.
|
||||
|
||||
Plans belong to the inspecting player, remain in memory for 15 minutes, and are consumed once. Close any active or opening Jigsaw Studio before `apply`. Apply takes the pack mutation lock, re-hashes the pinned source and target read set, rejects an expired or stale plan without writes, and atomically commits the ownership manifest plus adoption receipt. A successful result opens the owned target at Studio seed `1337`. Adoption metadata records source and target hashes and mappings for provenance; it does not provide a rollback command or promise a restorable preimage.
|
||||
|
||||
Automatic datapack imports have `MANAGED_DATAPACK` ownership because removing or refreshing the source may clean or replace them. Iris detects that provenance during inspect, forbids in-place adoption, and plans a private clone while leaving the managed graph unchanged:
|
||||
|
||||
```text
|
||||
/iris jigsaw adopt inspect overworld imported/key target=my-edits/key strategy=clone
|
||||
/iris jigsaw adopt apply <plan-uuid>
|
||||
```
|
||||
|
||||
### Convert a registered vanilla or datapack jigsaw
|
||||
|
||||
Raw registered structures are not Iris graph files and cannot enter `adopt` directly. Convert one registered jigsaw structure into a new add-only owned Iris graph, then open it automatically:
|
||||
|
||||
```text
|
||||
/iris jigsaw convert overworld minecraft:village_plains target=village/plains seed=1337
|
||||
```
|
||||
|
||||
The source must be a live namespaced registry key and a jigsaw structure. With `target=auto`, `minecraft:village_plains` becomes `minecraft_village_plains`. Conversion follows the registered start pool, reachable template pools, templates, connectors, weights, empty entries, and fallbacks; it stores source provenance and fidelity warnings in the ownership manifest. A native list pool entry remains one weighted choice: Iris retains its recursively first physical template and outer connectors, while additional colocated children and their processors are omitted and recorded as `LIST_ELEMENTS` fidelity loss. A captured template containing no non-air states is marked `collidable: false`, allowing its connector-scaffold bounds to overlap an attached physical piece; nonempty converted pieces remain collidable. It does not preserve native placement settings beyond start pool, depth, and maximum distance, and feature pool elements, palette alternatives, processors, entities, or other native-only behavior can be omitted or reported. Keep the source native when those capabilities matter. Conversion is add-only and refuses occupied or conflicting targets rather than overwriting them.
|
||||
|
||||
## Tutorial: create a spatial stronghold kit
|
||||
|
||||
Spatial projects use the same lifecycle without planar cell constraints:
|
||||
|
||||
```text
|
||||
/iris jigsaw create overworld stronghold/demo mode=spatial width=32 height=24 depth=32
|
||||
```
|
||||
|
||||
Spatial Studio has one `workcell/spatial` cell and no topology glyph. Right-click the control chest or triple-sneak to load the start variant, create another service-named variant, or duplicate the active owned variant. Build the room inside that one workcell, place Mojang jigsaw blocks at doorways, stairs, shafts, floors, or ceilings, and configure their name, target, pool, joint, final state, and priorities. Spatial connectors may use all 12 front/top orientations supported by the jigsaw block. Studio sizes the shared capacity to contain every reachable object and the horizontal footprint of its cardinal rotations, but automatic capture and per-variant resize preserve each variant's independent exact dimensions. Use **Resize to Capacity** or `/iris jigsaw piece expand` when only the selected object should become the full workcell size. Spatial workcell and variant labels are author metadata only; `cellSize` and labels do not constrain runtime assembly.
|
||||
|
||||
Use `ROLLABLE` when candidate top direction should not constrain the join and `ALIGNED` when it must match the source top after rotation. Iris still tries only cardinal Y rotations. A piece with `rotatable: false` is tried only at its authored rotation. The control-chest details view toggles this property; Studio does not render separate rotation cells. Vanilla-portable variants must remain rotatable, so their GUI toggle is disabled once rotation is enabled.
|
||||
|
||||
Create additional owned pools before targeting them from new spatial markers or planar variants:
|
||||
|
||||
```text
|
||||
/iris jigsaw pool create stronghold/demo/rooms
|
||||
/iris jigsaw pool create stronghold/demo/end fallbackPoolKey=none
|
||||
/iris jigsaw rules fallback stronghold/demo/rooms stronghold/demo/end
|
||||
```
|
||||
|
||||
`pool create` creates an empty pool and can point it at an already owned direct fallback. `rules fallback <pool> none` clears a fallback. Every change compiles the full owned graph before commit, so a missing pool or fallback cycle is rejected. **New Blank Variant** and **Duplicate This Cell's Variant** copy every exact owned pool entry assigned to the loaded source variant; the first creates empty same-sized geometry and the second copies the source object bytes. They do not select a first or lexicographically sorted fallback pool. If the workcell has no active owned assigned variant, use `/iris jigsaw piece create <poolKey> <pieceKey>` to choose the pool explicitly.
|
||||
|
||||
## Studio workcells and canonical planar display
|
||||
|
||||
The surrounding platform uses a four-block checker pattern, and smooth-quartz cages plus particles identify each workcell's editable volume. The first workcell origin is `(16, 65, 16)`; every workcell's bounds begin at Y 65, one block above its floor, and that origin is the displayed object's lowest unsigned corner. Planar projects use six cells in this exact three-by-two order. Each column uses the widest workcell in that column, each row uses the deepest workcell in that row, and adjacent bounds retain two clear blocks:
|
||||
|
||||
| Row | Workcell | Stable ID | Canonical open sides |
|
||||
|---|---|---|---|
|
||||
| 1 | Blank | `workcell/blank` | none |
|
||||
| 1 | End Cap | `workcell/end` | north |
|
||||
| 1 | Hallway | `workcell/straight` | north and south |
|
||||
| 2 | L Junction | `workcell/corner` | north and east |
|
||||
| 2 | T Junction | `workcell/tee` | north, east, and west |
|
||||
| 2 | Cross Junction | `workcell/cross` | north, east, south, and west |
|
||||
|
||||
Every planar footprint at Y 64 is light-gray wool. A one-block-wide red-wool glyph runs from its center toward each canonical side, and the endpoint on that workcell face is a sea lantern. The Blank workcell has no red path or connector cap. A disabled workcell retains this floor but gains one non-persistent red stained-glass block display scaled across its full editable volume; it remains selectable and editable but contributes no pieces to assembly or export. The renderer detaches the display when its origin chunk unloads and recreates it when that chunk loads again. Spatial Studio uses the single ID `workcell/spatial` and has no topology glyph or enable toggle.
|
||||
|
||||
The GUI groups every planar piece by rotational topology kind. For example, west, east, south, and north end pieces are variants of the one End Cap workcell; east-west and north-south pieces are variants of Hallway. When a variant is loaded, its source orientation is rotated clockwise into the archetype's canonical display, including directional block states, connector orientation and position, and connector final state. Capture applies the inverse rotation before writing the original piece and object resources. Pool memberships, weights, dimensions, labels, and the separate underlying piece resources are not merged by this display compaction.
|
||||
|
||||
`/iris jigsaw goto <workcell>` accepts one of these stable IDs or the workcell name case-insensitively. `/iris jigsaw select` selects the cell containing the player, and simply entering a cell updates the owning player's next menu selection. Fresh untouched cells report **Autosaved**. Autosave captures the active owned variant after a quiet period; `/iris jigsaw save [bay=selected]` or **Flush Autosave Now** requests an immediate flush and retains the pending ticket for retry when capture cannot start. An empty workcell, a read-only variant, an invalid render, incomplete marker hydration, a conflicting operation, or a stale Studio request is not capturable.
|
||||
|
||||
### Canonical planar sockets
|
||||
|
||||
For a planar piece whose source object dimensions are `X × Y × Z`, every connector must be horizontal, every connector top must be `UP_POSITIVE_Y`, and the canonically rotated object must fit its archetype workcell. Width and depth need not be equal. Integer division is floor division.
|
||||
|
||||
| Side | Position | Direction | Top |
|
||||
|---|---|---|---|
|
||||
| North | `(X / 2, Y / 2, 0)` | `NORTH_NEGATIVE_Z` | `UP_POSITIVE_Y` |
|
||||
| East | `(X - 1, Y / 2, Z / 2)` | `EAST_POSITIVE_X` | `UP_POSITIVE_Y` |
|
||||
| South | `(X / 2, Y / 2, Z - 1)` | `SOUTH_POSITIVE_Z` | `UP_POSITIVE_Y` |
|
||||
| West | `(0, Y / 2, Z / 2)` | `WEST_NEGATIVE_X` | `UP_POSITIVE_Y` |
|
||||
|
||||
New blank planar variants inherit the active source variant's exact dimensions and use those dimensions for these positions. Workcell capacity changes never rewrite sockets or object bytes. The **Variant Size** screen or `variant resize` changes only the selected owned object and moves its canonical sockets to the new face centers. **Resize to Capacity** is the one-click convenience for making that object exactly match its capacity. Planar mode is a horizontal topology and validation contract, not a global wave-function-collapse solver, and it does not backtrack across an entire map.
|
||||
|
||||
## Marker capture and connector rules
|
||||
|
||||
Jigsaw markers are real `minecraft:jigsaw` blocks while editing. Saving reads their tile data and orientation, then stores connectors in `jigsaw-pieces/<key>.json`; the marker itself is not retained as a jigsaw block in the `.iob`.
|
||||
|
||||
| Connector field | Studio source | Runtime rule |
|
||||
|---|---|---|
|
||||
| Structure | `structures/` | Entry point: start pool and assembly caps |
|
||||
| Jigsaw pool (`IrisJigsawPool`) | `jigsaw-pools/` | Weighted pieces + one fallback pool |
|
||||
| Jigsaw piece (`IrisJigsawPiece`) | `jigsaw-pieces/` | One object + connectors |
|
||||
| Connector | inline in a piece | Position, facing, target pool, name |
|
||||
| `position` | Marker offset from the workcell origin, inverse-rotated to source orientation during planar capture | Must be inside the object's unsigned `0..size-1` bounds |
|
||||
| `direction` | Jigsaw block front | Candidate must face the reverse direction after rotation |
|
||||
| `top` | Jigsaw block top | Must also match after rotation when the source joint is `ALIGNED` |
|
||||
| `pool` | Mojang Pool | UI value must be `iris:<owned-pool-key>`; Studio strips `iris:` and stores the internal pool used to choose the next piece |
|
||||
| `name` | Mojang Name | Identity exposed to a source connector |
|
||||
| `targetName` | Mojang Target name | Must equal the candidate connector's stored `name` exactly; matching is case- and whitespace-sensitive at runtime, while Studio marker capture trims both values |
|
||||
| `channel` | `/iris jigsaw connector channel <channel\|none>` on a saved marker's exact local position | Values match exactly, including case and whitespace; empty matches only empty, and any non-empty value blocks vanilla export |
|
||||
| `joint` | Mojang Joint | `ROLLABLE` ignores candidate top; `ALIGNED` requires it to match |
|
||||
| `finalState` | Mojang Final state | Canonical block state written into the `.iob` at the marker cell; `minecraft:structure_void` leaves the cell absent, while explicit air remains an authored block state |
|
||||
| `selectionPriority` | Mojang Selection priority | Signed integer; higher-priority connectors within one piece are processed first; ties preserve authored order |
|
||||
| `placementPriority` | Mojang Placement priority | Signed integer on the source connector; higher-priority attached child pieces expand first; ties preserve attachment order |
|
||||
|
||||
Keys are file paths under the folder minus `.json`. A structure attaches to the world via an `IrisStructurePlacement` in `structures[]` on a biome, region, or dimension — placement is separate from the structure resource.
|
||||
The ordinary Mojang jigsaw UI does not expose Iris `channel`. Let autosave capture the marker, look directly at it from within eight blocks in the loaded workcell, then run `/iris jigsaw connector channel <channel|none>`. The command maps the canonical display coordinate back to the source piece coordinate and transactionally updates that exact saved connector. It rejects a workcell without an active owned variant, a missing connector offset, whitespace inside a channel, and channels longer than 128 characters. The command trims outer whitespace, `none` clears the channel, and all remaining characters retain their exact case. Runtime matching never trims either saved side, so whitespace in schema-authored data remains significant even though this command cannot author it. Reopen Studio to refresh the workcell and particle diagnostics. A non-empty update is rejected without a write in `VANILLA_PORTABLE`; vanilla marker fields remain owned by Mojang's UI and ordinary capture.
|
||||
|
||||
Older packs may still have a `jigsaw-structures/` folder. Nothing reads it; live folders are the three above.
|
||||
`final_state` must be a valid canonical Minecraft block state. Use `minecraft:structure_void` for an absent cell in a portable template; use the exact solid block state when the connector should leave a block behind. A jigsaw final state of air is accepted and retained explicitly by Studio capture, so it is different from an absent cell.
|
||||
|
||||
## 1. Resources
|
||||
### How assembly chooses pieces
|
||||
|
||||
### 1.1 `structures/<key>.json`
|
||||
1. Iris selects one declared structure theme by positive relative weight. With no declared themes, the assembly is unthemed. A piece with no theme list is eligible for every selected theme.
|
||||
2. It filters the start pool by enabled planar workcell, selected theme, depth and placement rules, then rolls each exact pool membership's independent chance. No passing membership is an intentional empty result; an explicit `empty: true` winner also produces no structure.
|
||||
3. Iris chooses one positively weighted passing start entry and applies a random cardinal rotation when the piece is rotatable. A terminal start is placed but does not expand.
|
||||
4. It processes connectors on the current piece in descending `selectionPriority` order. For each connector, it filters the primary pool by enabled workcell, theme, depth, maximum placements, terminal requirement, and chance, then tries passing entries in weighted random order. An eligible piece still may fail because its connectors are incompatible, it collides, or it exceeds bounds.
|
||||
5. When any eligible entry still needs its declared minimum placement count, those required entries take precedence over other entries. After expansion, an unmet graph-wide minimum produces `FAILED_RULES` rather than silently accepting the assembly.
|
||||
6. A candidate connector is compatible when source `targetName` exactly equals candidate `name`, source `channel` exactly equals candidate `channel` with case and whitespace preserved, faces oppose after rotation, and an `ALIGNED` source also has matching top direction.
|
||||
7. Two pieces whose `collidable` values are both `true` may not have overlapping bounding boxes. A piece with `collidable: false` does not block or become blocked by another piece, while every piece still must stay inside `maxSizeChunks × 16` blocks from the assembly origin. Attached children are queued by the source connector's signed `placementPriority`; Iris finishes one piece's connectors before expanding its children.
|
||||
8. Before maximum depth, Iris tries the primary pool and then that pool's one direct fallback; at maximum depth it skips the primary and tries only the direct fallback. An allowed explicit empty entry or empty primary pool ends the branch immediately and does not continue into the fallback. If structure `requireCaps` or pool `mandatoryFallback` is true, the direct fallback must place a compatible terminal piece and an empty entry cannot satisfy it. Otherwise ordinary primary-plus-fallback exhaustion returns `FAILED_UNCAPPED` under `FAIL_ASSEMBLY` or ends only that connector branch under `TERMINATE_BRANCH`; a fallback's own fallback is never traversed in the same selection. The runtime hard cap is 512 pieces.
|
||||
|
||||
| Field | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `startPool` | required | Pool the assembler draws the start piece from. |
|
||||
| `maxDepth` | `7` (1..30) | Maximum recursion depth. |
|
||||
| `maxSizeChunks` | `8` (1..32) | Hard radius in chunks around start: candidate whose box leaves `maxSizeChunks * 16` blocks from start origin is rejected. Y is not bounded by it. |
|
||||
| `placeMode` | `STRUCTURE_PIECE` | Object place mode when stamping pieces. Other modes change structure-piece anchoring (3.6). |
|
||||
| `edit` | `[]` | Find-and-replace on every piece (same syntax as object placements). |
|
||||
| `loot` | `[]` | Loot-table keys applied to piece containers (each weight 1, non-overriding). |
|
||||
| `vanillaSource` | `""` | Provenance key from import. Locate alias for `/iris goto structure` and `verify`. Empty for hand-authored structures. |
|
||||
The compiler reports missing resources, invalid workcells/bounds/connectors/themes/chances/rules, fallback cycles, unreachable resources, uncappable required connectors, incompatible candidates, and sampled hard-cap failures. Studio reevaluates automatically after open and every committed mutation; `status`, the scoreboard, and the control GUI expose the current evaluation instead of requiring a separate validation action.
|
||||
|
||||
No terrain adaptation on the structure — terrain lives on the placement.
|
||||
## Commands and transactional ownership
|
||||
|
||||
Example (imported graph shape):
|
||||
`/iris jigsaw` aliases are `/iris jig` and `/iris jgs`. This tree is player-only and Bukkit-only; all commands use the root `iris.all` permission.
|
||||
|
||||
The create/open `<key>` is the root structure's internal lowercase resource path, not a display name or namespaced ID. For example, `village/demo` maps to `structures/village/demo.json`, is referenced as `"village/demo"` by Iris placements, and is reused by `open`, `edit`, or `reopen`. Pool and piece keys follow the same path grammar. Use one or more slash-separated segments containing only `a-z`, `0-9`, `.`, `_`, or `-`, such as `village/demo/hall`; the marker UI alone adds the required `iris:` namespace to pool keys.
|
||||
|
||||
| Command | Behavior |
|
||||
|---|---|
|
||||
| `create <dimension> <key> [mode=planar] [compatibility=iris] [width=16] [height=16] [depth=16] [seed=1337]` | Add-only atomic creation of a complete owned graph followed by an open request; mode completes `planar`/`spatial`, compatibility completes `iris`/`vanilla`, planar X/Z are `3..128`, spatial X/Z are `1..128`, Y is `1..192`, and one workcell volume is at most `2,097,152` |
|
||||
| `convert <dimension> <registered-key> [target=auto] [seed=1337]` | Add-only conversion of one live registered vanilla/datapack jigsaw into an owned Iris graph, followed by Studio open; aliases `import`, `import-vanilla` |
|
||||
| `adopt inspect <dimension> <source> [target=auto] [strategy=auto]` | Asynchronously inspect a complete existing Iris closure and issue a 15-minute, hash-pinned `IN_PLACE`, `CLONE_REQUIRED`, or `BLOCKED` plan; strategy completes `auto`, `in-place`, or `clone` |
|
||||
| `adopt apply <planId>` | Revalidate and atomically apply a plan owned by that player, then open the target with seed `1337`; no Studio may be active or opening |
|
||||
| `open <dimension> <key> [seed=1337]` | Map an existing graph into compact workcells; aliases `edit` and `reopen`; another owner, dirty work, or a conflicting lifecycle operation blocks replacement |
|
||||
| `close [discard=false]` | Close the transient Studio; refuses active autosave/load/graph work or a pending dirty capture unless `discard=true` deliberately abandons it |
|
||||
| `status` | Show structure, mode, compatibility, selected workcell dimensions/enabled state, variant count, whether autosave is pending, and the seed-`1337` evaluation/theme/piece result |
|
||||
| `menu` | Open the same workcell/variant/rules/toolbox GUI as the generated control chest or triple-sneak gesture |
|
||||
| `select` | Select the workcell containing the player |
|
||||
| `goto <workcell>` | Select and teleport above a stable workcell ID; alias `teleport` |
|
||||
| `particles <visible>` | Toggle player-local bounds and connector particles |
|
||||
| `save [bay=selected]` | Flush automatic capture now for one dirty ready workcell; ordinary block and container changes already schedule this operation |
|
||||
| `connector channel <channel\|none>` | Look at a saved marker in the active owned workcell within 8 blocks and set/clear its Iris-only channel at the inverse-mapped source position; reopen to refresh the workcell and particles |
|
||||
| `bounds <width> <height> <depth>` | Set the selected workcell capacity without rewriting any variant object; all variants must fit, live geometry stays unchanged, and editing pauses until close/reopen; aliases `cell`, `resize` |
|
||||
| `workcell capacity <width> <height> <depth>` | Explicit nested form of `bounds`; planar capacity belongs to one canonical archetype and spatial capacity is the single project envelope |
|
||||
| `workcell label <displayName>` | Set the selected planar or spatial workcell's author-facing label; quote spaces; canonical solver identity remains unchanged |
|
||||
| `workcell label-reset` | Reset the selected workcell to its canonical solver label; alias `reset-label` |
|
||||
| `pool create <poolKey> [fallbackPoolKey=none]` | Create a new empty owned pool; a non-`none` fallback must already be owned by this project |
|
||||
| `piece create <poolKey> <pieceKey> [weight=1]` | Create and load a new owned variant; planar derives canonical connectors from the contextual workcell, while spatial creates a connectorless blank |
|
||||
| `piece add <poolKey> <pieceKey> [weight=1]` | Re-add and load an existing piece/object already owned by this project |
|
||||
| `piece remove <poolKey>` | Remove the active variant from that pool without deleting its owned piece/object resources |
|
||||
| `piece rotatable <true\|false>` | Persist whether the active variant may use cardinal rotations; portable sessions reject `false` |
|
||||
| `piece expand` | Resize only the selected planar or spatial owned variant exactly to workcell capacity; planar sockets move to the resized faces |
|
||||
| `variant weight <poolKey> <weight>` | Set every matching entry for the active variant in that owned pool; weight must be positive |
|
||||
| `variant resize <width> <height> <depth>` | Resize only the active owned variant within workcell capacity; safe shrink rejects cropped content and the active cell reloads in place |
|
||||
| `variant label <displayName>` | Set the active variant's author-facing label; quote spaces |
|
||||
| `variant label-reset` | Reset the active variant to its resource-key fallback; alias `reset-label` |
|
||||
| `variant duplicate` | Copy the active variant's object bytes, metadata, and exact pool memberships into one new variant in this workcell |
|
||||
| `variant duplicate-family [themeKey=next]` | Atomically clone every enabled workcell's active owned variant into one coherent Iris family and load the complete family; alias `family` |
|
||||
| `rules limits <maxDepth> <maxSizeChunks>` | Atomically set expansion depth and horizontal radius; portable sessions enforce `<=20` and `<=8` |
|
||||
| `rules fallback <poolKey> <fallbackPoolKey\|none>` | Atomically set or clear one owned pool's direct fallback after compiling the complete graph |
|
||||
| `preview goto` | Teleport above the permanent seed-`1337` block preview; alias `teleport` |
|
||||
| `preview assemble [seed=1337]` | Compute a deterministic, read-only assembly at the player's coordinates; report its complete piece count and show in-range bounds as purple particles for 10 seconds within the shared particle budget, without placing blocks |
|
||||
| `export [namespace=iris] [output=jigsaw-export] [format=zip] [replace=false]` | Start a background strict export of the clean on-disk graph as a Minecraft 26.2 directory or zip; completion is reported with the originating structure key |
|
||||
| `delete [confirm=false]` | With `confirm=true`, inspect reverse references, close Studio, and atomically remove the complete hash-pinned owned project; external references or changed ownership bytes block deletion; alias `remove` |
|
||||
|
||||
The control chest is the primary workflow. Right-click it, run `menu`, or triple-sneak within 1.5 seconds. Its six-row GUI rechecks the exact Studio request before every callback. It manages independent workcell capacities and labels, per-variant dimensions and labels, enabled states, rotation, exact pool-entry weights/chances, coherent themes, piece rules, mandatory caps, automatic evaluation, preview navigation, toolbox sticks, and destructive deletion. **Duplicate This Cell's Variant** creates one independent variant; **Duplicate All Enabled Cells as Family** clones and atomically loads one matching variant across every enabled cell. Accepted asynchronous actions close the GUI while work runs. Variant geometry/details are editable only for owned variants, and building/capture applies only to the loaded variant.
|
||||
|
||||
The Toolbox issues schema-`2` named sticks bound to the exact request/workcell/variant/membership. Rename a variant/workcell stick in an anvil, right-click to apply its trimmed label, or sneak-right-click to reset. Labels allow at most 64 Unicode code points and reject control characters plus section-sign formatting. Schema-`1` sticks and bindings from a closed/replaced request are stale. The active variant icon is a jigsaw block, a valid evaluation is an emerald, and lime dye is used only for the explicitly labeled theme-membership toggle.
|
||||
|
||||
The ownership manifest stores the exact resource set, content hashes, source provenance, capabilities, and fidelity losses. Each mutation loads the complete owned graph, verifies current files against ownership, applies the change, compiles the result, stages backups, and commits the graph and updated manifest together. Persisted graph mutations wait for autosave to clear dirty work so a pool, rule, size, theme, or variant transaction cannot erase blocks. Duplicate-one and duplicate-family requests clicked during dirty or active autosave queue once, expedite capture, and resume automatically only if the pinned request/session/source variants still match. Project deletion resolves a symbolic pack root to its real directory before both reverse-reference scans and hash-pinned removal; the final ownership and reference scan holds the same in-process and cross-process authoring locks through removal, so a coordinated write cannot add a dangling reference between validation and commit. A symbolic JSON resource or symbolic directory inside that pack fails the safety scan instead of hiding references. A hash mismatch or outside edit produces an ownership conflict and leaves authored files unchanged. Do not hand-edit transaction-owned resources between Studio transactions.
|
||||
|
||||
The session tracks dirty state per active owned workcell variant. Bukkit coverage includes block place/break/multi-place, buckets, inventory click/drag/close and internal move/pickup, furnace cook/burn/smelt, brewing start/fuel/complete, dispenser and crafter activity, block/entity explosions, entity block changes, right-click and physical interactions, redstone, liquid movement, form/grow/spread/fade/burn, pistons, structure growth, and recognized mutating vanilla or WorldEdit-like commands. A persistent owning-region watch compares jigsaw tile NBT after Mojang's UI opens; transition commands and enabled lifecycle operations request one final snapshot and wait behind that watch before graph mutation or clean close. Interaction coverage intentionally prefers a harmless dirty false positive over losing a door, marker, container, machine, or switch edit. Each workcell has a mutation generation: an autosave clears only the captured generation, and a later edit stays dirty for the next capture. Paper drains pending work synchronously during plugin disable. A forced Folia plugin disable is too late to schedule a new cross-region capture, so operators must close Studio or wait for a clean `status` before reload or shutdown. External plugins that bypass these events must call `JigsawStudioService.markDirty(world, x, y, z)` or `markAllDirty(world)`.
|
||||
|
||||
Jigsaw Studio is globally single-project on Bukkit and belongs to one owning player session. Only that owner can open controls, switch variants, mutate the graph, or flush autosave; entering a workcell changes that owner's selected menu cell. Non-owner direct block edits and recognized mutating commands are cancelled across the whole active Studio world. The control chest and permanent preview are protected against players, explosions, pistons, entities, fluids, growth, fire, and redstone. Generic Studio lifecycle calls cannot bypass the Jigsaw owner transition. Autosave, load, graph-mutation, open, close, and deletion barriers reject conflicts; capture is project-global, so concurrent workcells cannot produce stale full-graph lost updates.
|
||||
|
||||
There is no world-edit undo command for Jigsaw Studio. A successful graph transaction is persistent; recover it from version control or a pack backup if the authored result was wrong.
|
||||
|
||||
### Capacity and per-variant object size
|
||||
|
||||
`bounds` and `workcell capacity` target the selected workcell. Planar workcells persist independent width, height, depth, enabled state, and display label; spatial mode persists one shared `cellSize` plus `spatialWorkcellDisplayName`. Planar capacity width/depth are `3..128`, spatial width/depth are `1..128`, height is `1..192`, and volume is at most `2,097,152`. Capacity is an upper bound for every variant in that workcell. A successful capacity change updates only structure JSON, verifies the complete graph, leaves every object byte unchanged, retains the old live cage, and blocks editing until close/reopen regenerates Studio.
|
||||
|
||||
`variant resize` and the **Variant Size** screen target one owned variant. The exact requested width, height, and depth must fit its workcell capacity. Growth and shrink preserve blocks and tiles at their in-bounds canonical coordinates, account for rectangular source rotations, and relocate each planar canonical connector plus its stored block payload to the new face center. Shrink is lossless only: any stored block, including explicit air, or tile outside the target; a connector destination collision; connector tile data that cannot move safely; a read-only object; or an object shared by another piece rejects the transaction before any authored file changes. New growth volume is air. A loaded variant reloads in place after commit; siblings keep their dimensions and bytes. Marker block-entity data is applied on its owning region before Iris verifies either the candidate or its rollback, so live resize cannot reject a valid marker merely because its NBT merge was deferred to the next tick.
|
||||
|
||||
Spatial capacity changes only the shared workcell envelope and rejects dimensions that do not contain every variant object. **Resize to Capacity** or `/iris jigsaw piece expand` changes one selected spatial or planar object exactly to that envelope/capacity. The exact-size editor also permits safe lossless shrink. Non-connector air and `minecraft:structure_void` cells are omitted from block entries; explicit authored air and connector final-state air remain distinct and are preserved.
|
||||
|
||||
Capture may cross chunks. Iris schedules each chunk intersection on its owning region, rejects unloaded or incomplete snapshots, aggregates them deterministically, then validates and performs one atomic owned-graph write. A scheduling failure, Studio replacement/unload, marker or tile read failure, duplicate/missing snapshot, or graph validation error aborts the whole capture before authored files are changed. This path has automated chunk-intersection/coordinator coverage; live multi-region Folia gameplay validation remains required.
|
||||
|
||||
For a rotated planar variant, capture moves each block-entity payload back to the inverse-rotated object coordinate while inverse-rotating the block state. The payload itself remains unchanged, matching Iris object placement: modern Bukkit capture omits source position metadata and applies the payload at the explicit destination block. Directional behavior stored in block data rotates normally; semantic values inside a tile payload remain author data.
|
||||
|
||||
## Resource reference
|
||||
|
||||
### Structure: `structures/<key>.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"startPool": "minecraft_village_plains/pool/minecraft_village_plains_town_centers",
|
||||
"maxDepth": 6,
|
||||
"startPool": "village/demo/start",
|
||||
"maxDepth": 7,
|
||||
"maxSizeChunks": 8,
|
||||
"mode": "PLANAR_JIGSAW",
|
||||
"compatibility": "IRIS_EXTENDED",
|
||||
"branchFailurePolicy": "FAIL_ASSEMBLY",
|
||||
"cellSize": {"x": 16, "y": 16, "z": 16},
|
||||
"spatialWorkcellDisplayName": "",
|
||||
"planarWorkcells": [
|
||||
{"displayName": "", "archetype": "BLANK", "width": 3, "height": 3, "depth": 3, "enabled": true},
|
||||
{"displayName": "Village Entrances", "archetype": "END", "width": 16, "height": 8, "depth": 16, "enabled": true},
|
||||
{"displayName": "", "archetype": "STRAIGHT", "width": 16, "height": 3, "depth": 3, "enabled": true},
|
||||
{"displayName": "", "archetype": "CORNER", "width": 3, "height": 3, "depth": 3, "enabled": true},
|
||||
{"displayName": "", "archetype": "TEE", "width": 3, "height": 3, "depth": 3, "enabled": true},
|
||||
{"displayName": "", "archetype": "CROSS", "width": 3, "height": 3, "depth": 3, "enabled": true}
|
||||
],
|
||||
"themeSets": [
|
||||
{"key": "variant-1", "weight": 1}
|
||||
],
|
||||
"requireCaps": false,
|
||||
"placeMode": "STRUCTURE_PIECE",
|
||||
"vanillaSource": "minecraft:village_plains"
|
||||
"edit": [],
|
||||
"loot": []
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 `jigsaw-pools/<key>.json` (`IrisJigsawPool`)
|
||||
|
||||
| Field | Default | Meaning |
|
||||
| Field | Default / range | Meaning |
|
||||
|---|---|---|
|
||||
| `pieces` | min 1 entry | Weighted entries: `{ "piece": "<key>", "weight": 1 }` or `{ "empty": true, "weight": 3 }`. |
|
||||
| `fallback` | `""` | Pool tried after this one — and used **alone** once `maxDepth` is reached. Exactly one level deep: a fallback's own fallback is never consulted. Empty = stop expanding at max depth. |
|
||||
| `startPool` | required | Pool used for the first piece |
|
||||
| `maxDepth` | `7`, range `1..30` | Maximum recursive connector depth |
|
||||
| `maxSizeChunks` | `8`, range `1..32` | Horizontal assembly radius in chunks |
|
||||
| `mode` | Hand-authored schema fallback `SPATIAL_JIGSAW`; Studio `create` default `PLANAR_JIGSAW` | `PLANAR_JIGSAW` enables strict cell validation; `SPATIAL_JIGSAW` is freeform |
|
||||
| `compatibility` | `IRIS_EXTENDED` | `VANILLA_PORTABLE` enables portable connector restrictions and is required for export |
|
||||
| `branchFailurePolicy` | `FAIL_ASSEMBLY` | `FAIL_ASSEMBLY` rejects an ordinary unresolved optional branch before maximum depth; `TERMINATE_BRANCH` ends only that branch and is required for vanilla portability |
|
||||
| `cellSize` | `16 × 16 × 16`; Studio X/Z `1..128`, Y `1..192`, volume `<=2,097,152` | Spatial workcell capacity; legacy uniform fallback when a planar graph has no `planarWorkcells` |
|
||||
| `spatialWorkcellDisplayName` | empty | Optional 64-code-point author label for `workcell/spatial`; empty displays `Spatial` |
|
||||
| `planarWorkcells` | Six unique archetypes; width/depth `3..128`, height `1..192`, volume `<=2,097,152`; `displayName` empty | Independent planar capacity, author label, and assembly/export enabled state for Blank, End Cap, Hallway, L Junction, T Junction, and Cross Junction; empty labels use the canonical name |
|
||||
| `themeSets` | Empty means implicit unthemed; positive unique key weights | One coherent theme is selected per assembly; an Iris Studio project starts with `variant-1`, while a vanilla-compatible project omits themes |
|
||||
| `requireCaps` | `false` | Require every unresolved connector pool to place a physical terminal piece through its direct fallback; Iris-only |
|
||||
| `placeMode` | `STRUCTURE_PIECE` | Object placement mode used for each piece |
|
||||
| `edit` | empty | Structure-wide Iris block replacements; not portable |
|
||||
| `loot` | empty | Iris loot injection for piece containers; not portable |
|
||||
| `vanillaSource` | empty | Import provenance; not an authoring target |
|
||||
|
||||
An `empty: true` entry terminates the branch without placing. Both `empty` and `piece` in one entry is a validation error; weights must be ≥ 1; fallback cycles are blocked.
|
||||
`rules limits` owns `maxDepth` and `maxSizeChunks`; `rules fallback` owns direct pool fallback; the GUI owns workcell capacity/labels, theme weights, `requireCaps`, per-piece size/labels/themes/rules, chance, rotation, and deletion; `connector channel` owns the saved connector's Iris-only channel. Rules without an in-game control, including `branchFailurePolicy`, `placeMode`, structure `edit`, structure `loot`, pool `mandatoryFallback`, and empty entries, remain schema-backed JSON fields. Transaction-owned projects reject outside resource edits on the next mutation; use the Studio controls or recreate/adopt the project through an ownership-aware workflow.
|
||||
|
||||
### Pool: `jigsaw-pools/<key>.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"pieces": [
|
||||
{ "piece": "mypack/watchtower/corridor", "weight": 5 },
|
||||
{ "piece": "mypack/watchtower/corridor_short", "weight": 2 },
|
||||
{ "weight": 3, "empty": true }
|
||||
{"piece": "village/demo/hall", "weight": 4, "chance": 0.75, "empty": false},
|
||||
{"weight": 1, "chance": 1.0, "empty": true}
|
||||
],
|
||||
"fallback": "mypack/watchtower/pool/terminators"
|
||||
"fallback": "village/demo/end",
|
||||
"mandatoryFallback": false
|
||||
}
|
||||
```
|
||||
|
||||
### 1.3 `jigsaw-pieces/<key>.json` (`IrisJigsawPiece`)
|
||||
`weight` must be positive. `chance` is finite `0..1` and independently gates that exact membership before weighting; zero never passes and one always passes. An `empty: true` entry canonically omits `piece`; omitted and blank piece keys are both accepted for existing graphs. It terminates its branch only when empty termination is allowed and stops later primary or fallback candidates. Native conversion never rewrites a start-pool member as empty or omits it solely because it has no connectors. Every non-start connectorless member in a pool with a distinct fallback also remains physical so weighted failed primary attachments reach that fallback. Conversion emits `empty: true` only when a non-start pool has one all-air connectorless source member and no fallback or a self-fallback. The same all-air connectorless member in a mixed no/self-fallback pool is omitted with an explicit selection-weight and RNG-consumption fidelity loss rather than becoming an empty choice that could cut off later valid candidates; other connectorless nonempty members in no/self-fallback non-start pools are omitted as inert with exact block, fallback, selection-weight, and RNG-consumption loss. Converted native graphs set `branchFailurePolicy: TERMINATE_BRANCH`, so ordinary optional candidate exhaustion ends only that connector branch. A pool with no entries terminates when no fallback is required and does not continue into its declared fallback. `fallback` is one direct pool tried after ordinary primary failure or at maximum depth; its own fallback is not chained into the same selection. `mandatoryFallback: true` applies the physical-terminal requirement to this pool even when structure `requireCaps` is false.
|
||||
|
||||
| Field | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `object` | required | Object (`objects/<key>.iob`) for this piece. |
|
||||
| `connectors` | optional | Connection points. For a terminal cap, **omit the key** — runtime and pack validation accept an empty list; generated VSCode schema requires at least one entry when the key is present. |
|
||||
| `rotatable` | `true` | Whether the assembler may Y-rotate the piece. |
|
||||
### Piece: `jigsaw-pieces/<key>.json`
|
||||
|
||||
### 1.4 Connectors
|
||||
|
||||
| Field | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `position` | required | Block coordinate **inside the object**, 0-based from lowest corner: `0..W-1 / 0..H-1 / 0..D-1`. |
|
||||
| `direction` | required | Face this connector points out of. |
|
||||
| `top` | `UP_POSITIVE_Y` | Authored "up" for `ALIGNED` roll lock. Write it explicitly. |
|
||||
| `pool` | required | Pool for the connecting piece. |
|
||||
| `name` | `""` | Identity this connector **exposes**. |
|
||||
| `targetName` | `""` | `name` this connector wants on the other piece. |
|
||||
| `joint` | `ROLLABLE` | `ROLLABLE` (free roll) or `ALIGNED` (roll locked — doorways/streets). |
|
||||
|
||||
`direction` / `top` values:
|
||||
|
||||
```
|
||||
UP_POSITIVE_Y DOWN_NEGATIVE_Y NORTH_NEGATIVE_Z SOUTH_POSITIVE_Z EAST_POSITIVE_X WEST_NEGATIVE_X
|
||||
```json
|
||||
{
|
||||
"object": "village/demo/hall",
|
||||
"displayName": "Market Hall",
|
||||
"connectors": [
|
||||
{
|
||||
"position": {"x": 8, "y": 8, "z": 0},
|
||||
"direction": "NORTH_NEGATIVE_Z",
|
||||
"top": "UP_POSITIVE_Y",
|
||||
"pool": "village/demo/start",
|
||||
"name": "iris:planar",
|
||||
"targetName": "iris:planar",
|
||||
"channel": "",
|
||||
"joint": "ALIGNED",
|
||||
"finalState": "minecraft:structure_void",
|
||||
"selectionPriority": 0,
|
||||
"placementPriority": 0
|
||||
}
|
||||
],
|
||||
"rotatable": true,
|
||||
"collidable": true,
|
||||
"themes": ["variant-1"],
|
||||
"rules": {
|
||||
"minimumDepth": 0,
|
||||
"maximumDepth": 30,
|
||||
"minimumPlacements": 0,
|
||||
"maximumPlacements": 0,
|
||||
"terminal": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Unlike vanilla jigsaw blocks:
|
||||
Positions are unsigned object coordinates: `(0,0,0)` is the object's minimum corner. The referenced `.iob` remains the geometry source and owns this variant's exact width, height, and depth; Studio materializes connector markers only in the authoring world. `displayName` is optional 64-code-point author metadata and falls back to the piece key's final segment. `collidable` defaults to `true`; use `false` only when an intentional connector scaffold must share its stored bounds with physical pieces. `themes: []` makes the piece available to every selected theme. Depth is `0..30`; placement counts are `0..512`, and `maximumPlacements: 0` means unbounded within the 512-piece safety cap. A terminal piece is placed but never expands its connectors.
|
||||
|
||||
- Connectors are **JSON metadata, not blocks**. The block at the connector cell stays — no `final_state`. Jigsaw blocks inside an object are stripped on load.
|
||||
- Matching is target-to-name: candidate attaches when its `name` equals the source's `targetName`, directions oppose after trial rotation, and for `ALIGNED` sources rotated `top` matches. Names are exact, case-sensitive.
|
||||
## Natural placement
|
||||
|
||||
## 2. Assembly
|
||||
|
||||
Constants: hard piece cap **512**, max depth **30**, max size **32 chunks**.
|
||||
|
||||
1. **Start.** Weighted-pick from `startPool` (`empty` → nothing), random cardinal rotation if `rotatable`, place at origin, queue connectors at depth 0.
|
||||
2. **BFS.** Pop open connector; resolve its `pool`. Below `maxDepth` try primary then direct fallback; at `maxDepth` only fallback.
|
||||
3. **Candidates** in weighted-random order. For each piece, test connectors (name match, opposed direction) across rotations — `{0,90,180,270}` shuffled for `ROLLABLE`, fixed for `ALIGNED`, `{0}` when `rotatable: false`.
|
||||
4. **Geometry.** New piece positioned so connector cells are adjacent along source facing.
|
||||
5. **Rejection.** Discard if box leaves `maxSizeChunks * 16` radius or intersects a placed piece (boxes may touch, not overlap).
|
||||
6. **Success** places the piece and queues remaining connectors at depth + 1.
|
||||
|
||||
**Authoring rule:** a connector at depth **below** `maxDepth` that cannot be satisfied drops the **whole** assembly for that chunk, silently, with no retry. At `maxDepth` and beyond, unsatisfied connectors are tolerated.
|
||||
|
||||
Give every pool a terminating option — `empty: true` or a `fallback` of connector-less caps.
|
||||
|
||||
Loud failures (missing pool/piece/object, malformed connector, non-positive weight, 512-piece cap with connectors still open) indicate a broken graph and are caught by pack validation.
|
||||
|
||||
**Determinism.** Assembly is a pure function of `(mantle seed, chunk X, chunk Z, placement identity)`.
|
||||
|
||||
## 3. Placement
|
||||
|
||||
`structures[]` on a **biome**, **region**, or **dimension**. The same array hosts native placements (`nativeStructures` — `22 - Native Structures & Datapacks.md`); each placement must declare exactly one of `structures` / `nativeStructures`.
|
||||
|
||||
### 3.1 Fields (Iris backend)
|
||||
|
||||
| Field | Default | Meaning |
|
||||
|---|---|---|
|
||||
| `structures` | `[]` | Iris structure keys. One picked uniformly per start chunk. Duplicates are a validation error. |
|
||||
| `placementId` | `""` | Stable identity. Empty derives identity from content — reordering does not move structures; changing settings does. Set when retuning spacing/heights without re-rolling positions, or when two placements would otherwise be identical. |
|
||||
| `distribution` | `RANDOM_SPREAD` | `RANDOM_SPREAD` / `DENSITY` / `CONCENTRIC_RINGS`. |
|
||||
| `spacing` | `32` (1..4096) | RANDOM_SPREAD grid cell size in chunks. |
|
||||
| `separation` | `8` | RANDOM_SPREAD minimum chunk separation; must be **smaller than** `spacing`. |
|
||||
| `salt` | `165745296` | Mixed into placement RNG. |
|
||||
| `density` | `0.02` (0..1) | DENSITY per-chunk start probability. |
|
||||
| `ringCount` / `ringDistance` / `ringSpread` | `128` / `32` / `3` | CONCENTRIC_RINGS around origin. |
|
||||
| `minHeight` / `maxHeight` | `-2032` / `2032` | Surface: pass/fail gate on surface Y. Underground: Y band. |
|
||||
| `underground` | `false` | Start at deterministic random Y in band, then shift down under terrain across footprint. |
|
||||
| `underwater` | `false` | If false, submerged origins skipped. |
|
||||
| `terrain` | `{mode: SOURCE}` | Iris backend: `SOURCE`, `PRESERVE`, `BORE`, `FORCE_CARVE`. **`VACUUM` and `ENCASE` are rejected** for Iris assemblies (native only). |
|
||||
| `stilt` | unset | Foundation columns under assembly bottom cells: `maxDepth` (default 64), `palette` (default cobblestone), `supportNonOccluding`. `spacing` is honored on the **native** backend only. Placed only when every piece succeeded. |
|
||||
| `nativeSuppression` | `NONE` | `REPLACE_SOURCE` suppresses each structure's `vanillaSource` native generation. Dimension-level only; failures then throw (no native fallback). |
|
||||
|
||||
`terrain` sub-fields for `BORE` / `FORCE_CARVE`: `horizontalPadding` (0..128), `ceilingPadding` (0..128), `floorPadding` (0..64; 0 preserves floor). `FORCE_CARVE`: `shape` (`BOX`/`ROUNDED`/`ERODED`) and for `ERODED` the erosion/lobe knobs. `BORE` always clears a box.
|
||||
|
||||
### 3.2 Scoping
|
||||
|
||||
Per chunk, Iris samples biome and region at chunk center and collects that biome's placements, that region's placements, and all dimension placements. Biome-level placements fire only where the center lands in that biome.
|
||||
|
||||
### 3.3 Height
|
||||
|
||||
- Surface: surface height at origin is anchor Y, gated by `minHeight`/`maxHeight`. With default `STRUCTURE_PIECE`, pieces stamp **centered** on that Y (start piece midpoint at surface). Assemblies with non-`STRUCTURE_PIECE`, non-`FLOATING` place mode re-anchor so base sits at surface.
|
||||
- Underground: seeded random Y in band, then burial shift so envelope (including carve padding) stays under surface; if it cannot fit, chunk skipped.
|
||||
- No `yBand` on this path — that belongs to native adjustments.
|
||||
|
||||
### 3.4 Worked example
|
||||
Place an Iris jigsaw by adding an `IrisStructurePlacement` object to `structures[]` on a dimension, region, or biome. Surface-biome placements apply where that surface biome owns the start chunk. A cave biome contributes only placements whose resolved anchor is one of the cave modes. Region and dimension placements remain broader scopes.
|
||||
|
||||
```json
|
||||
{
|
||||
"structures": [
|
||||
{
|
||||
"placementId": "ruined-watchtower",
|
||||
"structures": ["mypack/watchtower"],
|
||||
"structures": ["village/demo"],
|
||||
"placementId": "village-demo-surface",
|
||||
"distribution": "RANDOM_SPREAD",
|
||||
"spacing": 48,
|
||||
"separation": 12,
|
||||
"salt": 918273645,
|
||||
"minHeight": 62,
|
||||
"maxHeight": 140,
|
||||
"terrain": {
|
||||
"mode": "FORCE_CARVE",
|
||||
"shape": "ROUNDED",
|
||||
"horizontalPadding": 4,
|
||||
"ceilingPadding": 6,
|
||||
"floorPadding": 0
|
||||
},
|
||||
"stilt": {
|
||||
"maxDepth": 48,
|
||||
"palette": { "palette": [ { "block": "minecraft:cobblestone" } ] }
|
||||
}
|
||||
"spacing": 32,
|
||||
"separation": 8,
|
||||
"salt": 165745296,
|
||||
"anchor": "SURFACE",
|
||||
"minHeight": -64,
|
||||
"maxHeight": 320,
|
||||
"terrain": {"mode": "SOURCE"},
|
||||
"underwater": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.5 Distribution modes
|
||||
| Placement rule | Fields | Behavior |
|
||||
|---|---|---|
|
||||
| Random spread | `spacing`, `separation`, `salt` | One deterministic attempt per spacing grid cell; `spacing` must exceed `separation` |
|
||||
| Density | `density` | Independent deterministic per-chunk probability `0..1` |
|
||||
| Concentric rings | `ringCount`, `ringDistance`, `ringSpread` | Stronghold-like deterministic rings around world origin |
|
||||
| Surface | `anchor: SURFACE` | Surface Y must pass the inclusive `minHeight..maxHeight` gate |
|
||||
| Height band | `anchor: HEIGHT_BAND` | Deterministic random Y inside the inclusive band |
|
||||
| Legacy | `anchor: LEGACY` | `underground=false` resolves to `SURFACE`; `underground=true` resolves to `HEIGHT_BAND` |
|
||||
|
||||
- **`RANDOM_SPREAD`** — world cut into `spacing`-chunk cells, one candidate per cell, offset by up to `spacing - separation`.
|
||||
- **`DENSITY`** — independent per-chunk roll. Low densities make `/iris goto` searches expensive.
|
||||
- **`CONCENTRIC_RINGS`** — `ringCount` placements total, rings `ringDistance` chunks apart, `ringSpread` per ring. Chunk 0,0 is never a start.
|
||||
`placementId` is the stable authored identity for distribution. Set it when multiple placements share the same structure or when you want unrelated field/list reordering not to move starts. A placement with several `structures` keys chooses one uniformly; pool weights control pieces inside the chosen graph, not world-level start frequency.
|
||||
|
||||
### 3.6 Place modes for pieces
|
||||
Only newly generated chunks use a changed placement. Direct `/iris structure place` and Jigsaw Studio preview do not prove spacing, biome scope, height gates, or natural generation.
|
||||
|
||||
Structure `placeMode` matters most for single-piece structures: a terrain-following mode (e.g. `PAINT`, stilts) places through the normal object placer at the surface. Multi-piece assemblies stamp as `STRUCTURE_PIECE` at assembled coordinates; `FLOATING` is downgraded to `STRUCTURE_PIECE`. `underground` placements always stamp `STRUCTURE_PIECE`, with `ORGANIC_STILT` / `CEILING_HANG` as the exception. If in doubt, leave `STRUCTURE_PIECE`.
|
||||
|
||||
## 4. Authoring workflow
|
||||
|
||||
### 4.1 No in-game jigsaw editor
|
||||
|
||||
There is no `/iris jigsaw` command or connector wand. Build piece objects in-game and author piece, pool, and structure JSON directly. `/iris studio vscode [dimension=<pack>]` (alias `vsc`) provides JSON-schema autocomplete for all three folders.
|
||||
|
||||
### 4.2 End to end
|
||||
|
||||
**1. Studio and build**
|
||||
|
||||
```
|
||||
/iris studio open <pack>
|
||||
/iris object wand
|
||||
```
|
||||
|
||||
Note object-local coordinates of connector cells — local `(0,0,0)` is the selection corner with lowest X, Y, Z. Leave connector cells as air (or the keep block).
|
||||
|
||||
**2. Save object** (`19 - Objects.md`):
|
||||
|
||||
```
|
||||
/iris object save dimension=<pack> mypack/watchtower/base overwrite=true
|
||||
```
|
||||
|
||||
**3. Piece JSON** — `jigsaw-pieces/mypack/watchtower/base.json`:
|
||||
### Cave anchors
|
||||
|
||||
```json
|
||||
{
|
||||
"object": "mypack/watchtower/base",
|
||||
"rotatable": true,
|
||||
"connectors": [
|
||||
"structures": [
|
||||
{
|
||||
"position": { "x": 4, "y": 1, "z": 0 },
|
||||
"direction": "NORTH_NEGATIVE_Z",
|
||||
"top": "UP_POSITIVE_Y",
|
||||
"pool": "mypack/watchtower/pool/corridors",
|
||||
"name": "mypack:tower_side",
|
||||
"targetName": "mypack:corridor_end",
|
||||
"joint": "ALIGNED"
|
||||
"structures": ["stronghold/demo"],
|
||||
"placementId": "stronghold-demo-deep-caves",
|
||||
"distribution": "RANDOM_SPREAD",
|
||||
"spacing": 24,
|
||||
"separation": 8,
|
||||
"salt": 984211,
|
||||
"anchor": "CAVE_FLOOR",
|
||||
"minHeight": -48,
|
||||
"maxHeight": 80,
|
||||
"caveBiomes": ["carving/deep"],
|
||||
"caveAnchorAttempts": 12,
|
||||
"caveAnchorScanStep": 1,
|
||||
"caveMinimumClearance": 5,
|
||||
"terrain": {"mode": "PRESERVE"}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Matching corridor connector faces the opposite way and names itself `mypack:corridor_end`.
|
||||
| Anchor | Required carved-space geometry | Assembly alignment |
|
||||
|---|---|---|
|
||||
| `CAVE_FLOOR` | Solid/non-carved cell immediately below plus upward carved run | Lowest assembled piece bound moves to the anchor Y |
|
||||
| `CAVE_CEILING` | Solid/non-carved cell immediately above plus downward carved run | Highest assembled piece bound moves to the anchor Y |
|
||||
| `CAVE_CENTER` | Candidate is the actual midpoint of its contiguous carved cavern run, which must meet the clearance requirement | Assembly bounding-box midpoint moves to the anchor Y |
|
||||
| `CAVE_ANY` | A clearance-sized carved run is centered around the candidate | Assembly bounding-box midpoint moves to the anchor Y |
|
||||
|
||||
Connector geometry:
|
||||
Iris tests up to `caveAnchorAttempts` deterministic, unique X/Z columns in the start chunk and scans the clipped `minHeight..maxHeight` band in increments of `caveAnchorScanStep`. It stops at the first column with matches and chooses deterministically among all valid anchors in that column. Runtime clamps attempts to `1..64`, scan step to `1..16`, and clearance to `1..64`; at most 64 of the chunk's 256 columns are visited. `caveMinimumClearance` is the required vertical carved run. Empty `caveBiomes` accepts any resolved cave biome; otherwise trimmed, case-normalized keys with or without a namespace are rechecked against the cave/mantle biome at the actual X/Y/Z anchor.
|
||||
|
||||
- Connector cell is **inside** the object, on the outermost layer the neighbor butts against. Neighbor matching cell lands adjacent along `direction`.
|
||||
- Vertical connectors with `joint: ROLLABLE` suit toppers; horizontal doorways/streets want `ALIGNED`.
|
||||
For cave anchors, `underwater` checks `MatterCavern` at the actual anchor rather than the surface ocean height. A null or non-cavern cell never qualifies. With `underwater: false`, ordinary cavern air must be above the dimension's `caveLavaHeight`, explicit water/lava is rejected, and forced-air cavern matter remains dry even below that threshold. With `underwater: true`, fluid cavern cells are allowed but the cell must still be carved cavern matter.
|
||||
|
||||
**4. Pools** — every reachable pool needs a terminating option.
|
||||
Cave placement scope is sampled at the start chunk's center. A cave-biome `structures[]` list contributes cave anchors only; region and dimension placements remain broader, and a placement-level `caveBiomes` list revalidates the actual anchor. Lookup uses existing Iris carved-space mantle data, so a locator cannot resolve an ungenerated distant cave anchor until terrain generation has produced that mantle.
|
||||
|
||||
**5. Structure** — `structures/mypack/watchtower.json`:
|
||||
The anchor test reads one vertical `MatterCavern` column, not the complete assembled volume. `SOURCE` and `PRESERVE` can therefore leave pieces intersecting cave walls. Use `BORE` or `FORCE_CARVE` when the structure must create a reliable envelope, or inspect the full volume in gameplay when preserving the cave. Cave anchors apply to editable Iris `structures`, not the `nativeStructures` backend.
|
||||
|
||||
```json
|
||||
{
|
||||
"startPool": "mypack/watchtower/pool/starts",
|
||||
"maxDepth": 5,
|
||||
"maxSizeChunks": 4,
|
||||
"placeMode": "STRUCTURE_PIECE",
|
||||
"loot": ["mypack/watchtower_chest"]
|
||||
}
|
||||
## Vanilla datapack export
|
||||
|
||||
Create the project with `compatibility=vanilla`, then keep the graph within the strict subset below. An existing graph is exportable only when its saved compatibility is `VANILLA_PORTABLE` and its branch policy is `TERMINATE_BRANCH`; Studio has no compatibility-toggle or branch-policy control. A vanilla-compatible Studio project writes that policy and omits the default Iris theme and terminal-rule metadata. Export reads the committed graph, not pending workcell blocks, so wait for autosave to finish and confirm the automatic evaluation is no longer `PENDING`, `STALE`, or `INVALID`:
|
||||
|
||||
```text
|
||||
/iris jigsaw export namespace=demo output=village-demo format=zip replace=false
|
||||
```
|
||||
|
||||
**6. Placement** in biome, region, or dimension (3.4).
|
||||
Output is written under `<Iris data>/packs/exports/`. Compilation, NBT encoding, compression, and publication run off the server thread; wait for the final result rather than treating the initial background-start message as success. One player cannot start a second export while their first is running, and the same normalized output cannot be published by two concurrent commands. Completion names the originating structure even if that Studio was closed or replaced while export was running. `output` is one direct artifact name: the supplied value must be 1–128 characters, start with a letter, number, `_`, or `-`, and then use only letters, numbers, `.`, `_`, or `-`. Leading/trailing whitespace, `.`, absolute paths, slash or backslash, nested paths, and traversal names are rejected before export. `format=zip` adds `.zip` when needed. The publisher stages the complete directory or zip and replaces the destination atomically only when `replace=true`; existing output is otherwise rejected.
|
||||
|
||||
**7. Validate, inspect, place, iterate** (section 5).
|
||||
The command emits a Minecraft 26.2 datapack whose `pack.mcmeta` uses `min_format: [107, 1]` and `max_format: 107`, plus a default `minecraft:plains` biome tag, an empty processor list, template pools, compressed structure-template NBT, one jigsaw worldgen structure, and one random-spread structure set. Command-level export defaults are:
|
||||
|
||||
### 4.3 Reload
|
||||
|
||||
Studio hotloads into newly generated chunks. Outside studio, close and reopen the world after changes. Files written by `/iris structure import` are ownership-tracked; hand edits make later imports refuse to overwrite those files.
|
||||
|
||||
## 5. Testing and debugging
|
||||
|
||||
**Pack validation** — use `/iris pack validate pack=<pack>` on Bukkit or `/iris pack validate <pack>` on modded. It resolves references, connector bounds, weight/enum/range, `separation` ≥ `spacing`, duplicate `placementId`s, VACUUM/ENCASE on Iris backend; compiles graphs with reachability; and runs 16 seeded sample assemblies per structure. Common hand-author diagnostic: `NO_COMPATIBLE_CONNECTOR`.
|
||||
|
||||
**`/iris structure info <dimension> <structure>`** — compile + one sample assembly (piece count, footprint). No world needed. "assembled 0 pieces" means start pool resolved empty.
|
||||
|
||||
**`/iris structure place <dimension> <structure>`** (player) — stamp at your location (raw geometry; no carve/stilts/loot).
|
||||
|
||||
**`/iris structure verify <dimension> [radius=48]`** — Iris placement plans in range (`[iris-planned]` / `[iris-not-found]`) plus native statuses.
|
||||
|
||||
**`/iris goto structure <key>`** (`/iris find structure`) — locate nearest planned instance; Iris keys and `vanillaSource` aliases. Iris search up to 1024 chunks; native locate 100 chunks. `/iris goto unregistered` dumps excluded keys and reasons.
|
||||
|
||||
At world load, graph diagnostics log with `[StructureGraph:<key>]`. A structure without a runtime-viable graph refuses to place.
|
||||
|
||||
### Failure modes
|
||||
|
||||
| Symptom | Likely cause |
|
||||
| Vanilla setting | Export default |
|
||||
|---|---|
|
||||
| Only start piece | Every connector drew `empty` first, or all candidates rejected at max depth with no fallback. |
|
||||
| Nothing, no error | Start pool rolled `empty`; submerged with `underwater: false`; surface Y outside band; grid did not select chunk. |
|
||||
| Appears some seeds, vanishes others | Connector below `maxDepth` unsatisfiable for that seed. Add `empty` / terminator fallbacks. |
|
||||
| `references missing connector pool` | Pool key typo or file not under `jigsaw-pools/`. |
|
||||
| `exceeded the hard piece cap of 512` | Runaway recursion: always more connectors, no empty weight, depth too high. |
|
||||
| Pieces visually clip | Bounding-box collision; decorative overhang inside box still "fits". `/iris object shrink` pieces. |
|
||||
| VACUUM/ENCASE validate or runtime error | Native-only modes; use BORE / FORCE_CARVE, or place via `nativeStructures`. |
|
||||
| `REPLACE_SOURCE ... failed in chunk` | Suppression without guaranteed output; dimension-level placement, valid `vanillaSource`, graph must guarantee output. |
|
||||
| Biomes | `minecraft:plains` |
|
||||
| Start height | absolute `0`, projected to `WORLD_SURFACE_WG` |
|
||||
| Generation step | `surface_structures` |
|
||||
| Terrain adaptation | `none` |
|
||||
| Expansion hack | `false` |
|
||||
| Maximum vertical distance | `4064` |
|
||||
| Structure-set placement | random spread: spacing `32`, separation `8`, salt `0`, frequency `1`, linear spread |
|
||||
|
||||
## 6. Mapping from vanilla datapack jigsaws
|
||||
### Strict export blockers
|
||||
|
||||
| Vanilla | Iris |
|
||||
|---|---|
|
||||
| `worldgen/structure` type jigsaw | `structures/*.json` |
|
||||
| `start_pool` | `startPool` |
|
||||
| `size` | `maxDepth` |
|
||||
| `max_distance_from_center` | `maxSizeChunks` (chunks, hard bound) |
|
||||
| `worldgen/template_pool` | `jigsaw-pools/*.json` |
|
||||
| `elements[]` | `pieces[]` |
|
||||
| `minecraft:empty_pool_element` | `"empty": true` |
|
||||
| `fallback` | same idea, one level deep |
|
||||
| template `.nbt` | object + `jigsaw-pieces/*.json` |
|
||||
| jigsaw block pool/name/target/joint/facing/top | connector `pool`/`name`/`targetName`/`joint`/`direction`/`top` |
|
||||
| `final_state` | none — block at cell stays |
|
||||
| structure set `random_spread` | `RANDOM_SPREAD` |
|
||||
| structure set `concentric_rings` | `CONCENTRIC_RINGS` |
|
||||
| (none) | `DENSITY` |
|
||||
Export fails instead of dropping or approximating any of these features:
|
||||
|
||||
`terrain_adaptation`, `start_height`, `projection`, and per-element settings have no direct equivalents; nearest are placement height/underground fields and `terrain.mode`.
|
||||
- Structure compatibility is not `VANILLA_PORTABLE`.
|
||||
- Structure `branchFailurePolicy` is not `TERMINATE_BRANCH`.
|
||||
- Structure themes, piece theme membership, non-default depth/placement/terminal rules, structure `requireCaps`, pool `mandatoryFallback`, or membership `chance` other than `1` are present.
|
||||
- `placeMode` is not `STRUCTURE_PIECE`, or structure-wide `edit` or `loot` is non-empty.
|
||||
- `maxDepth` is outside `1..20`, or `maxSizeChunks × 16` exceeds Minecraft's 128-block horizontal limit.
|
||||
- A piece has `rotatable: false`.
|
||||
- A piece has `collidable: false`; vanilla templates have no equivalent per-piece collision flag.
|
||||
- A pool weight is outside `1..150`.
|
||||
- A resource key, connector name/target, namespace, orientation, block state, or final state is not vanilla-valid.
|
||||
- A connector has a non-empty Iris channel, duplicates another connector position, or its `finalState` does not exactly match the `.iob` block at that cell (`minecraft:structure_void` for an absent cell).
|
||||
- An object contains tile payloads, a block entity, a custom-content block, or retained `jigsaw`, `structure_block`, or `structure_void` marker blocks.
|
||||
|
||||
This exporter does not export tile/block-entity NBT. A chest, spawner, sign, or other tile-bearing object therefore blocks strict export even when it works intrinsically in Iris. The command exposes only namespace, one direct output filename, directory/zip format, and replacement choice; biome, height projection, generation step, terrain adaptation, and structure-set placement remain the fixed defaults above. Edit the emitted datapack after export if those defaults are not the desired vanilla placement.
|
||||
|
||||
Test the exported artifact on an unmodded Minecraft 26.2 server or client: stop the disposable world, install it in that world's `datapacks/`, restart so the worldgen registries load it, confirm it is enabled without data errors, locate `<namespace>:<resourcePath>`, and generate fresh chunks around the located start. `/reload` can list a newly copied pack as enabled without registering its worldgen structure in the already running world, so it is not a substitute for this restart. Iris validation and NBT round-trip tests do not substitute for the vanilla load and generation check.
|
||||
|
||||
## Failure recovery
|
||||
|
||||
| Symptom | Meaning | Recovery |
|
||||
|---|---|---|
|
||||
| Create reports occupied/conflicting files | Add-only ownership refused to overwrite existing resources | Choose a new structure key or deliberately remove/migrate the old graph outside this workflow |
|
||||
| Create reports success but Studio does not open | The complete graph was created before the follow-up open request encountered another owner, pending autosave, or lifecycle transition | Resolve the active Studio guard, then run `open` for the newly created structure; do not rerun `create` against its now-owned files |
|
||||
| A loaded variant is Read-only | Its graph is unowned or has managed datapack provenance | Close Studio, run `adopt inspect`, review the disposition/diagnostics, and apply the plan; managed input must use a clone target |
|
||||
| Adoption plan is expired, unknown, or stale | Its 15-minute in-memory plan was consumed/expired, or a pinned source/target changed | Run `adopt inspect` again and review the new plan; no stale plan is written |
|
||||
| Conversion refuses the source | The key is absent, is not a live registered jigsaw, has an incomplete graph, or the add-only target is occupied | Keep it native, choose a valid registered jigsaw, repair its source datapack, or choose a new target; use `/iris structure import` for non-jigsaw templates |
|
||||
| Ownership conflict on capture/edit | An owned file changed outside the last committed transaction | Restore the exact owned graph from version control/backup; Studio will not overwrite the mismatch |
|
||||
| Close refuses with pending work | An owned workcell is dirty or autosave/graph work is running | Wait for autosave, use **Flush Autosave Now** to expedite it, or use `discard=true` only when losing pending edits is deliberate |
|
||||
| An external plugin edit is not captured | The plugin bypassed Bukkit's covered mutation events | Have the integration call `JigsawStudioService.markDirty(...)` for affected coordinates or `markAllDirty(...)`; autosave then follows normally |
|
||||
| Autosave has no active/editable variant | The workcell is empty or its loaded variant is read-only | Load an owned variant, or adopt/clone the graph first |
|
||||
| Autosave reports Loading, Invalid, or not hydrated | Variant materialization or real jigsaw block-entity hydration is incomplete/failed | Wait for completion, reopen or reload the variant, and do not build until the scoreboard reports a stable state |
|
||||
| Capacity succeeds but the cage is still old | Capacity changes deliberately retain the current generated layout and never rewrite variant objects | Close and reopen Studio before editing; resize individual variants separately when their geometry should change |
|
||||
| Autosave says a chunk is not loaded | Part of the capture volume is unloaded | Visit/load the whole workcell; the autosave retry remains pending, or use **Flush Autosave Now** after loading it |
|
||||
| Multi-chunk autosave aborts | One owning-region schedule/snapshot failed, a chunk unloaded, Studio changed, marker/tile capture failed, or aggregation was incomplete/invalid | Keep the complete capture volume loaded and fix the reported cause; no graph file is written from a partial capture |
|
||||
| Marker capture fails | Marker NBT is incomplete, final state is invalid, or active NMS cannot serialize the tile | Fix the named marker field or use the matching supported Bukkit/NMS build |
|
||||
| The chest GUI closes after an action | The accepted operation is asynchronous and the GUI intentionally does not live-refresh | Wait for its player message, then right-click the chest again |
|
||||
| A named stick stops working | It uses schema `1`, its request ID belongs to a closed/replaced Studio, or the bound workcell/variant/pool entry changed | Discard the stale stick and take a schema-`2` replacement from the current Toolbox |
|
||||
| A queued duplicate cancels | The Studio request/session or one pinned source variant changed before autosave completed | Reopen the current controls, confirm the intended loaded source variants, and request the duplicate again |
|
||||
| Another player cannot edit or run a mutating command | The active Jigsaw Studio belongs to its activation owner | Have the owner perform the work or close the Studio; do not bypass world protection |
|
||||
| Evaluation is `STALE` | A workcell edit is waiting for autosave | Wait for capture; evaluation reruns from the new committed graph automatically |
|
||||
| Evaluation is `INVALID` | Compilation or the seed-`1337` assembly failed | Fix the displayed first diagnostic; wrong pool/name/facing, impossible rules, or an uncappable required fallback are common causes |
|
||||
| Permanent preview is empty | Evaluation is pending/invalid or seed `1337` intentionally produced no structure | Read the evaluation detail; fix invalid data or change chance/start rules if an empty result was not intended |
|
||||
| Project deletion is blocked | Another JSON resource or ownership manifest still references a resource owned by the project | Remove or repoint the reported external reference, let autosave finish, then inspect deletion again |
|
||||
| Studio closes but project deletion fails | The hash-pinned removal failed after a successful close | The project files remain on disk; reopen or back them up before retrying |
|
||||
| Transaction reports cleanup required | Authored graph committed but staging cleanup failed | Preserve console output and remove/recover only the named transaction with operator care; do not re-author blindly |
|
||||
| Export is rejected | At least one strict portability blocker remains | Fix each reported diagnostic; do not bypass by deleting diagnostics or assuming Iris runtime success proves vanilla fidelity |
|
||||
| Export output name is rejected | The value is not one direct safe artifact name | Remove whitespace, separators, traversal, and unsupported characters; keep the supplied name within 128 characters |
|
||||
|
||||
## Precise smoke-test checklist
|
||||
|
||||
Run this in a purpose-named disposable pack/world and record each gate separately.
|
||||
|
||||
1. **Creation:** create a planar `IRIS_EXTENDED` project without optional mode, compatibility, dimensions, or seed. Confirm planar/Iris/16×16×16/1337 defaults, one structure, three pools, six pieces, six objects, one ownership manifest, and no partial files after a duplicate-create rejection.
|
||||
2. **Default catalog:** confirm all six workcells have one loaded owned variant, `variant-1` is the selected theme family, End is terminal, and mandatory caps are initially off.
|
||||
3. **Workcell layout:** verify Blank/End Cap/Hallway then L Junction/T Junction/Cross Junction, two clear blocks between capacities, light-gray floors, red canonical glyphs, sea-lantern endpoints, and no orientation/permutation gallery.
|
||||
4. **Controls and context:** confirm every untouched workcell starts **Autosaved**. Walk outside and into End Cap; verify the Iris scoreboard context and `Triple-sneak for controls`, then open the menu and confirm End Cap is selected. Rename its workcell and active variant sticks in an anvil, apply them, verify the scoreboard shows the author names plus canonical role, then reset both labels.
|
||||
5. **Autosave:** change a solid block, a marker field, and container contents. Immediately click **Duplicate This Cell's Variant**; confirm autosave is expedited and the duplicate runs once automatically without a wait/retry instruction. Repeat with edits in multiple enabled cells and **Duplicate All Enabled Cells as Family**. Wait for the final clean state, reopen Studio, and verify all authored changes plus both clone operations round-trip.
|
||||
6. **Capacity and independent sizes:** make Hallway capacity `16×3×3` and another workcell capacity `16×8×16`; confirm no existing object byte changes and close/reopen moves only the cages. In the larger workcell, resize one variant to `16×3×16` and another to `3×3×3`; confirm exact independent dimensions, live reload of the loaded variant, and unchanged siblings. Confirm cropped authored content, connector collision, and shared/read-only objects each reject the single-variant resize without writes.
|
||||
7. **Disable:** disable Tee, confirm a full red stained-glass display fills that workcell, and confirm seed-`1337` evaluation excludes Tee pieces. Re-enable it and confirm participation returns; test export filtering separately on the portable fixture.
|
||||
8. **Dynamic preview:** confirm evaluation moves through pending/stale to valid or an understood warning, reports theme/piece count, and renders the same protected block assembly on the negative-X side after reopen. Reach it through both **Go to Preview** and `/iris jigsaw preview goto`; verify edits, fluids, pistons, explosions, growth, fire, entities, and redstone cannot alter it.
|
||||
9. **Variants and rules:** create a blank variant and duplicate one active variant; adjust one exact weight and chance; create `variant-2` through the all-enabled family action and confirm one exact-size clone per enabled workcell, duplicated memberships, and atomic active-family rebind. Change theme membership, depth/count rules, terminal status, and mandatory caps. Confirm only selected resources change and invalid rules fail atomically.
|
||||
10. **Toolbox:** take schema-`2` named sticks for selection, capacity, per-variant size, labels, duplicate-one/family, preview, Flush Autosave, themes/rules, membership changes, caps, and deletion. Confirm bindings target the named context, active/valid icons are jigsaw/emerald, lime dye only labels theme membership, destructive tools require two uses, and schema-`1` or replaced-Studio tools are rejected.
|
||||
11. **Deletion:** delete one owned inactive variant only after another remains. Add an external placement/reference and confirm project deletion is blocked; remove it, confirm deletion, and verify the complete owned closure plus manifest are removed.
|
||||
12. **Ownership protection:** have a second player attempt a direct edit, chest use, `/setblock`, `/fill`, and WorldEdit-style mutation. Confirm each is denied across the active Studio world and the owner remains able to edit.
|
||||
13. **Adoption:** apply an exclusive unowned graph in place without changing resource bytes; require a clone for a shared graph; reject a stale plan without writes; and clone a managed datapack import without changing the managed source.
|
||||
14. **Registered conversion:** convert one registered jigsaw to an unused target, review fidelity warnings/provenance, and open the owned target. A non-jigsaw source and occupied target must fail without overwrite.
|
||||
15. **Folia multi-region boundary:** use a workcell crossing chunks/regions. A fully loaded capture commits once; an unloaded intersection aborts the entire write. Automated coordinator coverage is not live Folia proof.
|
||||
16. **Natural Iris placement:** attach the graph with a unique `placementId`, generate and inspect one natural start, restart, then repeat in new chunks. For cave placement, verify each requested anchor and a no-anchor skip.
|
||||
17. **Vanilla export:** create a separate `VANILLA_PORTABLE` graph, keep all Iris-only themes/chance/rules/caps absent, export to zip, restart a clean Minecraft 26.2 world with it, locate the key, and inspect a natural instance. Do not substitute `/reload`.
|
||||
18. **Platform runtime:** copy the saved Iris pack to Fabric, Forge, and NeoForge, validate it, and prove natural shared-core assembly. Bukkit-only authoring controls are not expected on those loaders.
|
||||
|
||||
Automated tests, plugin startup, Bukkit gameplay, cross-loader generation, and vanilla datapack loading are separate evidence. Report exactly which gates ran.
|
||||
|
||||
@@ -9,6 +9,114 @@ Terminology:
|
||||
|
||||
Command listings are Bukkit/Paper; modded loaders expose a reduced set.
|
||||
|
||||
## Tutorial paths
|
||||
|
||||
Choose one path. Native placement does not require editable import, and installing a datapack does not require converting its structures into Iris objects.
|
||||
|
||||
### Path A: keep a native structure but adapt it to Iris terrain
|
||||
|
||||
Prerequisite: the structure appears in `/iris structure list <dimension>`. Use `/iris structure verify <dimension> radius=48` to confirm it reports `[native-eligible]` rather than `[disabled]` or `[unreachable]`. Merge a narrow adjustment into the declaring dimension; for example, this changes only plains villages:
|
||||
|
||||
```json
|
||||
{
|
||||
"importedStructures": {
|
||||
"adjustments": [
|
||||
{
|
||||
"match": ["minecraft:village_plains"],
|
||||
"terrain": { "mode": "VACUUM" }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. Find the registered key with `/iris structure list <dimension>`.
|
||||
2. Run `/iris structure verify <dimension> radius=48` and confirm the key is eligible before changing it.
|
||||
3. Add one `importedStructures.adjustments` entry in the dimension. Start with a narrow exact key and one operation such as `yShift`, `preserveSourceY`, or a terrain mode.
|
||||
4. Validate the pack, reopen Studio or update the test-world snapshot, and generate new chunks.
|
||||
5. Locate the key and inspect several starts. Existing chunks do not move.
|
||||
|
||||
The path passes when newly generated starts keep native blocks, entities, processors, and loot while the requested terrain operation is visible. Widen a prefix only after the exact-key test passes; a namespace or family prefix may affect many variants. If verify changes to `[disabled]`, remove the matching disable; if it is `[unreachable]`, fix the biome derivative mapping before tuning terrain.
|
||||
|
||||
### Path B: install a datapack for one Iris dimension
|
||||
|
||||
This Bukkit-family workflow keeps the datapack installed in Minecraft's global registry while scoping Iris-managed structure sets to dimensions that declare the source.
|
||||
|
||||
Prerequisites: a disposable Bukkit-family server, one declaring Iris dimension, one nondeclaring Iris dimension, and a vanilla control world. Merge the source into the declaring dimension only:
|
||||
|
||||
```json
|
||||
{
|
||||
"datapackImports": [
|
||||
"https://modrinth.com/datapack/towns-and-towers"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. Add the Modrinth or direct archive URL to `datapackImports` in the declaring dimension only.
|
||||
2. Leave the URL out of a second test dimension. Keep one vanilla world available as a control.
|
||||
3. Validate the pack, then run:
|
||||
|
||||
```text
|
||||
/iris datapack ingest restart=true
|
||||
```
|
||||
|
||||
4. After the full restart, run `/iris datapack list`, then `/iris structure list <declaring-dimension>` and choose one registered structure key from that source.
|
||||
5. Run `/iris structure verify <declaring-dimension> radius=48`. If the key is `[unreachable]`, set a compatible `vanillaDerivative` on an Iris biome before the generation test.
|
||||
6. Create fresh declaring and nondeclaring Iris worlds. In all three worlds, use `/locate structure <key>` and generate new chunks.
|
||||
7. Pass condition: the declaring Iris world locates and naturally generates the structure; the nondeclaring Iris world and vanilla world do neither. Restart without deleting the installed datapack and repeat the check.
|
||||
|
||||
If the key is absent after ingest, confirm the managed pack appears in `/iris datapack list` and that the requested restart completed; registry keys are not live on the installation boot. Removing a URL changes future per-world scope after restart; it does not delete existing chunks or generated structures. Declaring the same URL in two Iris dimensions intentionally enables the source in both.
|
||||
|
||||
### Path C: convert a registered structure for editing
|
||||
|
||||
1. Confirm the registered source generates natively first, because conversion is intentionally less faithful than Minecraft's native runtime.
|
||||
2. Back up the target pack. For one registered jigsaw graph, run:
|
||||
|
||||
```text
|
||||
/iris jigsaw convert <dimension> <namespace:path> target=auto seed=1337
|
||||
```
|
||||
|
||||
3. Conversion follows the registered start pool and reachable template-pool closure, writes a new add-only owned Iris graph, reports the imported piece/pool counts and fidelity-warning count, then opens Jigsaw Studio. `target=auto` changes a key such as `minecraft:village_plains` to `minecraft_village_plains`; use `target=<iris-path>` for a deliberate target.
|
||||
4. Load each variant from the Studio control chest or triple-sneak menu and inspect its real blocks plus Mojang marker fields. After a workcell finishes loading/hydrating, block and container changes autosave; **Save Now** is only an immediate flush. Review the automatic seed-`1337` evaluation and permanent read-only block preview before accepting fidelity. The owned copy can be reopened with `/iris jigsaw open <dimension> <target>`.
|
||||
5. For a non-jigsaw template or a bulk pass, use `/iris structure import <dimension>` instead. Review every per-structure result: successful bundles may coexist with failures.
|
||||
|
||||
`/iris jigsaw convert` accepts only a live registered jigsaw structure and refuses an occupied target. `/iris structure import` handles the broader importer passes described below. Both record source provenance and fidelity losses. A native list pool entry stays one weighted choice whose recursively first physical template and outer connectors are retained; later colocated children and their processors are omitted with a `LIST_ELEMENTS` warning instead of becoming separate alternatives. Every start-pool member remains a physical Iris piece even when it has no connectors, and an all-air template with at least one connector remains a non-collidable scaffold so its bounds can overlap attached physical pieces. Every non-start connectorless member in a pool with a distinct fallback also remains physical, regardless of pool size or air content, so weighted primary no-match attempts reach that fallback; an all-air retained member remains non-collidable. A singleton all-air connectorless member with no fallback or a self-fallback becomes an explicit empty entry with `connectorless_all_air_member_normalized_empty`; the observed waystone form uses the self-fallback case. The same member in a mixed no/self-fallback pool is omitted with `connectorless_all_air_mixed_member_omitted` because converting it to empty could terminate before later candidates; the loss records changed selection weights and RNG consumption. Other connectorless nonempty members in no/self-fallback non-start pools are omitted as unattachable with `connectorless_non_air_member_omitted`; that block loss also records exact fallback context plus selection-weight and RNG-consumption drift. Converted graphs explicitly use `branchFailurePolicy: TERMINATE_BRANCH`: after ordinary primary and direct-fallback candidates are exhausted, only that optional branch ends, while required physical fallbacks still fail. Explicit empty members and empty optional primary pools end the branch before the direct fallback. Native placement settings beyond start pool, maximum depth, and maximum distance, feature pool elements, alternate palettes, processors, entities, or other native-only behavior may not survive conversion. Use this path only when block geometry or graph topology must change; `nativeStructures` retains native processors, entities, spawners, loot, and placement behavior.
|
||||
|
||||
### Path D: put registered structures only on an Iris grid
|
||||
|
||||
Use this when the datapack should not generate from its own structure sets. First ingest and restart with the source URL as in Path B. After `/iris structure list <dimension>` confirms the keys, merge this fragment into that dimension:
|
||||
|
||||
```json
|
||||
{
|
||||
"datapackImports": [
|
||||
"https://modrinth.com/datapack/dungeons-and-taverns"
|
||||
],
|
||||
"importedStructures": {
|
||||
"disabled": ["nova_structures:"]
|
||||
},
|
||||
"structures": [
|
||||
{
|
||||
"placementId": "tutorial-native-tavern",
|
||||
"nativeStructures": [
|
||||
{ "structure": "nova_structures:tavern_oak", "weight": 1 }
|
||||
],
|
||||
"distribution": "RANDOM_SPREAD",
|
||||
"spacing": 24,
|
||||
"separation": 6,
|
||||
"salt": 776215551
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
1. Validate the pack after the restart: `/iris pack validate pack=<dimension>`.
|
||||
2. Open a fresh test world or update the world's pack snapshot and restart.
|
||||
3. Run `/iris structure verify <dimension> radius=48`; the tavern must report `[iris-planned]`, not `[disabled]`, because explicit placement bypasses the disable list.
|
||||
4. Use `/iris goto structure nova_structures:tavern_oak`, generate the planned chunk, and inspect native processors, entities, spawners, and loot.
|
||||
5. Generate several new grid cells and confirm the structure does not appear away from Iris-planned starts.
|
||||
|
||||
If validation cannot resolve the key, the datapack is not live in the registry for that dimension; return to Path B and complete the restart and scope check. If verify reports `[iris-not-found]`, increase the radius or reduce spacing for the test. Existing natural starts remain in old chunks after the namespace is disabled.
|
||||
|
||||
## 1. Vanilla structures in Iris worlds
|
||||
|
||||
### 1.1 Default: everything generates
|
||||
@@ -171,6 +279,8 @@ Dimension-file list of datapack sources Iris downloads and installs:
|
||||
}
|
||||
```
|
||||
|
||||
A `datapackImports` URL belongs to the dimension that declares it. Bukkit exposes installed resources through the server-wide registry, but before initial chunks load Iris removes disallowed managed structure sets and structure definitions from each world's generation state. Vanilla worlds and Iris worlds whose active dimension does not declare the source therefore neither generate nor locate those structures. Declaring the same URL in multiple dimensions deliberately shares its structures; if multiple managed sources claim the same key, every owner must be declared because the registry winner cannot be inferred safely.
|
||||
|
||||
Accepted URL forms:
|
||||
|
||||
- **Modrinth project page** — latest datapack version for the server's Minecraft version.
|
||||
@@ -183,7 +293,7 @@ Checksum-verified when Modrinth publishes a hash; size-capped.
|
||||
|
||||
Installed datapacks are real Minecraft datapacks at `<level root>/datapacks/<id>/`, each with `.iris-managed.json`. Unmanaged datapacks are never touched; id `iris` is reserved. Cache/staging/manifest under `plugins/Iris/datapacks/`.
|
||||
|
||||
Ingest runs shortly after plugin enable when `general.autoIngestDatapacks` is true (default). Minecraft builds worldgen registries at server start, so a **newly installed** datapack is not registered on the boot that installed it — auto-ingest **restarts the server** when anything changed. After that restart, keys are live. A repair path reinstalls staged datapacks that went missing without re-downloading.
|
||||
Ingest runs shortly after plugin enable when `general.autoIngestDatapacks` is enabled (default true). Minecraft builds worldgen registries at server start, so a **newly installed** datapack is not registered on the boot that installed it — auto-ingest **restarts the server** when anything changed. After that restart, keys are live only in the per-world structure state of declaring Iris dimensions. A repair path reinstalls staged datapacks that went missing without re-downloading.
|
||||
|
||||
### 2.3 Manual commands
|
||||
|
||||
@@ -193,7 +303,7 @@ Ingest runs shortly after plugin enable when `general.autoIngestDatapacks` is tr
|
||||
/iris datapack remove <id> (alias: rm)
|
||||
```
|
||||
|
||||
`ingest` aggregates `datapackImports` from every dimension of every loaded pack. `restart` defaults false (Iris tells you a restart is required). `remove` refuses unmanaged datapacks — also delete the URL or next ingest reinstalls it.
|
||||
`ingest` downloads each distinct URL declared by any loaded dimension while retaining the per-dimension ownership relationship used by the generation and locate state. `restart` defaults false (Iris tells you a restart is required). `remove` refuses unmanaged datapacks — also delete the URL or a later startup ingest reinstalls it. Scope changes do not delete installed datapacks, previously generated chunks, or existing structures.
|
||||
|
||||
### 2.4 Usage patterns
|
||||
|
||||
@@ -220,7 +330,7 @@ Ingest runs shortly after plugin enable when `general.autoIngestDatapacks` is tr
|
||||
}
|
||||
```
|
||||
|
||||
**(c) Manual placement only.** Disable the datapack namespace, then place specific keys with `nativeStructures` — `disabled` never blocks explicit placements (3.2):
|
||||
**(c) Manual placement only.** Disable the datapack namespace, then place specific keys with `nativeStructures` — see **`disabled` never blocks an explicit placement** below:
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -261,7 +371,7 @@ When `false`, Iris strips `data/minecraft/worldgen/structure_set|structure|templ
|
||||
| `weight` | `1` (min 1) | Weighted selection among sources. |
|
||||
| `jigsaw` | unset | Overrides for registered **jigsaw** structures only: `startPool`, `startJigsawName`, `maxDepth` (0..20), `maxDistanceHorizontal` (1..128), `maxDistanceVertical` (1..4064), `useExpansionHack`, `projectStartToHeightmap` (`SOURCE`/`NONE`/heightmap types), `dimensionPaddingBottom` and `dimensionPaddingTop` (nonnegative distance from floor/ceiling), and `liquidSettings`. Null/unset values preserve the registered definition. |
|
||||
|
||||
Placement grid fields (`distribution`, `spacing`/`separation`/`salt`, `density`, rings, heights, `underground`, `underwater`, `placementId`) match `21 - Jigsaw Structures.md` section 3.1, except the native backend supports **every** terrain mode including `VACUUM` and `ENCASE`, plus `stilt` (including `spacing`).
|
||||
Placement grid fields (`distribution`, `spacing`/`separation`/`salt`, `density`, rings, heights, `underground`, `underwater`, `placementId`) match the **Natural placement** section of `21 - Jigsaw Structures.md`, except the native backend supports **every** terrain mode including `VACUUM` and `ENCASE`, plus `stilt` (including `spacing`).
|
||||
|
||||
Scoping matches Iris placements. Validation requires the structure's effective assembly span stay inside Minecraft's 128-block (8-chunk) structure reference range.
|
||||
|
||||
@@ -323,7 +433,7 @@ A key that is both disabled and placed reports as Iris-placed.
|
||||
|
||||
## 4. Minecraft structure-block system
|
||||
|
||||
Structure blocks save/load `.nbt` templates; jigsaw blocks wire pools. Iris does not re-teach vanilla tools — see Minecraft wiki Structure Block and datapack tutorials.
|
||||
Structure blocks save/load `.nbt` templates; jigsaw blocks wire pools. Iris Jigsaw Studio is the documented in-game workflow for editable Iris graphs; use external vanilla Structure Block and datapack references only when authoring raw `.nbt` assets outside Iris.
|
||||
|
||||
How an authored `.nbt` reaches an Iris world:
|
||||
|
||||
@@ -335,22 +445,22 @@ Template-import fidelity (lossy by design): first palette only; structure voids
|
||||
|
||||
## 5. Importing native structures into Iris resources
|
||||
|
||||
You do not need import just to place — `nativeStructures` places any registered key with full fidelity. Import when you want to **edit** blocks, pools, pieces.
|
||||
You do not need import just to place — `nativeStructures` places any registered key with full fidelity. Import only when you need Iris object, pool, or piece resources. Manual imports are editable transaction-owned copies; automatic datapack imports remain managed by ingest and must be cloned before Jigsaw Studio editing.
|
||||
|
||||
### 5.1 `/iris structure import <dimension>`
|
||||
|
||||
Four passes, always overwriting its own previous output:
|
||||
|
||||
1. **Jigsaw rebuild** — registered jigsaw structures → editable pools/pieces/objects.
|
||||
1. **Jigsaw rebuild** — registered jigsaw structures → editable pools/pieces/objects. Connector `final_state`, signed `selection_priority`, and signed `placement_priority` values are retained in the Iris piece metadata, and the generated root writes `branchFailurePolicy: TERMINATE_BRANCH` so unmatched optional branches preserve native termination behavior.
|
||||
2. **Template import** — registered `.nbt` templates → `objects/<name>.iob` + single-piece `jigsaw-pieces/<name>.json`.
|
||||
3. **Template groups** — fixed multi-template structures (shipwrecks, ruined portals, ocean ruins, nether fossils) → one Iris structure each with every variant in the pool.
|
||||
4. **Capture** — code-generated structures without templates (swamp huts, igloos, ...) via scratch world (also alone as `/iris structure capture <dimension>`). Structures spanning more than **48 blocks** on any axis are skipped (strongholds, mansions, monuments stay native-only).
|
||||
4. **Capture** — only non-jigsaw registry keys for which the first pass found no same-key template are captured via a scratch world. This pass never rewrites a successful or failed jigsaw conversion. The standalone `/iris structure capture <dimension>` command remains unfiltered. Structures spanning more than **48 blocks** on any axis are skipped (strongholds, mansions, monuments stay native-only).
|
||||
|
||||
Naming: `minecraft:village_plains` → `minecraft_village_plains`. Generated structures carry `vanillaSource` for locate and `REPLACE_SOURCE`.
|
||||
|
||||
`/iris studio importvanilla <dimension> [variants=3] [structures=true]` also imports vanilla trees/features as objects, plus structure passes when `structures=true`.
|
||||
|
||||
### 5.2 Ownership and `unowned_resource`
|
||||
### 5.2 Ownership, manual editing, and `unowned_resource`
|
||||
|
||||
Imports use per-bundle ownership manifests (`<pack>/.iris/structure-manifests/`). Failure:
|
||||
|
||||
@@ -358,11 +468,22 @@ Imports use per-bundle ownership manifests (`<pack>/.iris/structure-manifests/`)
|
||||
Import conflict for '<name>': <path> is unowned_resource. Existing authored files were preserved.
|
||||
```
|
||||
|
||||
Iris found a file it did not write and refused to clobber it. `modified_resource` means Iris wrote it, you edited it, hash no longer matches. Rename or leave the key native.
|
||||
Iris found a file it did not write and refused to clobber it. `modified_resource` means Iris wrote it, you edited it, and the hash no longer matches. Rename the target, restore the exact owned bytes, or leave the key native. A successfully converted/manual-imported jigsaw can be opened directly with `/iris jigsaw open <dimension> <key>` because its ownership manifest is editable. A separate pre-existing Iris graph with no manifest uses the `adopt inspect` then `adopt apply` workflow in `21 - Jigsaw Structures.md`; no import command is required for that case.
|
||||
|
||||
### 5.3 Automatic datapack import
|
||||
|
||||
`general.autoImportDatapackStructures` (default **false**) converts each ingested datapack's structures into editable pack resources on ingest. Off by default because native generation and `nativeStructures` never need the copies, and conversion can write thousands of files. If auto-import fails (often `unowned_resource`), the manifest stays pending and import **retries every boot** until resolved or the setting is disabled. Removing a URL from `datapackImports` cleans bundles that import wrote for it.
|
||||
`general.autoImportDatapackStructures` (default **false**) converts each ingested datapack's structures into pack resources on ingest. These bundles carry `MANAGED_DATAPACK` provenance: ingest refresh owns them, and removing the source URL may clean them. Jigsaw Studio therefore displays their variants as read-only and forbids an in-place ownership claim. Inspect and apply a private clone before editing:
|
||||
|
||||
```text
|
||||
/iris jigsaw adopt inspect <dimension> <managed-iris-key> target=<editable-key> strategy=clone
|
||||
/iris jigsaw adopt apply <plan-uuid>
|
||||
```
|
||||
|
||||
Inspect verifies the existing manifest is exactly a managed vanilla/datapack Iris assembly, pins the complete source and target read set, and reports `CLONE_REQUIRED` or a blocking diagnostic. Apply re-hashes under the pack mutation lock, atomically writes a deep clone with deterministic internal reference rewrites plus its ownership receipt, leaves the managed source unchanged, and opens the editable clone. An expired, consumed, or stale plan writes nothing. There is no adoption rollback command; keep the pack backup made before conversion.
|
||||
|
||||
Automatic import is off by default because native generation and `nativeStructures` never need the copies, and conversion can write thousands of files. Deterministic source-content and graph-validation failures retain successfully written bundles and record the attempted source, importer-format, and target-pack revision, so the same failures do not repeat every boot; a source update, importer-format change, or different target retries them. Unexpected reflection, I/O, transaction, and runtime failures remain pending and retry. Removing a URL from `datapackImports` cleans only bundles still owned by that managed source; an adopted editable clone is independent.
|
||||
|
||||
Third-party jigsaw templates that use the legacy slab property `half=top|bottom` or the exact known misspelling `minecraft:chisled_polished_blackstone` are normalized to current Minecraft block data during editable conversion. Other invalid final-state values are recorded as fidelity loss and omitted without internal-error telemetry. Invalid structure graphs remain per-structure failures, but expected graph-contract rejections are reported as concise import results instead of internal Iris stack traces; unexpected reflection, I/O, and runtime failures retain full diagnostic traces.
|
||||
|
||||
## 6. Verification and debugging
|
||||
|
||||
@@ -375,6 +496,8 @@ Iris found a file it did not write and refused to clobber it. `modified_resource
|
||||
/iris goto unregistered # excluded keys + reasons
|
||||
```
|
||||
|
||||
`structure place` resolves the graph and edit resources from the named dimension pack, then stamps the assembled pieces into the player's current world. The pack's Studio and generation engine do not need to remain open for this explicit placement.
|
||||
|
||||
`verify` tags: `[iris-planned]`, `[iris-not-found]`, `[iris-search-limit]`, `[disabled]`, `[unreachable]`, `[native-eligible]`, `[error]`. Placements checked first — disabled-but-placed shows as `[iris-planned]`.
|
||||
|
||||
### Traps
|
||||
@@ -400,6 +523,9 @@ Iris found a file it did not write and refused to clobber it. `modified_resource
|
||||
| `/iris structure verify <dimension>` | `locateall` | `radius=48` (1..1000 chunks) |
|
||||
| `/iris structure info <dimension> <structure>` | | |
|
||||
| `/iris structure place <dimension> <structure>` | `p` | player only |
|
||||
| `/iris jigsaw convert <dimension> <source>` | `import`, `import-vanilla` | `target=auto seed=1337`; Bukkit player only; source is a registered jigsaw key |
|
||||
| `/iris jigsaw adopt inspect <dimension> <source>` | | `target=auto strategy=auto`; Bukkit player only; source is an existing Iris graph |
|
||||
| `/iris jigsaw adopt apply <planId>` | | Bukkit player only; no active/opening Jigsaw Studio |
|
||||
| `/iris goto structure <key>` | `/iris find structure` | |
|
||||
| `/iris goto unregistered` | | |
|
||||
| `/iris developer update-world` | | `world=<w> pack=<dim> confirm=true [fresh-download=false]` — all keyed |
|
||||
|
||||
@@ -4,6 +4,74 @@ Loot tables fill containers and entity drop inventories. Entities describe what
|
||||
|
||||
Related: `05 - Concepts & Pack Layout.md`, `11 - Dimensions.md`, `12 - Regions.md`, `13 - Biomes.md`, `19 - Objects.md`, `20 - Object Placement.md`, `03 - Configuration.md`, `10 - Studio & VSCode Schemas.md`.
|
||||
|
||||
## Tutorial: spawn one loot-bearing entity
|
||||
|
||||
This workflow adds a zombie that always rolls one iron nugget, spawns it at night in one biome, and proves each resource edge independently. Prerequisites are a validating pack, a working land biome with load key `tutorial/meadow`, and `world.ambientEntitySpawningSystem: true` in `settings.json`.
|
||||
|
||||
Create these files:
|
||||
|
||||
```
|
||||
loot/tutorial/zombie-drops.json
|
||||
entities/tutorial/zombie.json
|
||||
spawners/tutorial/night-zombies.json
|
||||
```
|
||||
|
||||
`loot/tutorial/zombie-drops.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Tutorial Zombie Drops",
|
||||
"minPicked": 1,
|
||||
"maxPicked": 1,
|
||||
"maxTries": 1,
|
||||
"loot": [
|
||||
{ "type": "iron_nugget", "rarity": 1, "minAmount": 1, "maxAmount": 1 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`entities/tutorial/zombie.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "minecraft:zombie",
|
||||
"loot": {
|
||||
"mode": "ADD",
|
||||
"tables": ["tutorial/zombie-drops"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`spawners/tutorial/night-zombies.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"group": "NORMAL",
|
||||
"maxEntitiesPerChunk": 3,
|
||||
"timeBlock": { "startHour": 20, "endHour": 5 },
|
||||
"weather": "ANY",
|
||||
"allowedLightLevels": { "min": 0, "max": 7 },
|
||||
"maximumRate": { "amount": 4, "per": { "seconds": 30 } },
|
||||
"spawns": [
|
||||
{ "entity": "tutorial/zombie", "rarity": 1, "minSpawns": 1, "maxSpawns": 2 }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Add the spawner key to the existing biome; this is a field excerpt, not a separate JSON file:
|
||||
|
||||
```json
|
||||
{
|
||||
"entitySpawners": ["tutorial/night-zombies"]
|
||||
}
|
||||
```
|
||||
|
||||
1. Validate the pack before opening the test world. The validator must resolve the spawner-to-entity edge and the entity's loot table.
|
||||
2. On Bukkit, run `/iris studio spawn tutorial/zombie` to prove the entity file loads, then kill it and confirm the nugget drop. On modded, continue directly to ambient spawning because the Studio spawn command is a stub.
|
||||
3. Open the pack in Studio, focus `tutorial/meadow`, set night, and stand in a dark surface area. Success is an Iris-spawned zombie within the configured rate and chunk cap; killing it rolls the custom table.
|
||||
4. If the entity loads manually but never spawns, check the ambient-spawning setting, time, block light, biome attachment, and living-entity cap. If validation fails, fix the first unresolved key in the chain rather than changing rarity or rate.
|
||||
5. Remove biome focus after the chain works. Add effects, markers, gear, passengers, and additional loot only after this minimal path remains repeatable.
|
||||
|
||||
## Where files live
|
||||
|
||||
| Path | Registrant | Role |
|
||||
@@ -116,6 +184,8 @@ On `IrisObjectPlacement`:
|
||||
|
||||
`IrisObjectLoot` fields: `name` (loot table key), `weight` (default 1), `filter` (block list, empty = all containers), `exact` (exact block-data match).
|
||||
|
||||
Object tile payloads, including authored container NBT, and deferred custom-block identifiers remain in Mantle until the platform post-load materialization iteration completes normally. Generic cleanup and pregeneration cleanup retain these sparse payloads for chunks that have not reached that pass; Bukkit keeps a failed region-scheduled pass retryable without repeating earlier completed passes.
|
||||
|
||||
### Real overworld loot sample
|
||||
|
||||
From `loot/global-treasure.json` (abbreviated):
|
||||
@@ -375,7 +445,7 @@ From `settings.json` → `world`:
|
||||
|
||||
Studio command `/iris studio loot` previews chest loot at the player position (see `10 - Studio & VSCode Schemas.md`, `04 - Commands & Permissions.md`).
|
||||
|
||||
## Authoring checklist
|
||||
## Reference checklist
|
||||
|
||||
1. Write `loot/<key>.json` tables; reference them from dim/region/biome `loot.tables` or object `loot[].name`.
|
||||
2. Write `entities/<key>.json` with at least `type`.
|
||||
|
||||
@@ -4,6 +4,36 @@ Snippets are active reusable JSON fragments for types annotated `@Snippet`; fiel
|
||||
|
||||
Related: `05 - Concepts & Pack Layout.md`, `10 - Studio & VSCode Schemas.md`, `11 - Dimensions.md`, `12 - Regions.md`, `13 - Biomes.md`, `14 - Generators & Noise.md`, `20 - Object Placement.md`, `25 - Pack Management.md`.
|
||||
|
||||
## Tutorial: reuse one active decorator snippet
|
||||
|
||||
Snippets are the executable reuse mechanism on this page. Start with a validating pack and a biome that already generates correctly. Save this complete decorator as `snippet/decorator/tutorial-wildflowers.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"chance": 0.08,
|
||||
"palette": [
|
||||
{ "block": "minecraft:dandelion" },
|
||||
{ "block": "minecraft:poppy" }
|
||||
],
|
||||
"slopeCondition": { "maximumSlope": 4 }
|
||||
}
|
||||
```
|
||||
|
||||
Reference it from the existing biome's `decorators` array without `.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"decorators": ["snippet/decorator/tutorial-wildflowers"]
|
||||
}
|
||||
```
|
||||
|
||||
1. Validate the pack and open it in Studio on a fixed seed.
|
||||
2. Generate new chunks in the target biome. Success is both flower types appearing only on slopes accepted by the snippet, with no missing-snippet error.
|
||||
3. If the field resolves to null, confirm the singular `snippet/` folder, the exact `decorator` type folder, and the suffix-free reference. If the snippet loads but does not place, raise `chance` temporarily and verify dimension `decorate` is true.
|
||||
4. Reuse the same string in another biome only after the first placement works. Generate the VSCode workspace so schema completion exposes valid snippet paths.
|
||||
|
||||
Do not implement this workflow with `mods/*.json`. Pack-mod files remain parseable schema data but are not applied by engine creation or Studio hotload.
|
||||
|
||||
## Pack mod schema (`IrisMod`, inactive)
|
||||
|
||||
Folder: `mods/`. The loader key is the path under `mods/` without `.json`. `IrisData` can parse and expose these registrants to schema and tooling paths, but engine creation and Studio hotload do not consume them. Treat the fields below as an inactive schema, not a supported way to modify a dimension.
|
||||
@@ -278,12 +308,15 @@ Schema registration alone does not prove a runtime consumer. The following types
|
||||
|
||||
The `mods/*.json` family is likewise schema/tooling-only as documented above.
|
||||
|
||||
### Authoring snippets
|
||||
### Tutorial: author and verify a snippet
|
||||
|
||||
1. Create `snippet/<type>/<name>.json` matching the field type shape.
|
||||
2. Reference as `"snippet/<type>/<name>"` (no `.json` suffix in the string).
|
||||
3. Prefer snippets for values reused across many biomes (decorators, styles, palettes).
|
||||
4. Open studio so VSCode schemas list available snippet paths under `.iris/schema/snippet/`.
|
||||
1. Copy one working inline value into `snippet/<type>/<name>.json`; the folder must match the field's `@Snippet` type.
|
||||
2. Replace one original value with `"snippet/<type>/<name>"` (no `.json` suffix).
|
||||
3. Validate and open Studio. Confirm schema completion lists the path and fixed-seed output matches the inline version.
|
||||
4. Replace the second duplicate only after the first call site passes.
|
||||
5. Change one value inside the snippet and confirm both call sites change on newly generated chunks, then restore the intended value.
|
||||
|
||||
Use snippets for values genuinely shared across biomes, such as decorators, styles, and palettes. A missing or wrong-type snippet resolves to null after an error, so treat validation and console output as required gates.
|
||||
|
||||
## Related commands
|
||||
|
||||
|
||||
@@ -4,6 +4,20 @@ Pack management covers download/install into the packs workspace, validation, un
|
||||
|
||||
See also: `03 - Configuration.md`, `04 - Commands & Permissions.md`, `10 - Studio & VSCode Schemas.md`, `24 - Pack Mods & Snippets.md`, `27 - Example - Configuring Overworld.md`.
|
||||
|
||||
## Tutorial: take a pack from workspace to production
|
||||
|
||||
Use this loop after a pack works in Studio and before creating or updating a production world. It produces a validated export while keeping cleanup and live-world replacement separate, reviewable decisions.
|
||||
|
||||
1. Place the authoritative authoring tree under `packs/<key>/` and confirm it contains at least one `dimensions/*.json`.
|
||||
2. Validate and read the result: Bukkit `/iris pack validate pack=<key>` then `/iris pack status pack=<key>`; modded `/iris pack validate <key>` then `/iris pack status <key>`. Continue only when the pack is loadable and every blocking error is resolved.
|
||||
3. Preview unused resources without writing: Bukkit `/iris pack cleanup <key> mode=preview`; modded `/iris pack cleanup <key>`. Review every candidate before applying cleanup.
|
||||
4. If cleanup is approved, apply it with Bukkit `mode=apply` or the modded `apply` literal, validate again, and use `pack restore` if a required resource was quarantined.
|
||||
5. Package the validated closure: Bukkit `/iris studio package dimension=<key>`; modded `/iris studio package <key>`. Success is `exports/<key>.iris` plus a completed command message; the source pack and world snapshots remain unchanged.
|
||||
6. Create a new disposable world from the release pack and run fresh-world and restart smokes. Prefer a new production world for breaking pack changes.
|
||||
7. Replace an existing `<world>/iris/pack` snapshot only after a world backup and explicit maintenance decision; use the **Developer update-world (unsafe)** procedure below.
|
||||
|
||||
If validation reports a missing edge, restore or repair that resource before packaging. If cleanup preview names an intentional dynamically loaded resource, leave cleanup unapplied. The workflow passes when the source closure validates, the package command creates the expected export, the disposable world reloads, and its world snapshot matches the intended release pack. Validate an unpacked export separately before distributing it when the release process consumes the `.iris` artifact rather than the source tree.
|
||||
|
||||
## Pack workspace
|
||||
|
||||
| Item | Path / rule |
|
||||
@@ -143,7 +157,7 @@ This is intentionally unsafe for production without backups: existing chunks kee
|
||||
| Strict content keys | `settings.general.strictContentKeys` (`03 - Configuration.md`) |
|
||||
| Datapack bootstrap / install | Server configurator + `/iris datapack` (see platform docs) |
|
||||
|
||||
## Operator checklist
|
||||
## Quick reference checklist
|
||||
|
||||
1. Download or place pack under `packs/<key>/` with `dimensions/*.json`.
|
||||
2. On Bukkit, run `/iris pack validate pack=<key>` until loadable. Modded uses `/iris pack validate <key>`.
|
||||
|
||||
@@ -4,34 +4,49 @@ This walkthrough builds a loadable pack with one dimension, one region, one biom
|
||||
|
||||
Related: `05 - Concepts & Pack Layout.md`, `02 - Getting Started.md`, `10 - Studio & VSCode Schemas.md`, `11 - Dimensions.md`, `12 - Regions.md`, `13 - Biomes.md`, `14 - Generators & Noise.md`, `25 - Pack Management.md`, `04 - Commands & Permissions.md`.
|
||||
|
||||
## Goal pack layout
|
||||
## Tutorial result
|
||||
|
||||
You will create four files under `packs/minimal/`, validate them, open them in Studio on seed `1337`, and create a disposable production world. Do not add objects, caves, structures, custom biomes, or datapacks until this exact baseline generates and reloads.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Iris is running and its default data folders exist.
|
||||
- You have operator access on Bukkit or gamemaster access on a mod loader.
|
||||
- No pack or world already uses the keys `minimal` or `minimal-test`.
|
||||
- You can inspect the server console while validation, Studio open, world create, and restart run.
|
||||
|
||||
## 1. Create the pack root
|
||||
|
||||
Create this tree relative to the platform packs root:
|
||||
|
||||
```
|
||||
packs/minimal/
|
||||
minimal/
|
||||
dimensions/minimal.json
|
||||
regions/starter.json
|
||||
biomes/starter.json
|
||||
generators/flat.json
|
||||
```
|
||||
|
||||
Pack folder name is the pack key. Dimension file name without `.json` is the dimension load key (`minimal`).
|
||||
The pack folder name is the pack key. The dimension file name without `.json` is the dimension load key (`minimal`).
|
||||
|
||||
## Create options
|
||||
### Creation methods
|
||||
|
||||
| Method | Command / action |
|
||||
|--------|------------------|
|
||||
| Studio create (code template) | `/iris studio create name=minimal` — writes starter files under `packs/` |
|
||||
| Studio create from template | `/iris studio create name=minimal template=overworld` — copies existing pack |
|
||||
| Manual | Create folders and JSON under the platform packs directory |
|
||||
| Platform / method | Command or action |
|
||||
|-------------------|-------------------|
|
||||
| Bukkit Studio starter | `/iris studio create name=minimal` |
|
||||
| Modded default-template copy | `/iris studio create minimal` — copies the `example` template |
|
||||
| Bukkit template copy | `/iris studio create name=minimal template=overworld` |
|
||||
| Modded template copy | `/iris studio create minimal overworld` |
|
||||
| Manual | Create the four folders and JSON files under the platform packs root |
|
||||
|
||||
Studio create without a template writes the starter project shown below (dimension/region/biome/generator only). After create, open studio: `/iris studio open minimal`.
|
||||
On Bukkit, Studio create without a template writes a starter project with the same four resource types. Modded Studio create defaults to the installed or downloadable `example` template. Create the tree manually when you need the exact four-file baseline on every platform; use the create commands when extra template content is acceptable.
|
||||
|
||||
Platform packs roots (same layout):
|
||||
|
||||
- Bukkit-family: `plugins/Iris/packs/`
|
||||
- Fabric / Forge / NeoForge: `config/irisworldgen/packs/`
|
||||
|
||||
## File contents
|
||||
## 2. Write the four resources
|
||||
|
||||
### `dimensions/minimal.json`
|
||||
|
||||
@@ -139,16 +154,27 @@ This matches shipping overworld `generators/flat.json` and the studio starter.
|
||||
|
||||
`StudioSVC.createStarterProject` writes the same four files with pack name substituted for the dimension file/name. It omits explicit `mode` and `fluidHeight` (code defaults: mode `OVERWORLD`, fluid height `63`). The JSON above adds those fields so authors see the required contract.
|
||||
|
||||
## Run the pack
|
||||
## 3. Validate and open Studio
|
||||
|
||||
1. Ensure the pack sits under `packs/minimal/` with `dimensions/minimal.json`.
|
||||
2. Validate: `/iris pack validate pack=minimal` (Bukkit).
|
||||
3. Create a world: `/iris create myworld type=minimal` (Bukkit) or `/iris create myworld minimal` (modded).
|
||||
4. Or open studio: `/iris studio open minimal` for hotload editing.
|
||||
1. Ensure the pack sits under `minimal/` in the platform packs root with `dimensions/minimal.json`.
|
||||
2. Validate with Bukkit `/iris pack validate pack=minimal` or modded `/iris pack validate minimal`. Do not open the pack while validation reports a blocking error.
|
||||
3. Open Studio with Bukkit `/iris studio open minimal seed=1337` or modded `/iris studio open minimal 1337`.
|
||||
4. Generate fresh chunks and run `/iris what region` and `/iris what biome`. The expected result is the `starter` region and biome over a uniform grass surface, with no missing-resource or parse errors in the console.
|
||||
5. Close Studio, reopen it with the same seed, and generate another new area. The terrain height and surface must reproduce.
|
||||
|
||||
World create copies the pack into the world folder at `iris/pack/` (see `06 - Worlds & Lifecycle.md`). Studio worlds hotload the live pack under `packs/` — prefer studio for authoring.
|
||||
|
||||
## Extend without breaking the minimal set
|
||||
## 4. Create and restart-test a disposable world
|
||||
|
||||
1. Create the world with Bukkit `/iris create minimal-test type=minimal seed=1337` or modded `/iris create minimal-test minimal 1337`. On Folia, creation stages the world and requires the instructed server restart before it can be entered.
|
||||
2. Teleport with Bukkit `/iris tp minimal-test` or modded `/iris tp irisworldgen:minimal-test`.
|
||||
3. Generate ordinary new chunks and confirm the same flat grass result seen in Studio.
|
||||
4. Stop the server cleanly, start it again, teleport back, and generate another new area.
|
||||
5. Confirm `<world>/iris/pack/` contains the four-file snapshot. Production generation reads this copy, so later authoring changes under `packs/minimal/` do not change the existing world automatically.
|
||||
|
||||
The tutorial passes only when validation, Studio reopen, production create, teleport, and server restart all succeed. Keep this four-file version as a rollback checkpoint before extending the pack.
|
||||
|
||||
## 5. Extend without breaking the minimal set
|
||||
|
||||
| Add | Where |
|
||||
|-----|-------|
|
||||
@@ -159,7 +185,19 @@ World create copies the pack into the world folder at `iris/pack/` (see `06 - Wo
|
||||
| Objects | Biome/region `objects` placements + `objects/*.iob` (`19 - Objects.md`, `20 - Object Placement.md`) |
|
||||
| Entity spawn | `entities/`, `spawners/`, then `entitySpawners` on dim/region/biome |
|
||||
|
||||
## Validation notes
|
||||
## Troubleshooting and recovery
|
||||
|
||||
| Symptom | Check / recovery |
|
||||
|---------|------------------|
|
||||
| Pack is not listed | Confirm the platform packs root, `minimal/` folder, and `dimensions/minimal.json` |
|
||||
| Validation reports a missing region | `dimensions/minimal.json` must reference `starter`, and `regions/starter.json` must exist |
|
||||
| Validation reports a missing biome | Every region list entry must match a file under `biomes/` without `.json` |
|
||||
| Terrain is empty or at the wrong height | Confirm the biome generator key is `flat`, the generator parses, and biome `min` / `max` remain `96` |
|
||||
| Studio shows old terrain | Generate untouched chunks; close and reopen Studio after contract changes |
|
||||
| Production world ignores edits | It uses `<world>/iris/pack/`; create a new world or follow the backed-up update procedure in `25 - Pack Management.md` |
|
||||
| Baseline no longer works | Restore the four exact files in this guide and validate before reintroducing extensions |
|
||||
|
||||
Validation invariants:
|
||||
|
||||
- Dimension load key must match a file under `dimensions/`.
|
||||
- Every region key in `regions` must load.
|
||||
|
||||
@@ -4,6 +4,95 @@ The shipping overworld pack is the default Iris dimension pack. This guide shows
|
||||
|
||||
Related: `05 - Concepts & Pack Layout.md`, `06 - Worlds & Lifecycle.md`, `10 - Studio & VSCode Schemas.md`, `11 - Dimensions.md`, `12 - Regions.md`, `13 - Biomes.md`, `14 - Generators & Noise.md`, `23 - Loot, Entities, Spawners, Markers.md`, `24 - Pack Mods & Snippets.md`, `25 - Pack Management.md`, `04 - Commands & Permissions.md`, `02 - Getting Started.md`.
|
||||
|
||||
## Tutorial result
|
||||
|
||||
Fork the shipping pack, add one visible biome, prove it in Studio and a disposable world, and leave the original `overworld` pack untouched. This is the recommended first Overworld customization because it exercises references, hotload, snapshots, and rollback without changing dimension height or native registries.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- The `overworld` pack is installed and validates.
|
||||
- You have operator access on Bukkit or gamemaster access on a mod loader.
|
||||
- The keys `my-overworld`, `overworld-test`, and `tutorial/meadow` are unused.
|
||||
- You can keep the fork in source control or make a filesystem backup before production use.
|
||||
|
||||
## End-to-end tutorial: add a temperate meadow
|
||||
|
||||
### 1. Fork and open the pack
|
||||
|
||||
Create the fork with Bukkit `/iris studio create name=my-overworld template=overworld` or modded `/iris studio create my-overworld overworld`. Wait for the command to report the completed project path; pack creation runs asynchronously.
|
||||
|
||||
Validate with Bukkit `/iris pack validate pack=my-overworld` or modded `/iris pack validate my-overworld`. Then open Studio with Bukkit `/iris studio open my-overworld seed=1337` or modded `/iris studio open my-overworld 1337`.
|
||||
|
||||
### 2. Add the biome file
|
||||
|
||||
Save this complete biome as `plugins/Iris/packs/my-overworld/biomes/tutorial/meadow.json` on Bukkit or `config/irisworldgen/packs/my-overworld/biomes/tutorial/meadow.json` on a mod loader:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Tutorial Meadow",
|
||||
"rarity": 1,
|
||||
"derivative": "minecraft:plains",
|
||||
"vanillaDerivative": "minecraft:plains",
|
||||
"layers": [
|
||||
{
|
||||
"minHeight": 1,
|
||||
"maxHeight": 1,
|
||||
"palette": [{ "block": "minecraft:grass_block" }]
|
||||
},
|
||||
{
|
||||
"minHeight": 3,
|
||||
"maxHeight": 3,
|
||||
"palette": [{ "block": "minecraft:dirt" }]
|
||||
}
|
||||
],
|
||||
"generators": [
|
||||
{ "generator": "plain", "min": 18, "max": 24 }
|
||||
],
|
||||
"decorators": ["snippet/decorator/wildflowers"]
|
||||
}
|
||||
```
|
||||
|
||||
The fork already contains `generators/plain.json` and `snippet/decorator/wildflowers.json`. Do not copy this biome into the original `overworld` folder.
|
||||
|
||||
### 3. Attach and focus the biome
|
||||
|
||||
Append `"tutorial/meadow"` to `landBiomes` in `regions/temperate.json`. In `dimensions/my-overworld.json`, temporarily add:
|
||||
|
||||
```json
|
||||
{
|
||||
"focusRegion": "temperate",
|
||||
"focus": "tutorial/meadow"
|
||||
}
|
||||
```
|
||||
|
||||
These are field excerpts: merge them into the existing region and dimension objects instead of replacing either file. Validate again after both edits.
|
||||
|
||||
### 4. Prove the authoring result
|
||||
|
||||
Generate untouched Studio chunks and run `/iris what region` and `/iris what biome`. Success is the `temperate` region, the `tutorial/meadow` biome, a grass-over-dirt surface, visibly higher rolling terrain than shipping plains, and wildflower decoration with no missing-key errors.
|
||||
|
||||
If validation cannot resolve the biome, compare `tutorial/meadow` against the file path and region entry character-for-character. If terrain is empty, confirm `generators/plain.json` still exists in the fork. If flowers are missing, confirm `snippet/decorator/wildflowers.json` exists; remove the decorator reference until the terrain baseline passes.
|
||||
|
||||
### 5. Prove natural selection and restart behavior
|
||||
|
||||
Remove `focus` and `focusRegion`, close Studio, and reopen with seed `1337`. On Bukkit, `/iris find biome tutorial/meadow` can locate the naturally selected biome after it appears; the same command is available on modded.
|
||||
|
||||
Create a disposable world with Bukkit `/iris create overworld-test type=my-overworld seed=1337` or modded `/iris create overworld-test my-overworld 1337`. Teleport with Bukkit `/iris tp overworld-test` or modded `/iris tp irisworldgen:overworld-test`, generate new chunks, stop cleanly, restart, and verify another new area. On Folia, honor the required restart immediately after the create command before teleporting.
|
||||
|
||||
The tutorial passes when validation is loadable, focused and natural selection both work, the disposable world contains `<world>/iris/pack/`, and the world reloads without pack or registry errors.
|
||||
|
||||
### 6. Package or recover
|
||||
|
||||
Package with Bukkit `/iris studio package dimension=my-overworld` or modded `/iris studio package my-overworld`. Keep the validated fork as the source of truth; the `.iris` export and world snapshot are outputs.
|
||||
|
||||
| Failure | Recovery |
|
||||
|---------|----------|
|
||||
| Fork creation fails or is partial | Move only the newly created incomplete `my-overworld` folder aside, then rerun after confirming the source pack validates |
|
||||
| Studio still shows old content | Generate untouched chunks; close and reopen Studio after dimension-contract or registry changes |
|
||||
| Natural selection cannot find the biome | Confirm it remains in `regions/temperate.json`, remove focus fields, and sample a broader new area |
|
||||
| Disposable world differs from Studio | Inspect `<world>/iris/pack/`; recreate the disposable world from the current validated fork |
|
||||
| Production update would change height, registries, or large terrain systems | Do not update in place; create a new world and migrate intentionally |
|
||||
|
||||
## Pack locations
|
||||
|
||||
| Platform | Authoritative packs root |
|
||||
@@ -44,7 +133,7 @@ Dimension load key is `overworld` (`dimensions/overworld.json`).
|
||||
|
||||
From `dimensions/overworld.json` (selected fields):
|
||||
|
||||
| Field | Shipping value (probe) |
|
||||
| Field | Shipping value |
|
||||
|-------|------------------------|
|
||||
| `name` | `"Overworld"` |
|
||||
| `version` | `4000` |
|
||||
@@ -86,13 +175,15 @@ Generator referenced by that biome: `generators/plain.json` (composite IRIS_DOUB
|
||||
### Prefer studio for authoring
|
||||
|
||||
1. Ensure overworld exists under `packs/overworld/`.
|
||||
2. Open studio: `/iris studio open overworld` (optional seed).
|
||||
3. Edit files under `packs/overworld/` with VSCode workspace / schemas (`10 - Studio & VSCode Schemas.md`).
|
||||
4. Hotload picks up JSON changes in the studio world. Regenerate or move to see new terrain.
|
||||
5. Use focus fields on the dimension for isolation:
|
||||
2. Fork it: `/iris studio create name=my-overworld template=overworld`.
|
||||
3. Open Studio: `/iris studio open my-overworld seed=1337`.
|
||||
4. Edit the fork under `packs/my-overworld/` with the generated VSCode workspace and schemas (`10 - Studio & VSCode Schemas.md`).
|
||||
5. Hotload picks up JSON changes in the Studio world. Generate new chunks to see terrain changes.
|
||||
6. Use focus fields on the forked dimension for isolation:
|
||||
- `"focus": "temperate/plains"` — only that biome
|
||||
- `"focusRegion": "temperate"` — only that region
|
||||
6. Close studio when finished: `/iris studio close`.
|
||||
7. Change `biomes/temperate/plains.json` generator `min` or `max` by a small amount, validate, and compare the same seed in fresh chunks.
|
||||
8. Restore/remove focus, close Studio, create a disposable world from `my-overworld`, and restart-test it.
|
||||
|
||||
Studio is the live pack. Production worlds still run on their `iris/pack` snapshot until updated.
|
||||
|
||||
|
||||
@@ -2,6 +2,28 @@
|
||||
|
||||
Iris integrates with selected Bukkit plugins for world management, selections, external blocks/items/entities, Mythic skill conditions, PlaceholderAPI, and tree felling. Soft-depends declare load order only; Iris still checks `isPluginEnabled` / readiness before use. Integrations are Bukkit-family unless noted. See also `04 - Commands & Permissions.md`, `06 - Worlds & Lifecycle.md`, `09 - PlaceholderAPI.md`, `19 - Objects.md`, and `93 - API - Tree Feller.md`.
|
||||
|
||||
## Tutorial: prove an integration boundary
|
||||
|
||||
Choose one boundary and one observable result:
|
||||
|
||||
| Boundary | Positive proof | Negative control |
|
||||
|---|---|---|
|
||||
| WorldEdit | Make a cuboid selection, run `/iris object we`, and save a disposable object | Clear the selection; Iris must report that no area is selected |
|
||||
| Multiverse-Core | Create or update a disposable Iris world and confirm generator `Iris:<pack>` in Multiverse | In a separate disposable test copy, restart without Multiverse installed; Iris must skip the link without failing its own world lifecycle |
|
||||
| External item/block/entity provider | Validate one exact namespaced key and generate one consumer in a new chunk | An invalid key logs and resolves empty without crashing generation |
|
||||
| MythicMobs conditions | `irisbiome` or `irisregion` passes inside the named Iris resource | The same condition returns false outside Iris or with engine access unavailable |
|
||||
| PlaceholderAPI | Complete the direct parse sequence in `09 - PlaceholderAPI.md` | A player outside Iris receives the documented unavailable values |
|
||||
| Tree feller | A sneaking survival player with permission and an axe fells a provenanced Iris tree | A player-planted or hand-built tree remains intact |
|
||||
|
||||
1. Start from a server where Iris alone passes its fresh-world smoke.
|
||||
2. Install one integration and its required dependencies; perform a full restart rather than a plugin reload.
|
||||
3. Confirm enable order and that both plugins report ready without a linkage or missing-class exception.
|
||||
4. Exercise the smallest read-only path listed in that integration section, then one controlled mutation such as a selection conversion, external item resolution, or managed-world import.
|
||||
5. Restart and repeat the same path. Test one unavailable/invalid external key so failure behavior is also known.
|
||||
6. Add the next integration only after the current boundary passes.
|
||||
|
||||
Soft-depend presence is not proof that an external provider is ready. Diagnose integration failures with both plugins' versions and startup order before changing an Iris pack.
|
||||
|
||||
## Soft-depends and load order (`plugin.yml`)
|
||||
|
||||
| Plugin | Relation | Role |
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
The Iris client mod (Fabric/Forge/NeoForge jar on the client) adds a native pregeneration HUD, Vision map, What overlay, studio toasts, and singleplayer world-type entries. It talks to Iris servers over the shared channel `irisworldgen:main`. Vanilla clients ignore the channel and use server-side fallbacks. See also `07 - Pregeneration.md`, `08 - Localization.md`, `10 - Studio & VSCode Schemas.md`, and `30 - Platform Differences.md`.
|
||||
|
||||
## Tutorial: verify the client/server path
|
||||
|
||||
1. Join the same Iris server once with a vanilla client and once with a matching Iris client mod.
|
||||
2. Start a small pregen. On a modded server, confirm the vanilla client receives the boss-bar fallback. On Bukkit-family servers, confirm progress through console/status or the configured Bukkit HUD path. In both cases, the client with Iris must receive the native HUD.
|
||||
3. On the modded client, toggle the HUD, open Vision, and toggle What using rebound keys if defaults conflict.
|
||||
4. Move between an Iris world and a non-Iris world. Confirm Vision/What report Iris data only where the handshake and world state allow it.
|
||||
5. Reconnect and repeat one action to prove the handshake is not relying on stale client state.
|
||||
6. Check the server log for payload decode, version, or channel errors.
|
||||
|
||||
The protocol smoke passes only when both client types follow the server-family behavior in the matrix below. A working boss bar or Bukkit status path proves server progress, not the Iris client payload path. If the native HUD remains absent, verify matching Iris/Minecraft versions, reconnect to force a new handshake, and check the server log for an unsupported protocol version or rejected capability frame.
|
||||
|
||||
## When the client mod does something
|
||||
|
||||
| Server | Client without Iris | Client with Iris mod |
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
Iris runs the same generation core on Bukkit-family servers and on Fabric, Forge, and NeoForge. Adapters differ in world lifecycle, command surface, permissions, datapacks, and optional tools. Shared config is `settings.json`; mod loaders add `modded.json`. See `01 - Installation & Platforms.md`, `03 - Configuration.md`, and `04 - Commands & Permissions.md`.
|
||||
|
||||
## Tutorial: move a pack between platform families
|
||||
|
||||
1. Freeze the source pack bytes and seed. Validate and package it on the source platform.
|
||||
2. Install the correct destination artifact and copy only the pack into the destination packs root; do not copy a Bukkit world folder into a modded world or vice versa.
|
||||
3. Restart so destination registries and forced datapacks are built before world creation.
|
||||
4. Validate the pack on the destination, then create a disposable world with the same seed.
|
||||
5. Run the same small GoldenHash inputs and the platform-specific fresh-world smoke.
|
||||
6. Exercise features marked partial or unavailable in the matrix below with explicit alternatives rather than assuming command parity.
|
||||
|
||||
The move passes when pack validation, world creation, restart, and deterministic comparison pass. Matching screenshots are useful visual evidence but do not replace GoldenHash or lifecycle checks.
|
||||
|
||||
## Artifacts and entry points
|
||||
|
||||
| Surface | Artifact | Bootstrap |
|
||||
@@ -35,7 +46,7 @@ Hotload: Bukkit file-watch engine; modded 3s poll. Same invalidate/reload/locale
|
||||
| Remove / delete | `/iris remove` optional folder delete | `/iris world delete` wipes chunk/mantle data |
|
||||
| Primary / main world | create `main=true` and Bukkit yml registration paths | `modded.json` primary + `routePlayersToPrimaryWorld`; `/iris world mainworld`, `replace-overworld` |
|
||||
| Evacuate | `/iris evacuate <world>` | `/iris evacuate [dimension]` → primary/overworld fallback |
|
||||
| Studio world | Transient studio world via StudioSVC | Studio dimension under `irisworldgen:studio_*` |
|
||||
| Studio world | Transient studio world via StudioSVC; `/iris jigsaw` can select the Jigsaw Studio generator for one activation | Studio dimension under `irisworldgen:studio_*`; no Jigsaw Studio authoring command tree |
|
||||
| Folia | Regionized schedulers; pregen `runtimeSchedulerMode` forces `FOLIA` when regionized | N/A (not Bukkit Folia) |
|
||||
|
||||
Default pack bootstrap still downloads the IrisDimensions overworld release into `packs/overworld` when missing (shared provisioner).
|
||||
@@ -55,11 +66,15 @@ Modded startup quarantines a corrupt persistent-dimension registry as `iris-dime
|
||||
|
||||
Full command tables and stubs: `04 - Commands & Permissions.md`.
|
||||
|
||||
Jigsaw pack resources are shared runtime data, but in-game Jigsaw Studio is not a shared command surface. Bukkit exposes one global Studio project/world and one owning Jigsaw command session. Non-owner block, inventory, interaction, and mutating-command changes are cancelled across that Studio world, while autosave and graph-operation barriers serialize the owner's changes. On Folia, a save schedules every intersecting chunk snapshot on its owning region and writes only after the complete capture validates; these protections and the coordinator have automated coverage but still require the live multi-region smoke in `31 - Operator Runbooks & Smoke Tests.md`. Then copy the saved pack to mod loaders for validation and generation. A strict `VANILLA_PORTABLE` export targets unmodded Minecraft 26.2 and is a separate compatibility gate.
|
||||
|
||||
## Feature matrix
|
||||
|
||||
| Feature | Bukkit | Fabric | Forge | NeoForge |
|
||||
|---------|--------|--------|-------|----------|
|
||||
| Core terrain / biomes / objects / jigsaw | yes | yes | yes | yes |
|
||||
| Jigsaw Studio create/grid/marker capture/rules/export | yes | no | no | no |
|
||||
| Saved planar/spatial Iris jigsaw runtime | yes | yes | yes | yes |
|
||||
| Pack validate / cleanup / download | yes | yes | yes | yes |
|
||||
| Pregen | yes (Paper-like / Folia modes) | yes (`moddedPregenInFlight`) | yes | yes |
|
||||
| Studio open/close/vscode/package | yes | yes | yes | yes |
|
||||
@@ -123,9 +138,10 @@ Goldenhash and genhash exist on both surfaces (command placement differs: Bukkit
|
||||
|
||||
1. Copy `packs/<key>/` between data folders.
|
||||
2. Structure/vanilla imports that need Bukkit: run import on Bukkit, then copy the pack to the mod server.
|
||||
3. Align `settings.json` keys that matter for generation (`generator`, `performance`, `treeFeller`); ignore Bukkit-only autoConfiguration if unused.
|
||||
4. On modded, set `modded.json` primary/main-world if you need overworld replacement.
|
||||
5. Re-run `/iris pack validate` and `/iris datapack status` (modded) or ingest (Bukkit) after moves.
|
||||
3. Jigsaw Studio projects: finish atomic saves and pack validation on Bukkit, then copy the saved pack; do not expect `/iris jigsaw` on mod loaders.
|
||||
4. Align `settings.json` keys that matter for generation (`generator`, `performance`, `treeFeller`); ignore Bukkit-only autoConfiguration if unused.
|
||||
5. On modded, set `modded.json` primary/main-world if you need overworld replacement.
|
||||
6. Re-run `/iris pack validate` and `/iris datapack status` (modded) or ingest (Bukkit) after moves.
|
||||
|
||||
## Related
|
||||
|
||||
@@ -134,6 +150,7 @@ Goldenhash and genhash exist on both surfaces (command placement differs: Bukkit
|
||||
- `04 - Commands & Permissions.md`
|
||||
- `06 - Worlds & Lifecycle.md`
|
||||
- `07 - Pregeneration.md`
|
||||
- `21 - Jigsaw Structures.md`
|
||||
- `22 - Native Structures & Datapacks.md`
|
||||
- `28 - Integrations.md`
|
||||
- `29 - Client HUD & Protocol.md`
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
Manual verification sequences for operators and maintainers after install, upgrade, pack change, or release candidate build. Each runbook ends when the stated gate passes. Full command trees and permissions live in `04 - Commands & Permissions.md`; pregen options in `07 - Pregeneration.md`; platform differences in `30 - Platform Differences.md`.
|
||||
|
||||
## How to run these tutorials
|
||||
|
||||
Use a purpose-named disposable world and record the exact Iris artifact, platform build, Java version, pack hash, seed, and commands before starting. Run only the sections affected by a local authoring change; run the full platform set for a release candidate.
|
||||
|
||||
Do not merge different proof types. A Gradle test is automated evidence, a successful server boot is startup evidence, and a player moving through generated chunks is gameplay evidence. Record each gate separately and clean up only instances/worlds created for the smoke.
|
||||
|
||||
## Fixed inputs for parity smoke
|
||||
|
||||
Use the same inputs whenever comparing platforms or runs:
|
||||
@@ -57,7 +63,16 @@ Or a single pack: `/iris pack validate pack=<pack>` on Bukkit, `/iris pack valid
|
||||
3. Optional: `/iris pack status` replays the last recorded validation result for the session.
|
||||
4. Gate: target pack is loadable; no unexpected blocking errors on the shipping default pack. Cleanup/restore flows are separate and opt-in (`25 - Pack Management.md`).
|
||||
|
||||
## D. Pregeneration control smoke
|
||||
## D. Bukkit datapack dimension-scope smoke
|
||||
|
||||
Use a disposable server with one managed datapack source, a vanilla world, one Iris dimension declaring that source, and one Iris dimension that does not declare it. Install or ingest the datapack, restart so its registries are live, then create both Iris worlds so the scope is applied before their initial spawn chunks load.
|
||||
|
||||
1. In each world, run `/locate structure <managed-structure-key>` using a key listed by `/iris structure list <declaring-dimension>`.
|
||||
2. Generate new chunks in all three worlds; do not use existing chunks as proof because scope changes do not rewrite them.
|
||||
3. Gate: locate and natural generation retain the managed structure in the declaring Iris world, while the vanilla and nondeclaring Iris worlds neither locate nor generate it.
|
||||
4. Restart without removing the installed datapack and repeat locate plus new-chunk generation. Gate: the same per-world result remains and no ownership or structure-state failure appears during world initialization.
|
||||
|
||||
## E. Pregeneration control smoke
|
||||
|
||||
Radius is always in **blocks**. Prefer a disposable test world.
|
||||
|
||||
@@ -97,7 +112,7 @@ Gates:
|
||||
|
||||
Client HUD: with the Iris client mod, pregen progress arrives on channel `irisworldgen:main`; vanilla clients use boss bar / console only (`29 - Client HUD & Protocol.md`).
|
||||
|
||||
## E. GoldenHash determinism smoke
|
||||
## F. GoldenHash determinism smoke
|
||||
|
||||
Run on a **disposable** Iris world. GoldenHash generates into buffers (does not write world blocks) but **resets mantle** by default — treat the world as expendable.
|
||||
|
||||
@@ -124,24 +139,131 @@ Gates:
|
||||
- Second run with the same pack/seed/radius/center reports **MATCH** and the same combined hash.
|
||||
- The same pack+seed+radius+center hash matches across Bukkit, Fabric, Forge, and NeoForge when comparing identical artifacts and pack bytes. Cross-platform rule: `32 - Determinism & Goldenhash.md`.
|
||||
|
||||
## F. Restart and existing-world smoke
|
||||
## G. Restart and existing-world smoke
|
||||
|
||||
1. After some pregen or free exploration, stop the server cleanly.
|
||||
2. Start again without deleting world data.
|
||||
3. Load the same Iris world; generate new chunks outside the pregenerated area.
|
||||
4. Gate: world loads; new chunks generate; no blank-chunk regression on restart; pregen cache resume behaves as documented when a job is resumed (`07 - Pregeneration.md`).
|
||||
|
||||
## G. Studio smoke (authoring path)
|
||||
## H. Studio smoke (authoring path)
|
||||
|
||||
### General pack Studio
|
||||
|
||||
```
|
||||
/iris studio open overworld seed=1337
|
||||
```
|
||||
|
||||
Edit a pack file on disk (or via the VSCode workspace from `/iris studio vscode dimension=overworld` on Bukkit). Confirm hotload applies without server restart when supported. Close with `/iris studio close` (studio worlds are transient and discarded).
|
||||
Edit a pack file on disk (or via the VSCode workspace from `/iris studio vscode dimension=overworld` on Bukkit) while moving through fresh chunks so Moonrise has active generation stages. Confirm hotload applies without server restart, already-admitted stages finish before the transition, and later stages resume afterward. Close with `/iris studio close` while fresh chunks are still queued (studio worlds are transient and discarded).
|
||||
|
||||
Gate: studio world opens; hotload either applies successfully or fails closed without poisoning the live engine for non-studio worlds. Studio details: `10 - Studio & VSCode Schemas.md`.
|
||||
Gate: studio world opens; hotload and close do not produce a generation-session rejection, partial chunk-stage failure, or chunk-system crash; hotload either applies successfully or fails closed without poisoning the live engine for non-studio worlds. Studio details: `10 - Studio & VSCode Schemas.md`.
|
||||
|
||||
## H. Offline probe module (no live server)
|
||||
### Jigsaw Studio: planar authoring and atomicity (Bukkit)
|
||||
|
||||
Use a disposable pack/structure key and the owning builder account. Bukkit has one global Studio project/world and one owning Jigsaw session. Non-owner block edits and recognized mutating commands must be denied throughout this world. This command tree is not registered on Fabric, Forge, or NeoForge.
|
||||
|
||||
1. Create a project without optional arguments so the defaults are exercised:
|
||||
|
||||
```text
|
||||
/iris jigsaw create overworld smoke/jigsaw
|
||||
/iris jigsaw status
|
||||
```
|
||||
|
||||
Gate: the add-only transaction owns one structure, three pools, six pieces, six objects, and one manifest before Studio opens. The player enters creative above Blank. `status` reports `PLANAR_JIGSAW`, `IRIS_EXTENDED`, six workcells, 16×16×16 for the selected workcell, six variants, no pending autosave, and the seed-`1337` evaluation. The GUI and owned resources show one loaded variant per archetype, theme `variant-1`, terminal End, and mandatory caps off.
|
||||
|
||||
2. Inspect the exact Blank, End Cap, Hallway, L Junction, T Junction, Cross Junction layout. Floors are light-gray wool, topology paths are red wool, and canonical endpoints are sea lanterns. There are no orientation, permutation, piece, or derived-rotation cells. Toggle player-local particles:
|
||||
|
||||
```text
|
||||
/iris jigsaw goto workcell/blank
|
||||
/iris jigsaw goto workcell/straight
|
||||
/iris jigsaw goto workcell/cross
|
||||
/iris jigsaw particles false
|
||||
/iris jigsaw particles true
|
||||
```
|
||||
|
||||
Gate: the occupied valid cell is aqua, nearby valid bounds are dark gray, and an invalid cell is red. Focused connectors draw 1.75-block direction lines. The Iris scoreboard replaces the general Studio context with Structure, Workcell, Variant, State, and `Triple-sneak for controls`, without orientation/mask fields. All six untouched cells initially report **Autosaved**. Enter End Cap, triple-sneak, and confirm the menu selects End Cap rather than the previously selected cell.
|
||||
|
||||
3. Open the same six-row controls three ways: right-click the protected chest, run `/iris jigsaw menu`, and start three sneaks within 1.5 seconds. Select Hallway and click **New Blank Variant**. Wait for its atomic graph result and load, then reopen the controls. Rename the loaded variant and Hallway workcell through their anvil inputs; confirm labels round-trip while the piece key, `straight` stable ID, and solver role stay unchanged. Load End Cap and use **Duplicate This Cell's Variant**, then load Cross Junction and duplicate it as well.
|
||||
|
||||
Gate: the new key follows `smoke/jigsaw/variants/straight/variant-<n>` and loads into Hallway. It has the source piece's complete metadata and exact pool entries but an empty same-sized object. At the default 16×16×16, its two real markers occupy `(8,8,0)` and `(8,8,15)`, face north/south with top `UP_POSITIVE_Y`, show pool `iris:smoke/jigsaw/pieces`, use name/target `iris:planar`, `ALIGNED`, `minecraft:structure_void`, and signed priorities `0`. Mojang's UI is usable after hydration. Each duplicate copies the active object's bytes, display label, and complete piece metadata. The End Cap duplicate has exact matching entries in both `smoke/jigsaw/pieces` and `smoke/jigsaw/caps`; the Cross Junction duplicate has exact matching entries in both `smoke/jigsaw/start` and `smoke/jigsaw/pieces`. An empty or unassigned workcell refuses both GUI actions and directs the operator to `/iris jigsaw piece create <poolKey> <pieceKey>` instead of choosing a fallback pool.
|
||||
|
||||
4. Change one permanent block, one marker field, and one chest inventory inside Hallway. Keep the permanent block and chest within the later 16×3×3 target, such as Y/Z offsets `1,1`. After changing a marker field in Mojang's UI, immediately run `/iris jigsaw status`; change it again and immediately run `/iris jigsaw close`. Also trigger internal inventory transfer or hopper pickup and at least one furnace, brewing-stand, dispenser, or crafter update inside the workcell. Do not flush autosave. Wait at least 40 ticks after the final update, then inspect status:
|
||||
|
||||
```text
|
||||
/iris jigsaw status
|
||||
```
|
||||
|
||||
Gates: the command and close attempt request a final owning-region marker snapshot; close waits behind marker finalization and autosave instead of losing the last UI change. State moves through dirty/saving to clean automatically; the inventory and machine changes also mark it dirty; one complete multi-resource commit occurs; and no partial resource appears. Make another edit while capture is pending, immediately click **Duplicate This Cell's Variant**, and confirm Iris expedites autosave then performs that one duplicate exactly once without a wait/retry instruction. Repeat with dirty edits in multiple enabled cells and **Duplicate All Enabled Cells as Family**. Invoke **Flush Autosave Now** while capture cannot start and confirm the same ticket remains pending, retries, and eventually becomes clean. Close/reopen, load the variant, and confirm block, marker NBT, inventory, explicit-air final state when used, and `structure_void` absence round-trip. **Flush Autosave Now** and `/iris jigsaw save` are not required.
|
||||
|
||||
On Paper, repeat one dirty edit immediately before plugin disable and confirm the synchronous final drain persists it. On Folia, verify an enabled-world unload or unregister remains deferred and retries until autosave finishes. Record the forced-disable boundary separately: once Folia has disabled the plugin it rejects new region tasks, so a new final cross-region capture cannot be guaranteed. Close Studio or wait for `status` to report no pending autosave before reload or server shutdown.
|
||||
|
||||
5. Change Hallway's capacity to 16×3×3 from **Workcell Settings** or `/iris jigsaw bounds 16 3 3`. Gate: only structure capacity metadata changes; every Hallway variant keeps its object bytes and exact dimensions. A capacity shrink below any assigned variant is rejected atomically. Resize one loaded Hallway variant to 16×3×3 from **Variant Size** or `/iris jigsaw variant resize 16 3 3`; confirm only that object changes and reloads in place, its canonical connector payloads and sockets move to `(8,1,0)` and `(8,1,2)`, and sibling Hallway variants keep their prior dimensions and bytes. Resize a second Hallway variant to 3×3×3 after raising capacity if required, proving variants in one cell can differ. Before one shrink, persist a block outside the target; confirm the resize is rejected without an owned-file change, then remove it and retry. Also confirm a shared or read-only object is rejected. **Resize This Variant to Capacity** affects only the selected variant.
|
||||
|
||||
6. Open the loaded variant's details. Change one exact pool entry's weight and chance, use **Duplicate This Cell's Variant**, toggle rotation, and use the two-click unlink confirmation. Gate: only that entry changes; chance moves in five-percentage-point steps; the duplicate has a new key, copied label, and independent object; and every stale callback is rejected by request ID.
|
||||
|
||||
7. Use **Duplicate All Enabled Cells as Family** to create `variant-2`. Gate: one owned clone is created from the active variant of every enabled workcell, matching pool memberships, labels, and independent object dimensions are duplicated, every clone is atomically loaded and assigned to `variant-2`, and a failure leaves both files and all live bindings unchanged. Seed `1337` selects one complete weighted theme without mixing families. Change a loaded piece's depth/count/terminal rules, theme membership, theme weight, and mandatory caps. Invalid combinations must fail atomically and appear in the automatic evaluation without a manual validation command.
|
||||
|
||||
8. Disable Tee. Gate: a red stained-glass display fills its full bounds, the workcell remains editable, and Tee pieces disappear from assembly. Unload and reload the display's origin chunk and confirm the full-volume red display returns once without a stale duplicate. The permanent seed-`1337` preview on the negative-X side updates in place and is protected from players, fluids, pistons, explosions, growth, fire, entities, and redstone; the GUI, scoreboard, or `status` shows its selected theme and piece count. Reach it through both **Go to Preview** and `/iris jigsaw preview goto`. Re-enable Tee and confirm participation returns.
|
||||
|
||||
9. Open **Toolbox** and take schema-`2` named sticks, including selection, capacity, per-variant size, variant/workcell rename, duplicate-one/family, preview, membership, rules/themes, caps, variant deletion, and project deletion. Gate: right-click uses the exact bound context, rename sticks open an anvil and sneak-right-click resets the label, other context sticks open the matching GUI, destructive tools require a second use within 10 seconds, and schema-`1` or replaced-Studio sticks are rejected. Confirm the active variant uses a jigsaw icon, a valid evaluation uses emerald, minimum placements does not use dye, and lime dye appears only as an explicitly labeled theme-membership boolean.
|
||||
|
||||
10. Have a second player try the chest, triple-sneak controls, a direct block edit, `/setblock`, `/fill`, `/execute run setblock`, `/function`, `/data merge block`, `/item replace block`, and an arbitrary plugin mutation command. Also try to break/move/explode the chest and preview. Gate: non-owner mutations and commands outside the strict informational/communication allowlist are cancelled throughout the Studio world; protected content remains intact; owner edits still work.
|
||||
|
||||
11. Test ownership onboarding with prepared fixtures outside an active Studio:
|
||||
|
||||
```text
|
||||
/iris jigsaw adopt inspect overworld smoke/unowned target=auto strategy=auto
|
||||
/iris jigsaw adopt apply <reported-plan-uuid>
|
||||
```
|
||||
|
||||
Gates: an exclusive closure reports `IN_PLACE`, apply leaves every resource byte unchanged while atomically adding ownership/receipt, and the target opens editable. A shared closure reports `CLONE_REQUIRED`; `target=auto` chooses a free `-studio` key and rewrites its internal references without changing the source. Mutate a pinned source after inspect and confirm apply reports stale with no write. An auto-ingested `MANAGED_DATAPACK` fixture must block in-place and succeed only as a private named clone; removing/refreshing the source must not remove that editable clone.
|
||||
|
||||
12. Convert one live registered jigsaw into an unused target:
|
||||
|
||||
```text
|
||||
/iris jigsaw convert overworld minecraft:village_plains target=smoke/converted-village seed=1337
|
||||
```
|
||||
|
||||
Gate: the command reports piece/pool counts and any fidelity-warning count, writes an owned add-only graph with source provenance, and opens it in compact workcells. A non-jigsaw registered key and occupied target both fail without overwrite. Inspect blocks, connectors, unsupported/native-only losses, and automatic display rotation before treating conversion as faithful.
|
||||
|
||||
13. Test variant and project deletion. Create a second variant, load it, and delete the now-inactive first variant through the two-click GUI; the last or currently loaded variant must remain protected. Add an external JSON placement/reference to the project and confirm project deletion is blocked with its owner path/location. Remove the reference, wait for autosave, then use `/iris jigsaw delete confirm=true`; gate: Studio closes and the hash-pinned complete owned closure plus manifest are removed. If removal fails after close, files remain recoverable.
|
||||
|
||||
14. On Folia, create a spatial project with one active workcell crossing several chunks/regions:
|
||||
|
||||
```text
|
||||
/iris jigsaw create overworld smoke/jigsaw-folia mode=spatial compatibility=iris width=32 height=24 depth=32 seed=1337
|
||||
/iris jigsaw bounds 48 24 32
|
||||
/iris jigsaw close
|
||||
/iris jigsaw open overworld smoke/jigsaw-folia seed=1337
|
||||
/iris jigsaw goto workcell/spatial
|
||||
/iris jigsaw piece expand
|
||||
```
|
||||
|
||||
Gate: spatial capacity and its author-facing workcell label persist while the live layout stays unchanged and require reopen. `piece expand` resizes only the active object to 48×24×32; another smaller variant keeps its dimensions. Change blocks in separated chunks of the expanded workcell without manually flushing autosave. With all intersections loaded, automatic capture schedules each intersection on its owning region and commits once after complete validation. Repeat with one intersection unloaded and confirm no owned file changes. Automated coordinator tests are not live Folia proof.
|
||||
|
||||
15. Reopen a retained Iris project, attach it to a dimension/region/biome placement with a unique `placementId`, validate the pack, and generate new chunks. Gate natural occurrence separately from Studio preview. For cave work, first generate the mantle, then verify no-anchor chunks skip and actual anchors align as described in `15 - Caves & Carving.md`.
|
||||
|
||||
### Jigsaw Studio: strict vanilla export
|
||||
|
||||
Create a separate portable project. Its six default planar pieces contain no Iris theme or terminal-rule metadata; keep chance, piece-rule, required-cap, channel, edit, loot, custom-block, and tile metadata absent. Wait for autosave and automatic evaluation to settle:
|
||||
|
||||
```text
|
||||
/iris jigsaw create overworld smoke/jigsaw-portable mode=planar compatibility=vanilla width=16 height=16 depth=16 seed=1337
|
||||
/iris jigsaw export namespace=smoke output=smoke-jigsaw format=zip replace=false
|
||||
/iris jigsaw close
|
||||
```
|
||||
|
||||
1. Confirm `<Iris data>/packs/exports/smoke-jigsaw.zip` was published and contains `pack.mcmeta`, biome tag, processor list, template pools, compressed structure templates, jigsaw structure, and structure set.
|
||||
2. Run the same export with `output=../escape` and confirm it rejects the traversal name without creating an escape artifact or any new output.
|
||||
3. Stop a disposable unmodded Minecraft 26.2 world, put the zip in its `datapacks/` directory, and restart it. Do not use `/reload` for this gate: it can list the pack as enabled without rebuilding the running world's worldgen registries.
|
||||
4. Confirm it is enabled without datapack/data errors, then run `/locate structure smoke:smoke/jigsaw` and generate fresh chunks around the result.
|
||||
5. Gate: the vanilla server loads the pack, locate resolves the exported key, and a natural assembled instance appears. Iris graph tests, successful plugin boot, and NBT decode alone are not vanilla runtime proof.
|
||||
|
||||
This is a required manual runtime gate, not a result established by the current automated checks. Record it as untested until the disposable vanilla server or client completes all five steps.
|
||||
|
||||
Strict export must reject coherent themes, membership chance, non-default piece rules, required caps, non-portable channels, fixed rotation, structure edits/loot, tile payloads/block entities, custom blocks, retained marker blocks, invalid/duplicate connectors, weights outside `1..150`, depth above `20`, or radius above `8`. Disabled planar archetypes are omitted. Full authoring and recovery details: `21 - Jigsaw Structures.md`.
|
||||
|
||||
## I. Offline probe module (no live server)
|
||||
|
||||
From the Iris project root (JDK 25). These are CI-oriented gates, not in-game commands.
|
||||
|
||||
@@ -155,7 +277,7 @@ From the Iris project root (JDK 25). These are CI-oriented gates, not in-game co
|
||||
|
||||
Gate: each probe exits 0. Classload and deserialization probes are part of the release verify job when CI is green (`86 - Maintainer - Release Checklist.md`).
|
||||
|
||||
## I. Minimal post-upgrade checklist
|
||||
## J. Minimal post-upgrade checklist
|
||||
|
||||
After replacing the jar/mod only:
|
||||
|
||||
@@ -167,7 +289,7 @@ After replacing the jar/mod only:
|
||||
|
||||
Gate: no enable crash, packs still loadable, generation continues.
|
||||
|
||||
## J. Failure triage order
|
||||
## K. Failure triage order
|
||||
|
||||
1. Confirm Java 25 and correct platform artifact (`01 - Installation & Platforms.md`).
|
||||
2. Confirm pack validates and dimension key exists (`25 - Pack Management.md`, `05 - Concepts & Pack Layout.md`).
|
||||
|
||||
@@ -2,6 +2,42 @@
|
||||
|
||||
GoldenHash is the cross-platform determinism gate: it generates chunks into in-memory buffers (no world block writes), hashes blocks and biomes, and either captures a baseline file or verifies against one. Identical pack bytes, Iris seed, radius, center, and height range must produce the same combined hash on Bukkit-family and every mod loader. Operator smoke sequences that use this gate are in `31 - Operator Runbooks & Smoke Tests.md`.
|
||||
|
||||
## Tutorial: capture and compare a baseline
|
||||
|
||||
Prerequisites: a disposable Iris world, fixed pack bytes, seed `1337`, center chunk `0,0`, and a small radius such as `8` for the first run. Record the Iris artifact, Minecraft version, pack hash, dimension height range, and JVM before capture.
|
||||
|
||||
1. On Bukkit, run the same `AUTO` command twice. The first run captures when the file is absent; the second must verify it:
|
||||
|
||||
```text
|
||||
/iris developer goldenhash world=<world> radius=8 threads=1 center-x=0 center-z=0 reset-mantle=true deep=false
|
||||
/iris developer goldenhash world=<world> radius=8 threads=1 center-x=0 center-z=0 reset-mantle=true deep=false
|
||||
```
|
||||
|
||||
2. On Fabric, Forge, or NeoForge, use explicit modes:
|
||||
|
||||
```text
|
||||
/iris goldenhash 8 1 capture
|
||||
/iris goldenhash 8 1 verify
|
||||
```
|
||||
|
||||
3. Retain the generated `.hashes` file with the exact artifact and pack hash. Copy that baseline into the platform-specific golden directory on another loader, keep the dimension key, seed, radius, center, and height range identical, then run `verify` or Bukkit `AUTO`.
|
||||
4. Confirm every platform reports `MATCH` and the same `#combined` value. Visual similarity is not parity evidence.
|
||||
5. In a disposable copy, change one pack or engine input, rerun verify, and confirm the expected mismatch produces `.new` and a diagnosis file.
|
||||
6. Restore the original input and confirm `MATCH` again before treating the baseline as a release artifact.
|
||||
|
||||
GoldenHash resets mantle according to the platform rules below. Never use a production world merely because block writes are buffered.
|
||||
|
||||
### Recovery
|
||||
|
||||
| Result | Correction |
|
||||
|---|---|
|
||||
| No baseline on Bukkit | Confirm the target is a loaded Iris world; the first `AUTO` run should capture. |
|
||||
| `VERIFY` says the file is missing | Check the golden directory and exact filename inputs: dimension key, seed, center, and radius. |
|
||||
| Seed or dimension mismatch | Recreate the disposable world with the recorded input; do not rename metadata to force a comparison. |
|
||||
| Multi-thread mismatch only | Re-run both sides with `threads=1`; treat continued order dependence as an engine defect. |
|
||||
| Stable mismatch | Compare pack bytes, height range, Iris artifact, Minecraft version warning, and mantle-reset choice, then inspect `.new` and the first `.diag-*` file. |
|
||||
| Unstable repeat generation | Preserve the diagnosis artifacts and stop the release comparison; consecutive generation is nondeterministic. |
|
||||
|
||||
## What it measures
|
||||
|
||||
- **Blocks:** every local column `x,z ∈ [0,15]` and every `y` from engine/world min height (inclusive) to max height (exclusive). Each block state key (for example `minecraft:stone`) is fed into a per-chunk SHA-256 digest.
|
||||
|
||||
@@ -2,6 +2,28 @@
|
||||
|
||||
Iris throughput is dominated by generation threads, mantle residency, pregen in-flight limits, cache sizes, and optional SIMD kernels. All knobs below live in `settings.json` under the Iris data directory unless noted. Settings overview: `03 - Configuration.md`. Pregen operations: `07 - Pregeneration.md`. Determinism must stay intact after tuning — verify with GoldenHash (`32 - Determinism & Goldenhash.md`).
|
||||
|
||||
## Tutorial: measure one tuning change
|
||||
|
||||
Choose the first knob from observed evidence, not from hardware size alone:
|
||||
|
||||
| Observed limit | First controlled change |
|
||||
|---|---|
|
||||
| Mantle backpressure or timeout warnings | Lower pregen in-flight concurrency; then test a lower `maxResidentTectonicPlates` if heap pressure remains |
|
||||
| High retained heap or long GC pauses | Lower loader caches and resident plate cap; do not raise concurrency |
|
||||
| Repeated object/resource loading with spare heap | Raise only the cache that is missing (`objectLoaderCacheSize` or `resourceLoaderCacheSize`) |
|
||||
| CPU-bound generation with stable heap | Confirm the Vector API module and A/B `simdKernels`; benchmark `engineSVC.parallelism` only afterward |
|
||||
| Region scheduler warnings or chunk-load timeouts | Lower pregen concurrency or use serial/sync isolation before increasing timeout values |
|
||||
| Studio memory growth during repeated edits | A/B `trimMantleInStudio` in Studio only |
|
||||
|
||||
1. Freeze the Iris artifact, pack bytes, seed, center, radius, Java flags, and server population.
|
||||
2. Run one warmup, then record at least three baseline runs with chunk throughput, wall time, peak heap, GC behavior, failed chunks, and GoldenHash.
|
||||
3. Change one setting from the tables below and restart if that setting constructs a pool, cache, or SIMD kernel.
|
||||
4. Repeat the same warmup and three measured runs. Reject comparisons that used different generated areas or active plugins.
|
||||
5. Keep the change only when the median improves without a determinism mismatch, new failure, unacceptable memory growth, or worse tick latency.
|
||||
6. Restore the previous value before testing the next knob.
|
||||
|
||||
Use JProfiler for stalls, allocation pressure, scheduler behavior, or unexplained regressions. A faster pregen status line by itself is not enough evidence for a production tuning recommendation.
|
||||
|
||||
## Where settings live
|
||||
|
||||
| Platform | Data directory | Settings file |
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
`gradle.properties` `minecraftVersion` is the single source of truth for the target Minecraft version. Most build outputs derive from it. This document lists every edit required to move Iris to a new Minecraft version, in order.
|
||||
|
||||
## How to run the bump
|
||||
|
||||
Use a dedicated branch or worktree and begin from a green build. Record the old and new Minecraft, Bukkit API, loader, datapack-format, and NMS binding values before editing. Complete the ordered steps without skipping ahead, run the focused gate after each platform boundary, then run the full all-platform build and disposable-server lifecycle smokes.
|
||||
|
||||
Stop when an upstream API, mapping, or loader artifact is unavailable; do not hide the gap behind a legacy fallback. The bump is complete only when generated artifact names, metadata ranges, data fixers, NMS selection, tests, and documentation all agree on the same target.
|
||||
|
||||
## Source of truth
|
||||
|
||||
`gradle.properties`:
|
||||
|
||||
@@ -6,6 +6,12 @@ Before starting this publication procedure, complete `87 - Maintainer - Release
|
||||
|
||||
Reference values below assume the current `gradle.properties`: `irisVersion=4.0.0-26.2`, `minecraftVersion=26.2`, `fabricLoaderVersion=0.19.3`, `forgeVersion=26.2-65.0.4`, `neoForgeVersion=26.2.0.12-beta`. For a Minecraft version bump, do `85 - Maintainer - MC Version Bump.md` first, then start this checklist.
|
||||
|
||||
## How to execute this checklist
|
||||
|
||||
Use one immutable commit and one JDK 25 environment from preflight through publication. Record build, automated, startup, gameplay, determinism, and publication evidence as separate results; a later successful gate does not erase an earlier unexplained failure. Stop at the first failed required item, fix it on a new candidate commit, and restart the checklist from preflight.
|
||||
|
||||
Publication remains manual. Commands in this document produce local artifacts until the explicit publish section; do not upload, tag, or announce from an unclean or differently tested tree.
|
||||
|
||||
## a. Preflight
|
||||
|
||||
- [ ] Working tree clean on the exact commit you intend to tag (`git status` shows nothing to commit).
|
||||
|
||||
@@ -8,6 +8,12 @@ The current runtime pass prioritizes isolated world creation, deterministic gene
|
||||
|
||||
Cross-links: GoldenHash (`32 - Determinism & Goldenhash.md`), operator smokes (`31 - Operator Runbooks & Smoke Tests.md`), performance knobs (`33 - Performance Tuning.md`), MC bump (`85 - Maintainer - MC Version Bump.md`).
|
||||
|
||||
## How to maintain this tracker
|
||||
|
||||
Work from the first incomplete blocking section. For each checked item, preserve the exact commit, platform artifact, input pack/seed, command or workload, and result outside this document; summarize only stable conclusions here. When a fix supersedes an earlier note, rewrite the note instead of stacking contradictory history.
|
||||
|
||||
Automated tests, server startup, real-player gameplay, profiler captures, and publishing are distinct gates. Mark only the gate actually observed, and leave client-operated or cross-server checks open until that evidence exists.
|
||||
|
||||
## Completion rules
|
||||
|
||||
- [ ] Work through the sections in order. A later section does not override a failed earlier gate.
|
||||
|
||||
Reference in New Issue
Block a user