This commit is contained in:
Brian Neumann-Fopiano
2026-08-11 20:35:27 -04:00
parent 82290a3090
commit 623a02025c
76 changed files with 4820 additions and 280 deletions
+1 -1
View File
@@ -164,7 +164,7 @@ Iris replaces the chunk generator. Vanilla and mod worldgen only runs where Iris
| Vanilla / mod worldgen | Over Iris terrain | Control |
|---|---|---|
| Structures (vanilla, datapack, mod) | Yes, on by default | `importedStructures.disabled` denies individual keys |
| Structures (vanilla, datapack, mod) | Yes, on by default | `importedStructures.disabled` denies families; `disabledExact` denies one complete key |
| Placed features: ores, trees, plants, springs, geodes | Yes, **off by default** | `importedFeatures.enabled` per dimension, with per-step and per-key filters |
| Carvers (caves, canyons, mod carvers) | Never | No `NoiseGeneratorSettings` for a carver to sample; use pack `caves` / `carvings` |
| Surface builders and surface rules | Never | Iris builds surfaces from pack palettes |
+7 -2
View File
@@ -9,6 +9,7 @@ Use these as entry points; follow the linked guide before running destructive or
| 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` |
| Replace the vanilla Nether slot | `/iris create world_nether type=underworld seed=1337 overwrite=true`, then restart | Not available | `minecraft:the_nether` loads through Iris and the retained old Nether is removed only after verification | `06 - Worlds & Lifecycle.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 15×15×15 workcells, one variant per archetype, and seed `1337`; edits then autosave | `21 - Jigsaw Structures.md` |
@@ -28,7 +29,7 @@ If a command fails before doing work, check in this order: platform syntax, perm
- Subcommands and nested groups use method names (or `@Director(name=...)`) and aliases.
- Required parameters appear as positionals; optional parameters with defaults accept `name=value` (or short aliases from `@Param`).
- Help uses Director mini-menu: required shown as `<name>`, optional with default as `[name=default]`.
- Example: `/iris create myworld type=overworld seed=42 main=false`
- Example: `/iris create myworld type=overworld seed=42 main=false overwrite=false`
- Example: `/iris pregen start 5000 world=world center=me gui=true serial=false`
- Contextual params (world, dimension, location) often resolve from the senders current world or look target when omitted.
@@ -69,7 +70,7 @@ Tree feller on mod loaders uses platform permission APIs (`irisworldgen:treefell
| (empty) / help | | Both | `[section]` (modded) | Open help; modded supports section path |
| `version` | | Both | — | Print Iris/platform/Minecraft version and engine count |
| `info` | | **Modded** (see `worlds`) | `[dimension]` | List Iris dimensions and pack details; seed only for gamemasters |
| `create` | `c` | Both | **Bukkit:** `<name> [type=default] [seed=1337] [main=false]` (`type` aliases `dimension`,`pack`). **Modded:** `<name> [pack=overworld] [seed=1337]` | Create Iris world/dimension |
| `create` | `c` | Both | **Bukkit:** `<name> [type=default] [seed=1337] [main=false] [overwrite=false]` (`type` aliases `dimension`,`pack`; `overwrite` alias `force`). **Modded:** `<name> [pack=overworld] [seed=1337]` | Create an Iris world/dimension; Bukkit `overwrite=true` stages an exact slot replacement for restart |
| `teleport` | `tp` | Both | **Bukkit:** `<world> [player=<name>]`. **Modded:** `<dimension> [player]` | Teleport self or named player into Iris world/dimension |
| `evacuate` | | Both | **Bukkit:** `<world>` (player origin). **Modded:** `[dimension]` | Move players out of Iris world to fallback/primary |
| `height` | | Both | — | Print world height (player on Bukkit) |
@@ -101,6 +102,10 @@ Tree feller on mod loaders uses platform permission APIs (`irisworldgen:treefell
---
On Bukkit, `overwrite=true` is deliberately restart-only. The name may resolve to a safe `iris:*` world or exactly the configured main, `_nether`, or `_the_end` alias; arbitrary `minecraft:*` and foreign namespaces are rejected. Iris stages and validates a fresh pack snapshot, compare-and-swaps only that world's `bukkit.yml` generator and seed, and retains the existing dimension folder as a rollback backup until the restarted world proves its Iris identity, pack, dimension, environment, and seed. Multiple distinct slots may be staged before one restart. `main=true` is valid with overwrite only when the name is the configured main-world name. Exact vanilla slots preserve the authoritative seed shared by the existing level, regardless of the supplied `seed`; this keeps Overworld/Nether/End coordinate generation aligned. Use ordinary new-main promotion when a new level seed is required.
---
## Find: `/iris find` (`goto`)
**Origin:** player (Bukkit). **Modded:** gamemaster gate.
+19 -8
View File
@@ -1,6 +1,6 @@
# 06 - Worlds & Lifecycle
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.
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`, validated by its exact normalized root rather than the common `pack` folder name, 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`.
@@ -77,7 +77,7 @@ If the whole registry cannot be parsed during startup, Iris moves it to `iris-di
| Command | Effect |
|---------|--------|
| `/iris create <name> [type=default] [seed=1337] [main=false]` | Create or Folia-stage a managed world |
| `/iris create <name> [type=default] [seed=1337] [main=false] [overwrite=false]` | Create/Folia-stage a managed world, or stage an exact restart replacement |
| `/iris load <name>` / `/iris import <name>` | Load a disk Iris world via reconciler |
| `/iris unload <world>` | Evacuate → unload → close generator |
| `/iris remove <name> [delete=true]` | Unregister / delete managed world |
@@ -91,10 +91,11 @@ Full permission table: `04 - Commands & Permissions.md`.
| Param | Default | Notes |
|-------|---------|-------|
| `name` | required | Becomes `iris:<logical>`; folder must not already exist |
| `name` | required | Normally becomes `iris:<logical>`; with overwrite it may also be the exact configured main, `_nether`, or `_the_end` alias |
| `type` | `default` | Pack/dimension selector: `default``settings.generator.defaultWorldType` (`overworld`); else pack name or `pack:dimensionKey` |
| `seed` | `1337` | World seed |
| `seed` | `1337` | World seed; exact vanilla-slot overwrite preserves the existing level's shared authoritative seed instead |
| `main` | `false` | Schedule main-world promotion on JVM shutdown (Paper path) or promote during Folia staging |
| `overwrite` (`force`) | `false` | Stage a validated exact-slot replacement for the next restart; never deletes a loaded world live |
Create refuses the primary Bukkit thread. Startup datapack validation must be ready and the selected source pack must have a loadable validation result before the lifecycle lease, datapack preparation, dimension folder, pack snapshot, registration, or Bukkit/NMS create path is entered; lifecycle domain `WORLD_MUTATION` / kind `WORLD_CREATE` must then be free or create fails busy.
@@ -103,7 +104,7 @@ Create refuses the primary Bukkit thread. Startup datapack validation must be re
1. Resolve the managed key and dimension without creating the dimension root.
2. Require startup datapack readiness and a loadable validation result for the dimension's owning pack.
3. Ensure datapacks for the dimension types are installed; queue restart if types not yet loaded.
4. Copy the pack into `<world>/iris/pack` (`StudioSVC.installIntoWorld`) — atomic stage → publish; refuses primary thread.
4. Copy the pack into `<world>/iris/pack` (`StudioSVC.installIntoWorld`) — atomic stage → publish; refuses primary thread. Iris invalidates any prior result for that exact root and validates the final published tree before generator creation; failure rolls the publication back.
5. Build `WorldCreator` with Iris generator (`studio=false`).
6. Create the world through `WorldLifecycleService` / NMS async create (timeout 120s; timeout triggers server restart).
7. Register the world in `bukkit.yml` with generator `Iris` dimension key and seed; update the Multiverse link when present.
@@ -116,13 +117,21 @@ Runtime world creation is disabled on Folia. `/iris create` instead:
1. Requires startup datapack readiness and a loadable validation result for the selected pack; refusal leaves no dimension folder or registration.
2. Acquires the `WORLD_CREATE` lease.
3. Installs datapacks if changed.
4. Stages the pack into the managed dimension root via `installIntoWorld`.
4. Stages the pack into the managed dimension root via `installIntoWorld`; the final published snapshot must pass exact-root validation before registration.
5. Registers the world in `bukkit.yml` (`BukkitWorldConfiguration.register`).
6. If `main=true`, promotes main-world files immediately under lease (failure rolls back bukkit.yml + deletes staged folder).
7. Instructs the operator to restart; generation/load happens on next startup.
`WorldLifecycleStaging` holds staged generators/biome providers for the backend that consumes them at load.
## Exact world-slot replacement
`overwrite=true` uses lifecycle kind `WORLD_REPLACE` and always stages for restart on Bukkit-family servers, including Paper and Folia. It accepts safe `iris:*` keys and only the three exact vanilla slots resolved from the configured level name: `minecraft:overworld`, `minecraft:the_nether`, and `minecraft:the_end`. A vanilla slot requires a matching pack environment (`NORMAL`, `NETHER`, or `THE_END`), and Nether/End replacement requires the server's matching allow setting to be enabled; foreign namespaces, other `minecraft:*` keys, path traversal, links, and special filesystem entries fail closed. `main=true` may accompany overwrite only for the configured main-world name. Minecraft stores one authoritative seed for the existing level, so all three exact vanilla slots preserve that loaded primary-world seed and report when it differs from the command's `seed`; changing the level seed remains the ordinary new-main promotion workflow.
The transaction copies and validates a fresh frozen pack under a same-filesystem sibling stage, fingerprints it, journals the original target state and `bukkit.yml` generator/seed, then compare-and-swaps that one configuration entry. Distinct slots can be queued before one restart. During Iris `STARTUP`, before Bukkit loads worlds, each authorized transaction atomically moves the old exact dimension directory to a retained sibling backup and publishes its stage. There is no chunk merge: old region, entity, POI, and Iris data remain only in the backup, while the target starts with the staged pack snapshot.
The backup is deleted only after `WorldLoad` proves the exact namespaced identity, Iris generator, selected dimension, seed, vanilla-slot environment, and unchanged pack fingerprint. A failed runtime check journals rollback, restores the prior `bukkit.yml` generator/seed with compare-and-swap semantics, requests another restart, and restores the retained directory before that restart loads worlds. A crash between either atomic move or journal write is retried idempotently. Conflicting manual configuration, changed staged bytes, unsafe storage, or corrupt journals block Iris world admission and preserve the stage/backup for operator recovery instead of guessing or deleting.
## Studio create
Studio uses `IrisCreator.studio(true)`:
@@ -143,7 +152,7 @@ Studio uses `IrisCreator.studio(true)`:
2. `BukkitWorldReconciler.loadWorld(bukkit.yml, worldKey)`.
3. Reports success, busy, restart-required, or failure.
Load does not re-download packs; the world must already have `iris/pack` content and registration data consistent with Iris. Reconciliation checks startup readiness and the resolved pack before touching `bukkit.yml` or calling a world backend.
Load does not re-download packs; the world must already have `iris/pack` content and registration data consistent with Iris. Reconciliation checks startup readiness, then lazily validates that world's exact snapshot root before touching `bukkit.yml` or calling a world backend. Results are path-scoped, so separate worlds whose snapshot folders are both named `pack` cannot authorize or reject one another.
## Unload
@@ -188,6 +197,8 @@ When create sets `main=true` (non-Folia), a shutdown hook rewrites `server.prope
Promotion requires absent target level folder and refuses symlink world data. Folia with `main=true` performs the same publish during staging instead of deferring to shutdown.
To replace the currently configured main slot in place, name that exact main world and use `overwrite=true`; this keeps the top-level level root, shared datapacks, player data, and non-target dimensions intact. Ordinary `main=true` without overwrite remains the new-level-root promotion workflow above.
## Pack snapshot vs studio (lifecycle view)
| Operation | Pack effect |
@@ -200,4 +211,4 @@ Promotion requires absent target level folder and refuses symlink world data. Fo
## Concurrent lifecycle guards
`LifecycleOperationCoordinator` serializes domains including `WORLD_MUTATION` and `PACK_MUTATION`. Overlapping create/load/unload/remove/pack-publish returns busy to the operator. World create also refuses if the dimension root already exists or the world is already loaded.
`LifecycleOperationCoordinator` serializes domains including `WORLD_MUTATION` and `PACK_MUTATION`. Overlapping create/load/unload/remove/replace/pack-publish returns busy to the operator. Ordinary world create refuses if the dimension root already exists or the world is already loaded; exact replacement uses a separately journaled restart transaction and never relaxes removal-path protection.
+3 -3
View File
@@ -82,7 +82,7 @@ If mode construction fails, the engine logs a warning and falls back to `OVERWOR
| `dimensionHeight` | `IrisRange` | min `-64`, max `320` | World min/max Y. Iris generates internal height `max - min`, then shifts by min on output |
| `fluidHeight` | int | `63` | Required; 01024. Fluid column top in **internal** Y (0 = bottom of dimension height). World Y ≈ `fluidHeight + dimensionHeight.min` |
| `environment` | `IrisEnvironment` | `NORMAL` | `NORMAL`, `NETHER`, `THE_END`, `CUSTOM` — selects base datapack dimension template (overworld/nether/end) |
| `fullbright` | boolean | `false` | Forces maximum ambient lighting when true |
| `fullbright` | boolean | `false` | Forces maximum ambient lighting; on Minecraft 26.2 this emits a white ambient-light color as well as scalar ambient light |
| `bedrock` | boolean | `true` | Places bedrock at internal Y 0 when true |
| `caveLavaHeight` | int | `8` | Subterrain fluid layer height (0318) |
@@ -127,7 +127,7 @@ If mode construction fails, the engine logs a warning and falls back to `OVERWOR
| Field | Type | Default | Notes |
|-------|------|---------|-------|
| `rockPalette` | `IrisMaterialPalette` | stone | Subsurface “stone” fill palette |
| `fluidPalette` | `IrisMaterialPalette` | water | Fluid block palette |
| `fluidPalette` | `IrisMaterialPalette` | water | Ocean columns and `allowFluid` cave aquifers; accepts any weighted block palette |
| `rockZoom` | double | `5` | Rock palette noise zoom |
| `ores` | `IrisOreGenerator[]` | empty | Dimension-wide ore generators (surface vs underground via generator flags) |
| `deposits` | `IrisDepositGenerator[]` | empty | Global deposit blobs |
@@ -208,7 +208,7 @@ Tri-state fields use `DEFAULT` | `TRUE` | `FALSE` (follow base dimension when `D
| `skylight` | `DEFAULT` | Has skylight |
| `ceiling` | `DEFAULT` | Logical bedrock ceiling |
| `coordinateScale` | `-1` (unset) | Portal scale |
| `ambientLight` | `-1` (unset) | 01 ambient |
| `ambientLight` | `-1` (unset) | 01 ambient; a resolved value of `1` emits white ambient-light color on Minecraft 26.2 |
| `fixedTime` | `-1` sentinel | Fixed day time when set |
| `cloudHeight` | `-1` sentinel | Cloud Y or null to disable |
| `monsterSpawnBlockLightLimit` | `-1` (unset) | 015 |
+4
View File
@@ -150,6 +150,8 @@ Type: `IrisBiomeCustom` (`@Snippet("custom-biome")`). Installed via datapack com
| `grassColor` | hex string | `""` (omit if empty) | |
| `foliageColor` | hex string | `""` | |
On Minecraft 26.2, Iris publishes sky, fog, water-fog, and ambient-particle values through the biome environment-attribute registry. Water, grass, and foliage colors remain biome effects. This conversion is automatic; pack fields do not change.
Tag inheritance: effective tags = authored `tags` plus non-structure tags of the vanilla derivative. Structure tags (`has_structure/*`) are **not** inherited so native structures are not double-placed.
#### Custom spawn entry (`IrisBiomeCustomSpawn`)
@@ -202,6 +204,8 @@ A surface biome contributes all of its `structures[]` placements when it owns th
`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.
Biome reachability follows configured region roots, enabled dimension-carving biomes, ordinary children and carving replacements, floating targets, and floating `carving` references recursively. Floating carving-entry ids resolve before direct biome keys, matching generation; cycles are deduplicated, and every generation-reachable biome participates in runtime spawn, placement, structure, and lookup indexes. Custom-biome datapack installation continues to scan the pack's complete authored biome set.
### Target, footprint, and altitude
| Field | Default / range | Behavior |
+22 -18
View File
@@ -1,6 +1,6 @@
# 15 - Caves & Carving
Iris carves caves itself during mantle generation via `MantleCarvingComponent` and `IrisCaveCarver3D`. Density fields from `IrisCaveProfile` decide solid vs air/water/lava. Cave biomes paint floors, ceilings, decorators, and objects inside carved space. Vanilla and mod noise carvers never run over Iris terrain.
Iris carves caves itself during mantle generation via `MantleCarvingComponent` and `IrisCaveCarver3D`. Density fields from `IrisCaveProfile` decide solid vs air, the dimension's configured fluid palette, or deep lava. Cave biomes paint floors, ceilings, decorators, and objects inside carved space. Vanilla and mod noise carvers never run over Iris terrain.
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`.
@@ -15,7 +15,7 @@ Start with a validating `OVERWORLD` pack whose surface and fluid height are alre
"enabled": true,
"verticalRange": { "min": 0, "max": 64 },
"allowSurfaceBreak": false,
"allowWater": false,
"allowFluid": false,
"allowLava": false
}
}
@@ -23,9 +23,9 @@ Start with a validating `OVERWORLD` pack whose surface and fluid height are alre
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.
3. Generate new chunks and inspect below the surface. Success is carved air within the configured vertical range, an intact surface, and no fluid-palette blocks or deep 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.
5. Once the void shape is proven, add one biome key to a region's `caveBiomes`, then add cave layers and decorators. Enable dimension fluid, deep lava, or surface breaks one setting at a time so each change remains observable.
## Architecture (author-relevant)
@@ -33,7 +33,9 @@ Start with a validating `OVERWORLD` pack whose surface and fluid height are alre
2. Per column, Iris resolves a cave profile from biome → region → dimension (`enabled` profiles only).
3. Profiles blend across neighbors; `IrisCaveCarver3D` samples 3D density and writes carve flags into the mantle.
4. Cave biomes (region `caveBiomes`, dimension `carving` Y-band overrides, surface biome `carvingBiome`) supply materials and content for carved voxels.
5. Fluid placement inside caves follows profile water/lava rules and surface-clearance guards.
5. Aquifers sample the dimension `fluidPalette` below `fluidHeight`; deep lava remains a separate profile rule controlled by `allowLava` and `caveLavaHeight`.
Enabled dimension `carving` biome graphs are included in the dimension's recursive reachable-biome closure even when no region lists them, so their custom biome identities and spawn mappings are available wherever the Y-band selects them.
Empty pack folders such as `caves/` or `ravines/` are not separate registrant types. Carving is profile-driven JSON on dimensions/biomes/regions, not standalone cave files.
@@ -56,7 +58,7 @@ Iris does not implement Minecraft `NoiseGeneratorSettings` carver sampling. Gene
## Cave profile (`IrisCaveProfile`)
Snippet key: `cave-profile`. Appears on **dimension**, **region**, and **biome**. Resolution prefers the most specific enabled profile in the mantle path (biome/region/dimension blend).
Snippet key: `cave-profile`. Appears on **dimension**, **region**, and **biome**. Resolution prefers the most specific enabled profile in the mantle path (biome/region/dimension blend). The dimension-level `fluidPalette` supplies aquifer material and accepts any weighted block palette; its default is water. A lava `fluidPalette` therefore turns otherwise identical Overworld aquifers into lava without changing cave geometry.
| Field | Type | Default | Notes |
|-------|------|---------|-------|
@@ -88,10 +90,12 @@ Snippet key: `cave-profile`. Appears on **dimension**, **region**, and **biome**
| `defaultObjectPlaceMode` | `ObjectPlaceMode` | null | Prefer stilt modes for cave props |
| `anchorScanStep` | int 1..8 | `1` | Vertical anchor search step |
| `anchorSearchAttempts` | int 1..64 | `6` | Random column retries per chunk |
| `allowWater` | boolean | `true` | Cave water below fluid height |
| `waterMinDepthBelowSurface` | int 0..64 | `12` | Depth before cave water |
| `waterRequiresFloor` | boolean | `true` | Solid floor under water |
| `allowLava` | boolean | `true` | Cave lava by lava height rules |
| `allowFluid` | boolean | `true` | Place dimension `fluidPalette` aquifers below fluid height |
| `fluidMinDepthBelowSurface` | int 0..64 | `12` | Minimum surface burial before aquifer placement |
| `fluidRequiresFloor` | boolean | `true` | Require a supported cup beneath aquifer blocks |
| `allowLava` | boolean | `true` | Place vanilla lava at or below `caveLavaHeight` |
`allowWater`, `waterMinDepthBelowSurface`, and `waterRequiresFloor` were removed. Pack validation rejects them with their replacement names so an old dry-cave setting cannot silently fall back to the new `allowFluid: true` default.
### Density module (`IrisCaveFieldModule`)
@@ -180,7 +184,7 @@ Editable Iris jigsaws can resolve starts against the carved-space mantle instead
| `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 |
| `underwater` | `false` | For cave anchors, require a dry cavern cell: ordinary cavern air must be above `caveLavaHeight`, explicit palette-fluid/deep-lava cells are rejected, and forced-air cavern matter remains dry below that threshold; `true` permits fluid cavern cells |
Geometry and alignment are exact:
@@ -193,7 +197,7 @@ Geometry and alignment are exact:
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 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 palette-fluid/deep-lava cells 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.
@@ -236,9 +240,9 @@ Dimension switch and deepdark band (`dimensions/overworld.json`):
"defaultObjectAnchor": "FLOOR",
"defaultObjectPlaceMode": "ORGANIC_STILT",
"anchorSearchAttempts": 12,
"allowWater": true,
"waterMinDepthBelowSurface": 20,
"waterRequiresFloor": true,
"allowFluid": true,
"fluidMinDepthBelowSurface": 20,
"fluidRequiresFloor": true,
"allowLava": true,
"modules": [
{
@@ -283,7 +287,7 @@ Cave biome content (`biomes/carving/amethyst.json` excerpt): floor/wall amethyst
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.
3. Add one tunnel or room module. Generate new chunks and verify void shape, surface clearance, fluid-palette 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.
@@ -298,7 +302,7 @@ Cave biome content (`biomes/carving/amethyst.json` excerpt): floor/wall amethyst
| Thinner tunnels | Raise threshold, lower `detailWeight`, add inverted modules |
| Fewer surface holes | Raise `surfaceBreakNoiseThreshold`, lower `surfaceBreakDepth`, or `allowSurfaceBreak: false` |
| Safer cave props | Raise `objectMinDepthBelowSurface`, set place mode + anchor |
| Dry caves | `allowWater: false` |
| No aquifers | `allowFluid: false` |
| Performance | Higher `sampleStep`, keep adaptive sampling on, simpler styles |
## Practical notes
@@ -306,4 +310,4 @@ Cave biome content (`biomes/carving/amethyst.json` excerpt): floor/wall amethyst
- Profile `enabled: false` (the Java default) produces no profile carving even if cave biomes are listed.
- Cave biome layers still need solid carve first; they do not create voids alone.
- Upper-dimension carving is optional and off in overworld.
- Pack JSON may contain unknown keys; only fields on `IrisCaveProfile` apply.
- The three removed water-specific cave-profile keys are blocking pack errors in inline dimension, region, and biome profiles and in `snippet/cave-profile` files; other unknown keys remain subject to the normal pack validation rules.
+1 -1
View File
@@ -97,7 +97,7 @@ Dimension defaults:
| Field | Default | Role |
|-------|---------|------|
| `rockPalette` | stone | Fill below biome layers |
| `fluidPalette` | water | Ocean/fluid column |
| `fluidPalette` | water | Ocean/fluid columns and `allowFluid` cave aquifers |
| `rockZoom` | `5` | Rock palette zoom |
`IrisBlockData` entries use `block` (id), optional `weight`, optional `data` blockstate map. They can also reference reusable block aliases as described below.
+1
View File
@@ -44,6 +44,7 @@ Generated by Minecraft's machinery with full native fidelity (processors, entiti
| 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` |
| Remove one native key without removing its variants | `importedStructures.disabledExact` |
| 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` |
+1 -1
View File
@@ -578,7 +578,7 @@ Run this in a purpose-named disposable pack/world and record each gate separatel
1. **Creation:** create a planar `IRIS_EXTENDED` project without optional mode, compatibility, dimensions, or seed. Confirm planar/Iris/15×15×15/1337 defaults, one structure, three pools, six pieces, six objects, one ownership manifest, tab completion of its key for `open`/`edit`/`reopen`, 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, one clear block between capacity rows and columns, 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.
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 through the control chest, `/iris jigsaw menu`, and triple-sneak and confirm End Cap is selected each time without an inventory-view linkage error. 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:** stage Hallway capacity `16×3×3` in the open Workcell Settings menu, apply it once, and make another workcell capacity `16×8×16`; confirm no existing object byte changes and the live relayout moves only the white-concrete cages without close/reopen. 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 its white-concrete cage remains while the GUI and scoreboard report Disabled, and confirm seed-`1337` evaluation excludes Tee pieces. Re-enable it and confirm participation returns; test export filtering separately on the portable fixture.
+33 -6
View File
@@ -148,9 +148,11 @@ A datapack structure whose filter lists only its own biomes never generates unti
| Field | Default | Meaning |
|---|---|---|
| `disabled` | `[]` | Structure keys/prefixes to deny. |
| `disabledExact` | `[]` | Complete structure keys to deny without matching related variants. |
| `undergroundYShift` | `0` (-512..512) | Vertical offset for underground-step structures only. Surface structures never use it. |
| `datapackOverrides` | `true` | Whether ingested datapacks may replace `minecraft:`-namespaced structure content (2.5). |
| `adjustments` | `[]` | Per-structure adjustments for structures still generating natively (1.4). |
| `frequencyOverrides` | `[]` | Exact structure-set placement-density multipliers (1.4). |
| `adjustments` | `[]` | Per-structure adjustments for structures still generating natively (1.5). |
#### Prefix matching
@@ -162,15 +164,38 @@ Used by `disabled` and `adjustments[].match`. Both sides trimmed and lowercased;
`"minecraft:village"` matches village variants; `"nova_structures"` without trailing colon does **not** match the namespace.
`disabledExact` trims and lowercases each complete key, then compares for equality only. For example, `"minecraft:ruined_portal"` there disables the Overworld variant while leaving `"minecraft:ruined_portal_nether"` enabled. Use `disabled` when the whole family should be denied.
```json
{
"importedStructures": {
"disabled": ["minecraft:village", "minecraft:pillager_outpost"]
"disabled": ["minecraft:village", "minecraft:pillager_outpost"],
"disabledExact": ["minecraft:ruined_portal"]
}
}
```
### 1.4 `adjustments[]`
### 1.4 `frequencyOverrides[]`
Use this to make a registered native structure set more or less common without converting its structures to explicit Iris placements. Each entry is `{ "structureSet": "namespace:path", "multiplier": 0.01..16 }`; `structureSet` is an exact registered **structure-set key**, not a structure key, and the last normalized duplicate wins. Bukkit/Paper, Fabric, Forge, and NeoForge apply the same dimension-scoped contract to newly generated chunks.
```json
{
"importedStructures": {
"frequencyOverrides": [
{ "structureSet": "minecraft:nether_complexes", "multiplier": 1.1 },
{ "structureSet": "minecraft:ruined_portals", "multiplier": 1.1 },
{ "structureSet": "minecraft:nether_fossils", "multiplier": 1.1 }
]
}
}
```
Iris retains the registered set entries, weights, biome eligibility, placement algorithm, salt, exclusion zones, structure start/Y logic, processors, entities, mobs, loot, and native locate path. For random-spread placement, it first scales Minecraft's placement probability up to `1`, then derives the nearest integer spacing with `round(oldSpacing / sqrt(remainingMultiplier))`, never below `separation + 1`. Integer rounding means the realized increase can be slightly lower or higher: at `1.1`, Nether complexes move from spacing `27` to `26` (about `7.8%` denser), ruined portals from `40` to `38` (about `10.8%` denser), and Nether fossils remain `2/1` because no smaller legal spacing exists.
Concentric-ring sets can scale only their placement probability; a ring placement already at probability `1` cannot become denser through this field. Minecraft or modded custom placement types outside the affected override and exclusion-zone graph remain untouched. An exact override, or an exclusion dependency on an overridden set, that requires copying an unsupported placement fails world binding instead of silently leaving stale exclusion behavior. Existing chunks and existing starts are never rewritten.
### 1.5 `adjustments[]`
Each entry (`match` selects targets by the same prefix rule):
@@ -295,6 +320,8 @@ Installed datapacks are real Minecraft datapacks at `<level root>/datapacks/<id>
Ingest and recovery run synchronously in Iris's startup admission gate when `general.autoIngestDatapacks` is enabled (default true); players and every Iris world/Studio creation path remain locked until that phase is valid. A persisted manifest/configuration/content fingerprint lets an unchanged boot skip remote resolution and full revalidation, and Iris refreshes that fingerprint after its own authorized post-start import maintenance; URL, Minecraft/Iris version, override policy, external manifest edits, staging, transaction, installed content, or cache corruption still invalidates reuse and runs the full fail-closed path. Minecraft builds worldgen registries at server start, so a **newly installed or repaired** datapack requires a clean restart before admission; after it returns, keys are live only in the per-world structure state of declaring Iris dimensions.
Cache reuse is a local validation decision and does not poll remote sources; run `/iris datapack ingest` when you want an update check. Every successful ingest persists fresh staging and installed-target receipts, so unchanged bootstrap recovery leaves the manifest stable and the next startup can reuse the cached fingerprint.
Scratch validation rejects links, junction-like special files, and real cross-volume entries. On Windows/Java 25, Iris also verifies the drive root and volume serial when the JDK reports unequal `FileStore` identities only because a path crossed the legacy 247-character prefix boundary; unresolved cleanup, identity, transaction, or validation failures remain blocking and create no world artifacts.
### 2.3 Manual commands
@@ -332,7 +359,7 @@ Scratch validation rejects links, junction-like special files, and real cross-vo
}
```
**(c) Manual placement only.** Disable the datapack namespace, then place specific keys with `nativeStructures` — see **`disabled` never blocks an explicit placement** below:
**(c) Manual placement only.** Disable the datapack namespace, then place specific keys with `nativeStructures` — see **`disabled` and `disabledExact` never block an explicit placement** below:
```json
{
@@ -377,9 +404,9 @@ Placement grid fields (`distribution`, `spacing`/`separation`/`salt`, `density`,
Scoping matches Iris placements. Validation requires the structure's effective assembly span stay inside Minecraft's 128-block (8-chunk) structure reference range.
### 3.2 `disabled` never blocks an explicit placement
### 3.2 `disabled` and `disabledExact` never block an explicit placement
The placement injector generates planned starts without consulting `disabled` and bypasses the structure's own biome filter. "Disable namespace, re-place explicitly" is supported.
The placement injector generates planned starts without consulting either deny list and bypasses the structure's own biome filter. Both "disable namespace, re-place explicitly" and exact-key denial with explicit replacement are supported.
### 3.3 `nativeSuppression: REPLACE_SOURCE`
+1 -1
View File
@@ -143,7 +143,7 @@ Implementation:
1. Requires `confirm=true`.
2. Optional `StudioSVC.downloadSearch` when `fresh-download`.
3. Acquires `PACK_MUTATION` / `PACK_PUBLISH` lease.
4. `StudioSVC.replaceIntoWorld` → install into `worldFolder/iris/pack` with `replaceExisting=true` (atomic stage/publish).
4. `StudioSVC.replaceIntoWorld` → install into `worldFolder/iris/pack` with `replaceExisting=true` (atomic stage/publish), invalidate the previous exact-root validation result, and validate the final published snapshot; validation failure rolls the replacement back.
5. If an engine still holds that pack data, Iris **restarts the server** after commit (`"An active Iris world pack was replaced."`).
This is intentionally unsafe for production without backups: existing chunks keep old terrain; only future generation and pack-driven systems see new content. Prefer staging a new world when pack contracts change.
+3 -2
View File
@@ -41,10 +41,10 @@ Hotload: Bukkit file-watch engine; modded 3s poll. Same invalidate/reload/locale
| Concern | Bukkit | Modded |
|---------|--------|--------|
| Create | `/iris create` → managed world name, generator Iris, optional main-world | `/iris create` or `/iris world enable` → dimension id + pack injection |
| Create | `/iris create` → managed world name, generator Iris, optional main-world; `overwrite=true` stages exact Iris/vanilla-slot replacement for restart | `/iris create` or `/iris world enable` → dimension id + pack injection |
| Load / unload | `/iris load` (`import`), `/iris unload` | `/iris world disable` unloads; no separate load command |
| 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` |
| Primary / main world | create `main=true` for a new level root, or name the configured main with `overwrite=true` for journaled in-place dimension replacement | `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; `/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) |
@@ -76,6 +76,7 @@ Jigsaw pack resources are shared runtime data, but in-game Jigsaw Studio is not
| 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 |
| Exact restart replacement of configured Overworld/Nether/End slots | yes | no | no | no |
| Pregen | yes (Paper-like / Folia modes) | yes (`moddedPregenInFlight`) | yes | yes |
| Studio open/close/vscode/package | yes | yes | yes | yes |
| Object wand / paste / save / undo | yes | yes | yes | yes |
+13 -2
View File
@@ -36,6 +36,17 @@ GoldenHash details and file layout: `32 - Determinism & Goldenhash.md`.
4. Join or teleport into the world. Confirm non-empty terrain, surface biomes, and no repeating console stack traces on first chunks.
5. Gate: world is loaded as an Iris world; chunks generate without enable-time crash; console shows no fatal engine init failure.
## A.1 Exact vanilla-slot replacement (Bukkit-family)
Use a disposable server whose configured level name is `world`, with a valid `NETHER` Iris pack and a generated vanilla Nether containing a unique marker chunk. Record hashes of the old Nether `region`, `entities`, and `poi` files before staging.
1. Run `/iris create world_nether type=<nether-pack> seed=1337 overwrite=true`. Gate: the command says the replacement is staged, the loaded Nether and its files remain unchanged, `bukkit.yml` now names `Iris:<dimension>`, and one pending replacement journal plus one sibling stage exists.
2. Optionally stage the configured main name with a `NORMAL` pack and the End alias with a `THE_END` pack. Gate: each distinct slot gets its own transaction and no live dimension folder is moved.
3. Restart normally. Gate: Iris publishes before Bukkit world loading; `minecraft:the_nether` loads with the Iris generator, requested dimension and seed; its frozen `iris/pack` exists; no old `region`, `entities`, or `poi` file was merged into the target; and the marker chunk is absent.
4. Gate after `WorldLoad`: the retained sibling backup and journal disappear only after identity, environment, seed, dimension, and pack-fingerprint verification succeeds.
5. Restart again and generate fresh Nether chunks. Gate: the exact vanilla identity and Iris generator persist, ordinary Nether portals still target `minecraft:the_nether`, and no pending stage/backup/journal returns.
6. Repeat once with a deliberately changed staged pack or conflicting `bukkit.yml` value before restart. Gate: Iris refuses publication or world admission, preserves recoverable artifacts, and never guesses a target. For a post-publication verification failure, gate that Iris restores the prior configuration, requests the controlled rollback restart, and restores the retained original directory before world load.
## B. Fresh install and first world (Fabric / Forge / NeoForge)
1. Install the matching mod jar into `mods/`. Fabric requires Loader ≥ declared floor; Forge/NeoForge require their declared floors. See `01 - Installation & Platforms.md` and `30 - Platform Differences.md`.
@@ -61,7 +72,7 @@ Or a single pack: `/iris pack validate pack=<pack>` on Bukkit, `/iris pack valid
2. Review blocking errors vs warnings. Blocking errors must be fixed before treating the pack as production-ready.
3. `/iris pack status` replays the startup-published result, including a persisted result reused for unchanged content.
4. Restart without changing packs or registry context. Gate: startup logs persisted validation reuse instead of full parsing, player admission opens only after datapack and pack phases are ready, and the target remains loadable.
4. Restart without changing packs or registry context. Gate: startup logs `External datapacks match the persisted startup validation` without logging another external-datapack `Validating` or `Ingesting` pass, pack validation reuses its persisted result instead of full parsing, player admission opens only after both phases are ready, and the target remains loadable.
5. Change one pack byte and restart. Gate: the content fingerprint invalidates reuse and validation runs again; restore the pack before continuing. Cleanup/restore flows are separate and opt-in (`25 - Pack Management.md`).
## D. Bukkit datapack dimension-scope smoke
@@ -185,7 +196,7 @@ Use a disposable pack/structure key and the owning builder account. Bukkit has o
Gate: every cell has one physical white-concrete edge cage, no workcell-bound display entity exists, and focused plus nearby particle trails outline the editable bounds inside those cages. Focused connectors draw 1.75-block direction lines when particles are enabled. 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.
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. Gate: each path opens without an `InventoryView` linkage error on the target Paper-family runtime. 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 15×15×15, its two real markers occupy `(7,7,0)` and `(7,7,14)`, 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. Break one marker and click **Reset Connector Blocks** before autosave; both saved markers must return while another edited block remains unchanged. 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.
+3 -1
View File
@@ -282,13 +282,15 @@ Iris replaces the chunk generator. Vanilla/mod worldgen runs only if Iris runs i
| System | Over Iris? | Notes |
|---|---|---|
| Structures (vanilla, datapack, mod) | **Yes**, on by default | Vertical fit, stilts, vegetation clear. Deny: `importedStructures.disabled` |
| Structures (vanilla, datapack, mod) | **Yes**, on by default | Vertical fit, stilts, vegetation clear. Deny families with `importedStructures.disabled`, one complete key with `disabledExact`, or scale an exact structure set with `frequencyOverrides` |
| Placed features (ores, trees, plants, …) | **Yes**, **off** by default | Dimension `importedFeatures.enabled` |
| Carvers | **Never** | No noise router / aquifer for vanilla carvers |
| Mod biomes as sources | Only as derivative / scatter targets | Iris chooses biomes from the pack |
| Mob spawning (incl. mod mobs) | **Yes** | Merges pack biome table with vanilla derivative |
| Surface builders / rules | **Never** | Pack palettes |
`importedStructures.frequencyOverrides` has Bukkit parity on all three mod loaders. Entries use `{ "structureSet": "namespace:path", "multiplier": 0.01..16 }`; keys are exact registered structure-set keys, last duplicate wins, and changes affect new chunks only. Random-spread sets scale probability first and then derive the nearest legal integer spacing, while concentric rings can scale probability only. Custom placement types outside the affected override and exclusion-zone graph remain untouched; an unsupported placement that must be copied fails level binding rather than applying a partial override. Full semantics and the Nether `1.1` example are in `22 - Native Structures & Datapacks.md`.
### `importedFeatures`
Disabled by default. Absent or `enabled: false` → no feature table; terrain matches long-standing Iris-only output. Biome tags of the vanilla derivative are always inherited on custom biomes (not gated on this flag). Structure tags `#minecraft:has_structure/*` are **not** inherited.