Structure Configuration
Register world-generated structures — buildings, plants, dungeon pieces — with placement rules and optional branching links.
Structures are pre-built arrangements of blocks placed during world generation — trees, buildings, ruins, dungeons. This is the catalog file that registers structure placement rules; the actual block layout comes from .structure files (exported from the in-game structure editor) under mods/<YourMod>/structures/<structure-name>/.
File Location
Register the file in your mod’s manifest (mods/<YourMod>/<YourMod>.yaml):
structure-config: structure-config.yaml
Then create the file it points to, relative to your mod’s folder:
mods/<YourMod>/structure-config.yaml
The file has two top-level keys — structures: (required, the base placement catalog) and structure-links: (optional, for branching/composite structures):
structures:
- name: my_structure
spawn-type: surface
name must match a subfolder under mods/<YourMod>/structures/ containing one or more .structure files exported from the structure editor. There is no automatic mod-name prefixing here — name is used directly as the folder name.
Minimal Example
structures:
- name: my_bush
spawn-type: surface
spacing:
x: 5
y: 5
spawn-chance: 0.3
biomes:
- YourMod:my_biome
(A link-free structure like this no longer needs max-size — they default to the structure’s own measured size.)
Fields
| Field | Default | Description |
|---|---|---|
name | (required) | Matches a subfolder under mods/<YourMod>/structures/. |
spawn-type | (required) | Where/how this structure is placed — see Spawn Types below. |
biomes | (none) | Which biome names this structure can spawn in. Empty means it never spawns. |
spawn-chance | 1.0 | Probability (0.0–1.0) a given eligible cell spawns this structure. Resolved once per cell from the world seed at chunk generation, not re-rolled — see Chance vs. weight. (Trees use the same key name for the same purpose.) |
spawn-chance-inverse | false | Selects the opposite end of the per-cell random range, so the structure lands on a different set of cells at the same density — spawn-chance: 0.5 still fills 50% of eligible cells either way. Use it to shift a layout without changing how much spawns. |
spacing (x, y) | 1 each | Spacing, in blocks, of the placement grid cell this structure is checked against — larger values space out attempts (and allow bigger structures) at the cost of density. Must be at least 1 (a smaller value is clamped up with a warning). |
max-link-depth | 64 | Maximum number of link hops followed out from this structure’s anchor. A safety bound against a self-link or link cycle chaining forever (a self-link that always attaches — chance: 1.0 in an any-of:, or a lone candidate in a one-of: — would otherwise never stop). Raise it only if you intentionally build deep link chains; there is also a hard per-anchor cap on total linked structures that no value can exceed. |
max-size (width, height) | (structure’s own size) | Optional. The generator’s scan-reach budget — how far out it looks for this structure’s anchor so placement still works when only a distant part overlaps the chunk being generated. Defaults to (and can never go below) the structure’s own size, measured automatically from its .structure files, so a link-free structure never needs to set it. Raise it above the body size only if the structure has links that place children far from it — the value must cover the whole linked span, or those far children won’t generate. |
surface-snap | none | How the structure is positioned vertically relative to the terrain surface. none keeps its authored Y; on_top snaps it to rest one block above the surface; embedded snaps it so its top row is flush with the surface (sunk one block into the ground). |
air-blocks | none | How the structure’s own AIR cells treat existing terrain. none leaves terrain intact (the air is transparent); all carves the air space out of the ground; except_bottom carves it out but keeps terrain under the structure’s bottom row (useful for a room without a floating floor). |
fill-to-surface | false | After placing, extends each solid column straight down to the ground so the structure doesn’t sit on stilts. Independent of surface-snap. |
placement | normal | normal uses the dense per-chunk scan (best for small, common structures). landmark uses the sparse region-grid pass for large, rare set-pieces (super-trees, boss arenas) so their size doesn’t slow generation everywhere — see Landmark Structures below. |
selection | rare | Landmark-only. rare keeps the spawn-chance roll (a region may have none). guaranteed bypasses the roll and places exactly one per spacing cell wherever a valid anchor exists. singular places exactly one in the whole world, in a seeded ring around origin (see distance), with an absolute guarantee — see Landmark Structures. Ignored when placement: normal. |
distance (min, max) | 300 / 2500 | For selection: singular only. The ring of distances, in blocks from world origin, the one instance is placed within (|x| ∈ [min, max], side chosen from the seed). Ignored for every other selection/placement mode (setting it elsewhere logs a warning). max below min collapses to a zero-width ring at min (warned). |
anchor-altitude | average | Landmark-only, and only with surface-snap: none. How a wide body’s vertical anchor is derived from the terrain sampled across its whole footprint: average (mean surface), min (lowest surface point — the body rises through higher terrain; pair with clear-above), or max (highest point — pair with fill-to-surface). |
clear-above | false | Landmark-only. Clears (sets to air) terrain that rises above the anchor within the body’s footprint columns, up to the body’s top, so a hill doesn’t bury it. Foreground (collision) layer only. |
entity-placements | (none) | Entities to spawn alongside the structure — see Entity & Static Object Placements below. |
static-object-placements | (none) | Static objects (interactables, trees) to spawn alongside the structure — see below. |
Spawn Types
Which values are valid depends on which world type (default/surface, cave, sky island) the structure’s biome belongs to:
| Value | World | Where |
|---|---|---|
surface | Default | On the terrain surface. |
anywhere | Default | No placement restriction. |
cave | Default | Inside a cave’s open space. |
cave_floor | Default | On a cave’s floor. |
island_surface | Sky Island | On top of a sky island. |
island_interior | Sky Island | Inside a sky island’s solid body. |
island_underside | Sky Island | On the underside of a sky island. |
void | Sky Island | In the open air between and under the islands — the void gulf. See Void-Spanning Structures below. |
A spawn-type outside this list (a typo, or a value valid only in a different world type) parses without error but never spawns anything — the engine logs a warning naming the structure and listing the valid values when it loads the config, so check the server log if a structure isn’t appearing.
Landmark Structures
placement: landmark is for large, sparse set-pieces — super-trees, boss arenas, hand-authored monuments that span many chunks — as opposed to the small, common structures the default normal placement handles. It changes three things:
- Cost isolation. A normal structure’s size widens the neighbourhood scan run for every chunk in the world, so one huge normal structure would slow generation everywhere. A landmark is excluded from that global radius and is instead discovered through its own coarse
spacinggrid — so its size costs nothing outside the regions it can actually appear in. Keep a landmark’sspacingmuch larger than itsmax-size(region-scale — e.g.spacing: 512for a body a couple hundred blocks wide); aspacingsmaller than the body defeats the isolation and logs a warning. - How many, and how strong a guarantee.
selection: rarekeeps the ordinaryspawn-chanceroll for landmarks that should be occasional finds.selection: guaranteedplaces exactly one perspacingcell (its location still varies with the seed — the “authored content, procedural placement” model), scanning the cell for the first valid surface anchor so a cave mouth or biome edge at the canonical spot doesn’t lose it; it is best-effort (a cell that genuinely can’t host it yields nothing).selection: singularplaces exactly one in the entire world — the right mode for a unique set-piece a boss lair, a world portal, anything a second copy of would break. Its one instance sits in a seeded ringdistance: { min, max }blocks from origin (default300–2500), and its guarantee is absolute: if the ring target is unsuitable the engine escalates — scan the target cell, then widen outward, then relax the biome filter, and finally manufacture a site (clearing/filling terrain) so placement can never fail. Each escalation past the ordinary cell scan is logged, so a world that had to reach for a fallback is visible in the server log rather than silent. - Footprint fit. With
surface-snap: none(rigid placement),anchor-altitudederives the anchor from the surface across the whole footprint (not one column), andclear-abovecarves away any hill that would bury the body.fill-to-surfacealready extends each column down to its own local surface.
structures:
- name: greatbough # a colossal multi-chunk super-tree
spawn-type: surface
placement: landmark
selection: guaranteed # one per region, always
spacing: { x: 512, y: 512 } # region-scale — much larger than the body
max-size: { width: 180, height: 320 }
anchor-altitude: min # base at the lowest surface under the footprint...
clear-above: true # ...and carve the hill it rises through
fill-to-surface: true
biomes:
- YourMod:smotherwood
structures:
- name: border_seal_lair # a unique boss lair — exactly one in the world
spawn-type: surface
placement: landmark
selection: singular
distance: { min: 300, max: 2500 } # somewhere in this ring around origin (seed picks where)
spacing: { x: 512, y: 512 }
max-size: { width: 120, height: 160 }
fill-to-surface: true # so a manufactured site (last-resort rung) reads cleanly
biomes:
- YourMod:blighted_reach
guaranteedis best-effort per cell;singularis absolute. Aguaranteedcell whose terrain genuinely can’t host the structure (no valid surface, wrong biome throughout) yields nothing there, logged as a warning.singularcannot fail: it escalates (widen the ring → relax the biome → manufacture a site) until it places, logging each step it had to take.singularworks for every spawn type —surface,cave/cave_floor, andisland_surface/island_underside/island_interior/void. A singular structure that falls to the manufacture-a-site rung brings its own footing: a cave lair carves its pocket viaair-blocks; an island set-piece supplies its own platform in its authored geometry (setfill-to-surface/clear-aboveon a surface lair so a fabricated ground reads cleanly). Noteguaranteed(as opposed tosingular) oncave/islandstill only bypasses the roll without a cell scan.
A singular landmark reserves its footprint and wins every conflict. Any recurring landmark (
selection: rareorguaranteed) whose body would overlap a singular structure’s footprint is dropped there — the singular boss lair never has a flavour ruin punched through it. The reservation is a safe over-estimate based onmax-size, so leave a singular landmark’smax-sizeaccurate. This only governs landmark-vs-landmark: two recurring landmarks can still overlap each other (give overlapping-prone recurring landmarks distinct biomes), and smallplacement: normalstructures are not pushed out of a lair’s footprint.
Landmarks are for monolithic set-pieces, not link chains. A landmark should be a single authored body (a super-tree, an arena). If a structure uses links that place children far from the anchor, do not make it a landmark: landmarks are excluded from the global scan radius (that is what keeps them cheap), so raising their
max-sizeno longer widens the range that guarantees far-placed link children generate — the far end of a long chain can then fail to appear depending on which direction chunks stream in from. Keep linked, spread-out structures onplacement: normaland set theirmax-sizeto cover the whole linked span, as usual.
Void-Spanning Structures
spawn-type: void places an authored structure into the open air between and under sky islands — the void gulf — rather than on or inside island matter. It is the vehicle for large set-pieces strung across the emptiness of a sky-island world: a boss arena hung in the deeps, or web/bridge geometry spanning the gaps between isles.
Unlike the island_* types, which scan for solid island matter to attach to, a void anchor requires an open-air cell within the feature’s altitude band — so the structure lives in the archipelago’s vertical space, not in infinite empty sky. The vertical home of the structure is the sky-island feature’s own min-altitude/max-altitude band, which is what controls where the void set-piece lands:
- Register the structure on a low “gulf” feature (a feature whose altitude band sits below the walkable isles) and it hangs in the deep gulf under the archipelago.
- Register it on the surface archipelago feature and it fills the open gaps between the isles.
The span itself comes from the structure’s own authored .structure geometry — the engine drops the set-piece into a valid void cell; the bridges, platforms, and web that reach from isle to isle are painted into the structure. Pair void with placement: landmark so a large arena is placed sparsely and cost-isolated (see Landmark Structures above), exactly as a surface super-tree is.
structures:
- name: web_gulf_arena # a multi-chunk arena strung across the void
spawn-type: void
placement: landmark
selection: guaranteed # one per region
spacing: { x: 512, y: 512 } # region-scale — much larger than the body
max-size: { width: 300, height: 400 }
biomes:
- YourMod:web_gulf
A void set-piece composes with a world’s void-floor-y death plane: keep the arena’s floor above that Y (via the hosting feature’s altitude band) so the lethal void sits below the playable geometry, not through it.
Void anchoring is single-cell, like the
island_*types: eligibility is tested at the anchor cell only, so a very wide body could overlap island matter at its edges even when the anchor is clear. Give a void arena its own feature/biome band that no isles occupy, as you would keep overlapping landmarks in distinct biomes. Footprint-wide clearance verification is a possible future refinement.
Entity & Static Object Placements
Both are a map keyed by marker id, and each id’s value is a one-of: list of candidates:
static-object-placements:
chest_nook: # a marker id you painted in the editor
one-of:
- static-object-type: interactable
name: chest
loot-table: test
loot_corner: # a second marker id
one-of:
- static-object-type: interactable
name: barrel
weight: 4 # ...one of these two is picked, 80% barrel
- static-object-type: interactable
name: crate
weight: 1 # 20% crate
Assigning ids in the editor: place a spawn/extender marker block, hover it, and press F3 to type its id (Enter confirms, Esc cancels, an empty id clears it). The hovered marker’s current id shows by the cursor. Ids are saved into the
.structurefile on export.
The key is the marker’s id, painted per marker in the structure editor and stored in the .structure file — not a probability table, and no longer positional:
- Each spawn-marker block carries an id you assign in the editor. A placement entry binds to the marker with the matching id, so moving or reordering markers in the editor never reshuffles bindings (the old behaviour, when groups were a positional list bound to the Nth marker in scan order). A marker whose id no entry references — or that was left unnamed — spawns nothing and logs a warning; an entry whose id matches no marker simply never fires.
- Every marker always spawns exactly one entry — which is what
one-of:names. If an id lists more than one candidate, one is chosen at random (a deterministic per-location roll), in proportion to each candidate’sweight— letting a single marker spawn a weighted pick from a set (e.g. the 80/20 barrel-or-crate above). - Each candidate takes an optional
weight(default1.0): its share of the marker’s single draw, relative to the other candidates. It is not a probability —4vs1and0.8vs0.2are the same 80/20 split. Omittingweighton every candidate makes the pick uniform (the previous behaviour).weight: 0opts a candidate out; if every candidate is0the marker spawns nothing. A placement entry takes nochance— that is anany-of:concept, and writing it (or any other field the entry’s type doesn’t read, e.g.default-stateon atree) is a load-time error naming the key.
one-of:here vs. in a structure link. Placements only ever useone-of:(a weighted single pick — the same weighting a link’sone-of:uses); there is noany-of:for placements, since a marker is a single cell that spawns exactly one thing. A link group, by contrast, chooses betweenone-of:(exactly one, byweight) andany-of:(each rolls its ownchance) — seeany-of:vs.one-of:.
Each entry needs an entity-type (entity-placements) or static-object-type (static-object-placements) discriminator plus that type’s own reference fields, and optionally a loot-table (see Loot Table Configuration) to fill that instance’s inventory on spawn. A loot-table naming a table no loaded mod defines (e.g. a typo) is warned about at startup and leaves that instance’s inventory empty.
The reference field is not spelled the same on both sides. A static-object placement names its object with name; an entity placement of entity-type: mob names its mob with entity-name:
The accepted keys depend on the discriminator, and the list below is exhaustive — anything else fails the entry at load:
| Placement | Discriminator | Names the content with | Also accepts |
|---|---|---|---|
static-object-placements | static-object-type: tree | name | loot-table, weight |
static-object-placements | static-object-type: interactable | name | loot-table, weight, collidable, default-state, interact-cooldown-ms |
entity-placements | entity-type: mob | entity-name | loot-table, weight, spawn-group, display-name, health, oxygen |
entity-placements | entity-type: player / item_drop | (nothing — the type is the whole reference) | loot-table, weight |
weight (default 1.0) is the candidate’s share of its marker’s single one-of: draw — see the bullet list above.
The extra keys on an interactable placement, and health / oxygen on a mob, override that object’s catalog values for this placed instance only, leaving interactable-config.yaml / mob-config.yaml untouched. Note that collidable, default-state and interact-cooldown-ms are legal only on an interactable — writing one on a tree is an error, not a no-op.
A mob placement’s optional spawn-group overrides which natural-spawn pool this spawned instance counts against, defaulting to Neutral rather than to whatever the mob’s own catalog entry declares — set it explicitly if the structure’s mobs should count against the same pool as naturally-spawned ones. See spawn-group.
Structure Links
structure-links lets one placed structure spawn additional connected structures at fixed offsets — useful for branching layouts (a cactus with random limb variations, a village with a random number of houses) that a single fixed .structure file can’t express:
structure-links:
- name: cactus_base
base-path: "/CACTUS/"
links:
arms: # the extender marker's id
any-of:
- link: cactus_left_1
path: "/CACTUS_LIMBS/CACTUS_LEFT_1.structure"
chance: 0.10
- link: cactus_right_1
path: "/CACTUS_LIMBS/CACTUS_RIGHT_1.structure"
chance: 1.0
name matches a structure-spawn point from one of the base structures: entries (or another link’s own link name, allowing chains). base-path is a folder prefix prepended to every path below it. links is a map keyed by the extender-marker id each group attaches at — paint an id on each structure-extender marker in the editor, then key its group here. (This replaces the old positional list, where the Nth group bound to the Nth extender marker in scan order and silently reshuffled if you moved a marker.) A marker whose id no group references grows nothing; a group whose id matches no extender marker never attaches.
Offsets are derived from the marker by default. A child attaches with its own spawn point on the parent’s extender marker, so you usually omit x-offset/y-offset entirely — set them only to nudge the child off that connection point.
any-of: vs. one-of:
Each group picks one of two rules, and the wrapper key you write is the choice. They are not interchangeable, and each accepts a different per-entry field:
| Group | Attaches | Per-entry field | Use for |
|---|---|---|---|
any-of: | zero, one, or several — each candidate rolls alone | chance (default 1.0) | candidates that don’t exclude each other |
one-of: | always exactly one, drawn in proportion to weight | weight (default 1.0) | mutually exclusive candidates |
Writing weight inside an any-of:, or chance inside a one-of:, is a load-time error naming the key — the field would otherwise look like it was doing something it isn’t.
If this feels familiar, it’s the same split a drop-table makes between its drops: (independent chance per entry) and its groups: (weighted pick-one), down to each key being rejected on the wrong side.
any-of: — each link’s chance is simply its own spawn probability, so chance: 1.0 always attaches and 0.0 never does. The cactus above uses this: a left limb 10% of the time, a right limb always, independently.
one-of: — exactly one candidate always attaches, so there is no “nothing happened” outcome. weight is relative within the group, not a probability: 0.9 against 0.1 means 90%/10%, and so does 9 against 1. A single-candidate one-of: therefore always attaches that candidate. weight: 0 opts a candidate out; if every candidate is 0 the junction grows nothing.
This is what lets a chain terminate deliberately. A road segment that either continues or is capped by a house is mutually exclusive, so it needs one-of: — with any-of: the two rolls are independent, and the common outcome is that neither fires and the chain just stops with no house at all:
- name: village
base-path: "/VILLAGE/"
links:
road_ahead: # extender marker where the road continues
one-of: # continue the road, or cap it — never both, never neither
- link: left_house
path: "/HOUSES/LEFT_HOUSE.structure"
weight: 0.1 # 10% -> the chain ends here, in a house
- link: village
path: "VILLAGE.structure"
weight: 0.9 # 90% -> another road segment; mean run ~10
surface-snap: embedded
road_end: # a second extender marker
one-of:
- link: right_house # sole candidate -> the far end is always a house
path: "/HOUSES/RIGHT_HOUSE.structure"
Both rules are deterministic per world seed and position, so a given seed always reproduces the same layout.
A link may point back at its own parent (a self-link) to build repeating structures like the village road above — but a self-link that always attaches would chain endlessly. Give it a real chance of stopping: in an any-of: group that means a chance below 1.0, and in a one-of: group it means at least one sibling with a non-zero weight (a lone self-link in a one-of: never terminates, since exactly one candidate always attaches). Generation is bounded by the parent structure’s max-link-depth (default 64 hops) plus a hard total cap, so a runaway chain is stopped rather than hanging the world; raise max-link-depth only for intentionally deep chains.
| Field | Default | Description |
|---|---|---|
link | (required) | A name for this specific link instance. |
path | (required) | The .structure file to place, relative to base-path. |
chance | 1.0 | any-of: groups only. Probability (0.0–1.0) this specific link spawns, rolled independently of its siblings. An error inside a one-of: group. |
weight | 1.0 | one-of: groups only. This candidate’s share of its group’s single draw, relative to its siblings (not a probability — 0.9 vs 0.1 and 9 vs 1 are the same split). 0 opts it out. An error inside an any-of: group. |
x-offset / y-offset | 0 each | Optional nudge from the connection point. By default the child attaches with its own spawn point on the parent’s extender marker (offset derived from the marker), so you only set these to shift it off that point. |
surface-snap | none | Same meaning as the base structure field above (none / on_top / embedded). |
air-blocks | none | Same meaning as the base structure field above (none / all / except_bottom). |
fill-to-surface | false | Same meaning as the base structure field above. |
Complete Example
Based on the real shipped structures:
structures:
- name: cactus
spawn-type: surface
spacing:
x: 5
y: 5
spawn-chance: 0.7
max-size:
width: 5
height: 10
biomes:
- desert
- name: base
spawn-type: cave_floor
spacing:
x: 40
y: 40
spawn-chance: 0.5
air-blocks: except_bottom
max-size:
width: 20
height: 20
static-object-placements:
vault: # marker id painted on the chest's spawn marker
one-of:
- static-object-type: interactable
name: chest
loot-table: test
- name: village
spawn-type: surface
biomes:
- plains
spacing:
x: 50
y: 50
spawn-chance: 0.6
max-size:
width: 30
height: 30
surface-snap: embedded
structure-links:
- name: village
base-path: "/VILLAGE/"
links:
road_ahead:
one-of:
- link: left_house
path: "/HOUSES/LEFT_HOUSE.structure"
weight: 0.1
- link: village
path: "VILLAGE.structure"
weight: 0.9
surface-snap: embedded
road_end:
one-of:
- link: right_house
path: "/HOUSES/RIGHT_HOUSE.structure"
Last updated