Structures & jigsaw

The full custom-structure pipeline: the structure definition, the structure set that places it, the template pool and processor list that assemble its pieces, and the .nbt builds plus the structure-block / jigsaw-block workflow behind them. Primary syntax targets 1.21+ (singular worldgen/structure/ and structure/ folders).

Structure definition

The structure definition JSON at data/<namespace>/worldgen/structure/ is distinct from the .nbt build files (see below). It declares which biomes the structure may generate in, its generation step, mob-spawn overrides, terrain adaptation, and β€” for jigsaw structures β€” the starting template pool and expansion limits.

worldgen/structure β€” jigsaw root schema shape

{
  "type": "minecraft:jigsaw",
  "biomes": "#minecraft:has_structure/village_plains",
  "step": "surface_structures",
  "terrain_adaptation": "beard_thin",
  "spawn_overrides": {},
  "start_pool": "example:village/town_centers",
  "size": 6,
  "start_height": { "absolute": 0 },
  "project_start_to_heightmap": "WORLD_SURFACE_WG",
  "max_distance_from_center": 80,
  "use_expansion_hack": false
}

Structure types

One data-driven type plus fifteen hardcoded types. Most custom work uses jigsaw.

KindTypes
Data-drivenjigsaw β€” villages, outposts, bastions, ancient cities, trail ruins, trial chambers, and all custom structures.
Hardcoded (15)buried_treasure, desert_pyramid, end_city, fortress, igloo, jungle_temple, mineshaft, nether_fossil, ocean_monument, ocean_ruin, ruined_portal, shipwreck, stronghold, swamp_hut, woodland_mansion

Hardcoded types accept the common fields below plus a few type-specific ones β€” e.g. mineshaft has mineshaft_type + probability; ocean_ruin has biome_temperature, large_probability, cluster_probability; ruined_portal has a setups[] list; shipwreck has is_beached; nether_fossil has height.

Common fields (all structure types)

FieldNotes
typeOne of the types above.
biomesA biome id, an inline array, or a #tag. Structure biome tags live in tags/worldgen/biome/.
stepGeneration step enum β€” surface_structures is typical. (Full enum: raw_generation, lakes, local_modifications, underground_structures, surface_structures, strongholds, underground_ores, underground_decoration, fluid_springs, vegetal_decoration, top_layer_modification.)
spawn_overridesPer-mob-category spawn rules (optional). See below.
terrain_adaptationnone, beard_thin, beard_box, bury, or encapsulate. See below.

spawn_overrides is an object keyed by mob category (monster, creature, ambient, water_creature, water_ambient, misc, underground_water_creature, axolotls). Each entry sets a bounding_box (piece or full) and a spawns list:

spawn_overrides β€” one category

{
  "monster": {
    "bounding_box": "piece",
    "spawns": [ { "type": "minecraft:pillager", "weight": 1, "minCount": 1, "maxCount": 2 } ]
  }
}
🎩 Trick

An empty spawns list suppresses natural spawns inside the box β€” the Ancient City / Trial Chamber "no mobs wander in" trick.

terrain_adaptation blends the structure into surrounding terrain, and the adaptation counts against max_distance_from_center:

ValueEffect
noneDo nothing.
beard_thinLight blending under/around pieces.
beard_boxHeavy blending.
buryEncase in the dimension's base block (used by strongholds).
encapsulateFully surround (used by trial chambers).

Jigsaw-specific fields

FieldType / values
start_poolTemplate pool id (must contain ≥ 1 element) β€” the first piece.
sizeInt 0–20 (max jigsaw recursion depth; villages use 6).
start_heightHeight provider (see projection behavior below).
project_start_to_heightmapHeightmap enum, or omit to disable surface projection.
max_distance_from_centerInt; horizontal Chebyshev cap. Hard max 128 (1–128 if terrain_adaptation: none, ≤ 116 otherwise).
use_expansion_hackBool β€” legacy vertical-expansion behavior.
pool_aliasesList β€” redirect pool names at generation time (see below).
dimension_padding{ "bottom": n, "top": n } β€” keep pieces away from world limits.
liquid_settingsapply_waterlogging (default) or ignore_waterlogging.

start_height × project_start_to_heightmap interaction:

pool_aliases entries take one of three shapes:

pool_aliases β€” direct, random, random_group

{ "type": "minecraft:direct", "alias": "example:target", "target": "example:pool_a" }

