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

FieldDefaultDescription
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-chance1.0Probability (0.01.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-inversefalseSelects the opposite end of the per-cell random range, so the structure lands on a different set of cells at the same densityspawn-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 eachSpacing, 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-depth64Maximum 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-snapnoneHow 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-blocksnoneHow 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-surfacefalseAfter 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:

ValueWorldWhere
surfaceDefaultOn the terrain surface.
anywhereDefaultNo placement restriction.
caveDefaultInside a cave’s open space.
cave_floorDefaultOn a cave’s floor.
island_surfaceSky IslandOn top of a sky island.
island_interiorSky IslandInside a sky island’s solid body.
island_undersideSky IslandOn 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 .structure file, in the order the markers are encountered (top-to-bottom, left-to-right). Group 0 fills the first marker, group 1 the 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 chance and no weight — 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-state on a tree, say).

one-of: here vs. in a structure link. Placements only ever use one-of:, and always weight candidates equally. A link group chooses between one-of: (exactly one, by weight) and any-of: (each rolls its own chance) — see any-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:

PlacementDiscriminatorNames the content withAlso accepts
static-object-placementsstatic-object-type: treenameloot-table
static-object-placementsstatic-object-type: interactablenameloot-table, collidable, default-state, interact-cooldown-ms
entity-placementsentity-type: mobentity-nameloot-table, spawn-group, display-name, health, oxygen
entity-placementsentity-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 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:

GroupAttachesPer-entry fieldUse for
any-of:zero, one, or several — each candidate rolls alonechance (default 1.0)candidates that don’t exclude each other
one-of:always exactly one, drawn in proportion to weightweight (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.

FieldDefaultDescription
link(required)A name for this specific link instance.
path(required)The .structure file to place, relative to base-path.
chance1.0any-of: groups only. Probability (0.01.0) this specific link spawns, rolled independently of its siblings. An error inside a one-of: group.
weight1.0one-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-offset0 eachBlock offset from the connection point.
surface-snapnoneSame meaning as the base structure field above (none / on_top / embedded).
air-blocksnoneSame meaning as the base structure field above (none / all / except_bottom).
fill-to-surfacefalseSame 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