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. |
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. |
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.
Entity & Static Object Placements
Both are a list of groups, each group a one-of: list of candidates:
static-object-placements:
- one-of:
- static-object-type: interactable # group 0 -> the 1st spawn marker
name: chest
loot-table: test
- one-of:
- static-object-type: interactable # group 1 -> the 2nd spawn marker
name: barrel
- static-object-type: interactable # ...one of these two is picked at random
name: crate
The grouping is positional, not a probability table — this is different from a block drop-table:
- Each group binds to a spawn-marker block you place inside the
.structurefile, in the order the markers are encountered (top-to-bottom, left-to-right). Group0fills the first marker, group1the second, and so on. There must be at least as many groups as markers of that kind (a marker with no matching group is skipped with a warning, not a crash). - Every marker always spawns exactly one entry — which is what
one-of:names. If a group lists more than one candidate, one is chosen uniformly at random (a deterministic per-location roll), letting a single marker spawn a random pick from a set (e.g. barrel-or-crate above). - A placement group takes no
chanceand noweight— the pick is always uniform. Writing either on a placement entry is a load-time error naming the key, as is any other field that entry’s type doesn’t read (default-stateon atree, say).
one-of:here vs. in a structure link. Placements only ever useone-of:, and always weight candidates equally. A link group 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 |
static-object-placements | static-object-type: interactable | name | loot-table, collidable, default-state, interact-cooldown-ms |
entity-placements | entity-type: mob | entity-name | loot-table, spawn-group, display-name, health, oxygen |
entity-placements | entity-type: player / item_drop | (nothing — the type is the whole reference) | loot-table |
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:
- any-of:
- link: cactus_left_1
path: "/CACTUS_LIMBS/CACTUS_LEFT_1.structure"
chance: 0.10
x-offset: 1
- link: cactus_right_1
path: "/CACTUS_LIMBS/CACTUS_RIGHT_1.structure"
chance: 1.0
x-offset: -1
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 list of groups, one group per connection point (structure-extender marker) on the parent.
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:
- 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
x-offset: 2
- link: village
path: "VILLAGE.structure"
weight: 0.9 # 90% -> another road segment; mean run ~10
x-offset: 1
surface-snap: embedded
- one-of:
- link: right_house # sole candidate -> the far end is always a house
path: "/HOUSES/RIGHT_HOUSE.structure"
x-offset: -1
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 | Block offset from the connection 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:
- 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:
- one-of:
- link: left_house
path: "/HOUSES/LEFT_HOUSE.structure"
weight: 0.1
x-offset: 2
- link: village
path: "VILLAGE.structure"
weight: 0.9
x-offset: 1
surface-snap: embedded
- one-of:
- link: right_house
path: "/HOUSES/RIGHT_HOUSE.structure"
x-offset: -1