{ "type": "minecraft:random", "alias": "example:target",
  "targets": [ { "data": "example:pool_a", "weight": 1 }, { "data": "example:pool_b", "weight": 2 } ] }

{ "type": "minecraft:random_group",
  "groups": [ { "data": [ /* list of direct/random aliases */ ], "weight": 1 } ] }

data/example/worldgen/structure/tall_tower.json (single-piece surface structure)

{
  "type": "minecraft:jigsaw",
  "biomes": "#minecraft:has_structure/mineshaft",
  "step": "surface_structures",
  "terrain_adaptation": "beard_thin",
  "start_pool": "example:tall_tower",
  "size": 1,
  "start_height": { "absolute": 0 },
  "project_start_to_heightmap": "WORLD_SURFACE_WG",
  "max_distance_from_center": 80,
  "use_expansion_hack": false,
  "spawn_overrides": {}
}
⚠ Version differences
  • Since 1.19 (22w11a): the consolidated jigsaw type was introduced, merging the old village / pillager_outpost / bastion_remnant configured-structure-feature types; the registry moved from configured_structure_feature to structure.
  • Since 1.21 (pack_format 48): folder is singular worldgen/structure/; dimension_padding, liquid_settings, pool_aliases, and split horizontal/vertical distance handling are available.
  • size max: was 7 in ≤ 1.20.2, raised to 20 from 1.20.3+.
  • Structure biome tags live in tags/worldgen/biome/ β€” not tags/biome/.
πŸ’‘ Best practice
  • Single-build structure: size: 1, a start_pool with one single_pool_element, and a start piece containing one downward/heightmap-facing jigsaw β€” no other pieces needed.
  • Every jigsaw structure gets a random rotation even with aligned joints β€” design relative-coordinate mechanics (functions, puzzles) to be rotation-agnostic behind an anchor block.
  • Reduce max_distance_from_center / size if a sprawling structure hurts chunk-load performance.

Structure set

A structure set at data/<namespace>/worldgen/structure_set/ controls where and how often a group of structure definitions is attempted. Biome validity is not here β€” that lives on each structure. Vanilla groups same-frequency structures (all village biome variants) into one set.

worldgen/structure_set β€” root schema shape

{
  "structures": [ { "structure": "example:tall_tower", "weight": 1 } ],
  "placement": { "type": "minecraft:random_spread", "salt": 1234567, "spacing": 32, "separation": 8 }
}

Fields

structures[] β€” each { "structure": <id>, "weight": <positive int> }. On a successful attempt, one is chosen by weight among those whose biome/conditions pass.

placement common fields:

FieldNotes
typeminecraft:random_spread or minecraft:concentric_rings.
saltNon-negative int seed offset (unique per set).
frequencyFloat 0–1 (default 1) β€” attempt keep-chance.
frequency_reduction_methoddefault, legacy_type_1, legacy_type_2, legacy_type_3.
locate_offset[x,y,z] chunk offset, each −16..16.
exclusion_zone{ "other_set": <id>, "chunk_count": 1–16 } β€” fail if too near another set.

random_spread extras: spacing (0–4096 chunks, average grid cell), separation (0–4096, minimum gap, must be < spacing), spread_type (linear default, or triangular).

concentric_rings extras (used for strongholds): distance (0–1023, ring spacing in 6-chunk units), count (1–4095 total placements), spread (0–1023, placements on the nearest ring), preferred_biomes (biome ids / #tags).

data/example/worldgen/structure_set/towers.json

{
  "structures": [
    { "structure": "example:tall_tower", "weight": 3 },
    { "structure": "example:short_tower", "weight": 1 }
  ],
  "placement": {
    "type": "minecraft:random_spread",
    "salt": 84920155,
    "spacing": 34,
    "separation": 8,
    "spread_type": "linear",
    "frequency": 0.6,
    "exclusion_zone": { "other_set": "minecraft:villages", "chunk_count": 8 }
  }
}
⚠ Version differences
  • Structure sets were introduced with the 1.18 worldgen rewrite; the schema is stable through 1.21.8.
πŸ’‘ Best practice
  • Give every custom set a unique large salt β€” reused salts make different structures land on identical chunks.
  • Lower spacing / separation = denser; always keep separation < spacing or generation errors.
  • exclusion_zone is how vanilla keeps pillager outposts away from villages.
  • /locate structure <id> searches by structure definition, independent of the set.

Template pool

A template pool at data/<namespace>/worldgen/template_pool/ is "a loot table for builds." A jigsaw's start_pool and each jigsaw block's Target Pool point at one of these. It weights the candidate pieces and supplies a fallback.

FieldNotes
fallbackAnother pool id used when elements can't fit or size is exhausted (an end-cap); minecraft:empty if none.
elements[]Each { "weight": <int 1–150>, "element": { ... } }.
nameOptional β€” ignored on load; the file path is the id.

element_type (inside each element):

TypeFields
single_pool_elementlocation (NBT id), processors (list id/inline), projection, optional override_liquid_settings.
legacy_single_pool_elementAs single; legacy structure-block block handling.
list_pool_elementelements[] (list of pool elements placed together), projection.
feature_pool_elementfeature (placed feature id/inline), projection.
empty_pool_element(nothing to place) β€” terminator.

projection: rigid (piece stays flat, ignores terrain β€” houses, start piece) or terrain_matching (each column drops to follow terrain β€” village roads).

data/example/worldgen/template_pool/tall_tower.json

{
  "name": "example:tall_tower",
  "fallback": "minecraft:empty",
  "elements": [
    { "weight": 3, "element": {
        "element_type": "minecraft:single_pool_element",
        "location": "example:tower/body",
        "processors": "example:mossify",
        "projection": "rigid" } },
    { "weight": 1, "element": {
        "element_type": "minecraft:single_pool_element",
        "location": "example:tower/body_ruined",
        "processors": "minecraft:empty",
        "projection": "rigid" } }
  ]
}
⚠ Version differences
  • list_pool_element, legacy_single_pool_element, and override_liquid_settings are Java-only; the schema is stable across 1.19 → 1.21.8.
πŸ’‘ Best practice

Provide a small fallback pool (a blank wall / cap) so truncated jigsaw chains don't look sheared off. Use terrain_matching only for pieces meant to melt into the ground (roads); everything else rigid, especially the start piece.

Processor list

A processor list at data/<namespace>/worldgen/processor_list/ is an ordered set of block transformations applied as a template piece is stamped into the world (weathering, block swaps, gravity settling, loot injection). Pool elements reference it via processors. The root is a processors array-holder object:

worldgen/processor_list β€” root schema shape

{ "processors": [ { "processor_type": "minecraft:block_ignore", "blocks": [ { "Name": "minecraft:structure_void" } ] } ] }

Processor types

processor_typeFields
block_ignoreblocks[] (block states to skip placing).
block_rotintegrity (0–1 keep-fraction), optional rottable_blocks (block list).
gravityheightmap, offset (int) β€” settle each column to a heightmap.
jigsaw_replacementnone β€” convert leftover jigsaw blocks to air.
rulerules[] (see below).
block_agemossiness (float) β€” random weathering / cobweb / cracks.
blackstone_replacenone β€” swap cobblestone-family → blackstone-family.
lava_submerged_blocknone β€” waterlogged / air-in-lava handling.
protected_blocksvalue (block #tag that must never be overwritten).
cappeddelegate (a processor), limit (int provider β€” max applications).
nopnone.

Rules & rule tests

Each entry in a rule processor's rules[]: input_predicate tests the block in the template; location_predicate tests the block already in the world at that spot; if both pass, output_state is written.

rule β€” one entry

{
  "input_predicate":       { "predicate_type": "minecraft:always_true" },
  "location_predicate":    { "predicate_type": "minecraft:block_match", "block": "minecraft:red_sand" },
  "position_predicate":    { "predicate_type": "minecraft:always_true" },
  "output_state":          { "Name": "minecraft:red_sandstone" },
  "block_entity_modifier": { "type": "minecraft:passthrough" }
}

Rule tests (predicate_type β€” shared with ore targets):

Predicate typeFields
always_trueβ€”
block_matchblock
blockstate_matchblock_state
tag_matchtag (no leading #)
random_block_matchblock, probability
random_blockstate_matchblock_state, probability

Position predicates (the optional position_predicate): always_true, linear_pos (min_chance, max_chance, min_dist, max_dist), axis_aligned_linear_pos (adds axis).

block_entity_modifier types: clear, passthrough (keep existing NBT), append_static (data compound merged in), append_loot (loot_table β€” sets a LootTable so the container fills on first open).

data/example/worldgen/processor_list/mossify.json

{
  "processors": [
    { "processor_type": "minecraft:block_age", "mossiness": 0.2 },
    { "processor_type": "minecraft:rule",
      "rules": [
        { "input_predicate": { "predicate_type": "minecraft:block_match", "block": "minecraft:sand" },
          "location_predicate": { "predicate_type": "minecraft:block_match", "block": "minecraft:red_sand" },
          "output_state": { "Name": "minecraft:red_sand" } }
      ] }
  ]
}
⚠ Version differences
  • block_rot, block_age, gravity, blackstone_replace, lava_submerged_block, and nop are Java-only; the set is stable across 1.18 → 1.21.8.
  • capped (delegate + limit int provider) and protected_blocks are present in 1.21.
🎩 Trick
  • append_loot in a rule's block_entity_modifier is how structures ship unfilled chests.
  • protected_blocks with a custom tag prevents a processor or terrain from overwriting hand-placed key blocks.
  • Wrap a heavy processor in capped to bound how many blocks it touches per piece.

Structure NBT & jigsaw workflow

Keep the two "structure" things straight:

Structure Block workflow

  1. /give @s structure_block. The placed block defaults to Load mode; click Load twice to reach Save mode.
  2. Set a Structure Name (namespace:path, lowercase / no spaces), a Relative Position (e.g. 1 0 1), and a Structure Size (max 48×48×48; extends in +X/+Y/+Z).
  3. Toggle Include Entities if display entities / mobs are inside (they must sit ≥ 0.01 blocks inside the box or they're dropped). Redstone state is not saved.
  4. Press SAVE β€” the file is written under the world's generated/<namespace>/structure(s)/. Merge it into the data pack.
  5. Use Structure Void (/give @s structure_void) at the build's edges so natural terrain / foliage shows through instead of air. (Build with sponge as a placeholder, swap to voids last β€” voids have a tiny hitbox.)

Jigsaw blocks

/give @s jigsaw. Directional; the sideways interface shows:

FieldMeaning
Target PoolTemplate pool this jigsaw may spawn from (minecraft:empty if it's only a connector).
NameThis jigsaw's own name (what a parent's Target Name matches).
Target NameThe Name of the jigsaw in the child piece it should line up with.
Turns Into (final_state)Block that replaces the jigsaw after generation (usually minecraft:air; accepts blockstates).
Joint Type (up/down only)rollable (random rotation allowed) or aligned (fixed to the indicator line).
Selection / Placement Priority 1.20.4+Higher = generated first, so key children claim space before others.

Convention: pair a parent and child by swapping Name ↔ Target Name and namespacing both.

πŸ’‘ Best practice

Single-piece structure recipe: a .nbt build (with one downward or heightmap-facing jigsaw, Target Pool minecraft:empty) → a one-element template_pool (single_pool_element, location = the .nbt, projection: rigid) → a structure definition with size: 1 pointing start_pool at that pool → a structure_set with a random_spread placement. Done.

🚧 Workaround
  • Structure Block max is 48³; larger builds must be split across jigsaw-connected pieces.
  • A jigsaw child's bounding box cannot intersect any previously placed piece's box β€” put jigsaws at the very edge facing out, or keep an internally-spawned child small enough to fit.
  • Max horizontal reach from the start center is 128 blocks (also bounded by max_distance_from_center and terrain adaptation).
  • Max jigsaw steps = 20 (7 in ≤ 1.20.2); villages use ~6. Deep branching hurts chunk-load performance. Everything inherits the start piece's random rotation.
⚠ Version differences
  • Since 1.21: the build folder is singular structure/. Structure Blocks in ≤ 1.20 saved to structures/, so rename after exporting a pre-1.21 build.
  • Since 1.20.4: jigsaw blocks expose Selection / Placement Priority.

The /place command

Used for testing structure generation without waiting for natural spawns.

/place β€” the four subcommands

/place feature   <configured_feature_id> [<pos>]
/place structure <structure_id>          [<pos>]
/place jigsaw    <start_pool> <target_jigsaw_name> <max_depth> [<pos>]
/place template  <template_id> [<pos>] [<rotation>] [<mirror>] [<integrity>] [<seed>] [strict]
πŸ’‘ Best practice

Prototype spawning with /place structure and /locate structure before relying on natural generation. Keep a tiny fallback / end-cap piece in every pool so truncated chains look intentional, and store rotation-sensitive logic behind an anchor block/entity so mechanics survive the random rotation.

Sources