Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Chapter 41 — Structures and Structure Sets

What You’ll Build

In Chapter 40 you saved a small building to a .nbt structure file and learned to drop it into the world by hand with a structure block or /place. That building just sits in a folder until you tell the game to place it. In this chapter you make it appear on its own, scattered through freshly generated land, the same way villages and pillager outposts do.

To get there you’ll write four small JSON files in your mypack data pack:

  • a structure definition — says what the structure is and which biomes it may grow in;
  • a template pool — lists the saved piece (or pieces) the game may choose from;
  • a processor list — an optional step that ages or alters blocks as they place;
  • a structure set — decides where in the world copies show up, and how far apart.

By the end you’ll have a tiny outpost that generates in plains biomes, and you’ll have closed the loop from Chapter 34: the “forge cache” chest we promised back then can finally live inside a real, self-generating structure, with its chest pointed at the loot table you already wrote.

Figure (to be captured). a small outpost building standing in a plains biome, freshly generated, no command used

Heads up — this is the hard part of the book. World generation has more moving parts than anything you’ve built so far, and four files all have to agree with each other. We’ll go one file at a time, and the outpost we build uses a single saved piece so you can see the whole machine without drowning in it. Take it slowly; re-read a section if a field doesn’t click yet.

Concepts

What a structure actually is

A structure (the wiki also calls it a “generated structure” or “structure feature”) is a naturally-generated formation you can find with /locate and place by hand with /place. Villages, pillager outposts, ancient cities, ocean monuments: all structures. They will not appear if a world was created with the “Generate Structures” option turned off.

One detail matters for understanding when your structure shows up: structures are generated for a chunk after the terrain of that chunk has been formed. The ground is shaped first; the structure is dropped onto it afterward. That ordering is why a structure can ask the game to flatten or bury the terrain under it: the land is already there to adjust.

Four registries, one structure

Here’s the mental model. Four separate files, each in its own folder under data/mypack/worldgen/, work together:

FileFolderJob
Structure definitionworldgen/structure/What the structure is; which biomes; spawn rules
Template poolworldgen/template_pool/Which saved piece(s) to place
Processor listworldgen/processor_list/Block-by-block changes as it places (optional)
Structure setworldgen/structure_set/Where in the world, and how often

The structure definition points at a template pool. The template pool points at your .nbt file (and, optionally, at a processor list). The structure set points at the structure definition. Nothing points at the structure set, and that’s the surprising part. As the wiki puts it: a structure set is “not referenced in a dimension or biome. Instead, the existence of the resource is enough to make the structures generate.” Drop the file in, and the structure starts appearing.

Modern Minecraft. All four of these are dynamic registries, the same family as biomes, dimensions, and enchantments. Dynamic registries are read when the world loads. That means /reload does not update them. When you change a worldgen file you must exit the world and open it again. (And changes only affect newly generated chunks: land that already exists keeps whatever generated there the first time.) Keep this in your back pocket; it’s the number-one source of “why isn’t my change doing anything?” in this chapter.

Walkthrough

We’ll assume you finished Chapter 40 and have a saved building .nbt (there we saved cottage.nbt). This chapter uses one called outpost as its running example:

data/mypack/structure/outpost.nbt

If you only have your cottage.nbt (or any other small saved .nbt), that’s fine; just use its name in place of outpost below. We’ll build the four JSON files from the inside out: the pool first (it names your .nbt), then the processors, then the structure definition, then the structure set.

Step 1 — The template pool

A template pool is a group of structure pieces that the jigsaw system may choose from. A “piece” is usually one saved structure template; during generation the game randomly picks pieces from the pool. Pools are stored as JSON files in data/<namespace>/worldgen/template_pool.

Our outpost is a single building, so our pool has exactly one piece. Here’s the file:

data/mypack/worldgen/template_pool/outpost.json

{
  "fallback": "minecraft:empty",
  "elements": [
    {
      "weight": 1,
      "element": {
        "element_type": "minecraft:single_pool_element",
        "projection": "rigid",
        "location": "mypack:outpost",
        "processors": "minecraft:empty"
      }
    }
  ]
}

Field by field, straight from the Template pool format:

  • fallback — another template pool, used “for terminating pieces (such as the end of a village road) or as fallback if structures in this pool can’t generate.” We have nothing to fall back to, so we point it at the built-in empty pool, minecraft:empty.
  • elements — the list of pieces to randomly select from. Ours has one entry.
  • weight — “how likely this element is to be chosen when using this pool. Value between 1 and 150 (inclusive).” With one element the weight doesn’t compete with anything, so 1 is fine.
  • element — the piece itself:
    • element_type — we use minecraft:single_pool_element, which “places a single structure template.” (There are four others; see the box below.)
    • projectionrigid “to place a fixed structure (like a house),” or terrain_matching “to match the terrain height (like a village road).” A building should stay rigid; a flat path that follows hills would use terrain_matching. We want a solid building, so rigid.
    • location — the structure template to place. This is the namespaced ID of your Chapter 40 .nbt file: mypack:outpost points at data/mypack/structure/outpost.nbt.
    • processors — the processor list to run on the template. We have none yet, so minecraft:empty. We’ll come back and swap this in Step 2.

Under the Hood — the five pool element types (skippable). A single_pool_element is one of five kinds of piece. The others: legacy_single_pool_element (an older single piece that keeps the world’s original blocks instead of placing air), feature_pool_element (places a placed feature, such as a tree or ore, in a 1×1×1 box), list_pool_element (places several pieces in sequence), and empty_pool_element (places nothing). For a one-building outpost you only need single_pool_element; the others matter once you build village-sized, multi-piece structures.

Step 2 — A processor list (aging the build)

A processor list “is used to transform blocks of a structure template during generation.” It’s a list of processors, and each processor is one rule for changing blocks as the piece is placed, for example making a fresh stone-brick build look weathered and broken. Processor lists live in data/<namespace>/worldgen/processor_list.

This step is optional, but it’s what makes a generated build look like it belongs in the world instead of looking brand-new. Let’s give the outpost a worn, half-ruined look:

data/mypack/worldgen/processor_list/outpost_aging.json

{
  "processors": [
    {
      "processor_type": "minecraft:block_age",
      "mossiness": 0.2
    },
    {
      "processor_type": "minecraft:block_rot",
      "integrity": 0.9
    }
  ]
}

Both processors come straight from the Processor list format:

  • minecraft:block_age — “Makes blocks aged.” Stone bricks get a chance to become cracked, mossy, or turned into stairs and slabs; obsidian can crack to crying obsidian. Its one field is mossiness: “the probability of using mossy variants when making a block aged” (clamped to the 0.0–1.0 range). We use 0.2 for a lightly mossy look.
  • minecraft:block_rot — “Randomly removes blocks.” Its field integrity is “the probability of randomly removing blocks in the structure,” a value between 0 and 1. We use 0.9, meaning each block has a 90% chance to survive (so about one in ten is knocked out, leaving gaps). Important detail from the docs: removed blocks “are not replaced by air.” They keep whatever was already in the world there, so the rot blends into the surroundings instead of leaving holes.

Now wire it into the pool from Step 1 by changing the one processors line:

data/mypack/worldgen/template_pool/outpost.json

{
  "fallback": "minecraft:empty",
  "elements": [
    {
      "weight": 1,
      "element": {
        "element_type": "minecraft:single_pool_element",
        "projection": "rigid",
        "location": "mypack:outpost",
        "processors": "mypack:outpost_aging"
      }
    }
  ]
}

Figure (to be captured). two copies of the outpost side by side — left brand-new, right aged with moss and a few missing blocks

Try It! There are more processors you can drop into the list. minecraft:gravity shifts blocks up or down “to fit the terrain like a village road” (handy for paths). minecraft:nop “does nothing”: useful as a placeholder. A few others (block_ignore, protected_blocks, capped) and a heavier rule-based processor exist too; read about the rule processor below before you reach for those.

The rule processor — for precise, conditional block swaps. Minecraft also has a minecraft:rule processor that swaps blocks based on tests (an input_predicate, location_predicate, position_predicate, an output_state, and an optional block_entity_modifier). It’s a small language of its own. This book teaches the simpler block_age/block_rot/gravity processors fully; when you need precise, conditional block swaps, open the Processor list page on the wiki or copy a vanilla processor list and adapt the rule you find there.

Step 3 — The structure definition

The structure definition is the file that says what your structure is. Don’t confuse it with the .nbt structure file from Chapter 40: that one is the saved blocks; this one is the JSON configuration. The wiki’s own words: a structure here “is a large decoration… configured using JSON files within a data pack in the path data/<namespace>/worldgen/structure. To generate in a world, a structure needs to be part of at least one structure set.”

data/mypack/worldgen/structure/outpost.json

{
  "type": "minecraft:jigsaw",
  "biomes": "#minecraft:is_overworld",
  "step": "surface_structures",
  "terrain_adaptation": "beard_thin",
  "spawn_overrides": {}
}

Each field traces to the Structure definition format:

  • type — “the ID of structure feature type.” Structures that build themselves out of template pools and jigsaw blocks use the jigsaw type, so we write minecraft:jigsaw. (See the box after this list; the jigsaw type needs more companion fields than the five universal ones shown here.)
  • biomes — “biomes that this structure is allowed to generate in.” This can be one biome ID, a list of IDs, or a biome tag (written with a leading #, the tag syntax from Chapter 14). We start broad with #minecraft:is_overworld so it can appear across the surface; we’ll narrow it to plains through the structure set in the next step.
  • step — “the step where the structure generates.” The allowed values are: raw_generation, lakes, local_modifications, underground_structures, surface_structures, strongholds, underground_ores, underground_decoration, fluid_springs, vegetal_decoration, and top_layer_modification. A surface building belongs in surface_structures.
  • terrain_adaptation — “the type of terrain adaptation used for the structure” (optional, defaults to none). The values: none (no adaptation), beard_thin (“generating terrain under the structure, while removing terrain inside the structure,” used by pillager outposts and villages), beard_box (an advanced version, ancient cities), bury (buries the structure, strongholds), and encapsulate (advanced bury, trial chambers). Since our outpost is modeled on the real pillager outpost, beard_thin is the natural choice: it lays a little foundation under the build so it doesn’t float on a hillside.
  • spawn_overrides — overrides which mobs spawn inside the structure (for example, how blazes spawn in nether fortresses, or how ancient cities block spawns). It is “required, but can be empty,” and an empty object means “don’t override anything; spawn based on the biome as normal.” We leave it empty with {}.

The jigsaw structure’s own fields. Setting type to minecraft:jigsaw is correct, but a real jigsaw structure definition needs several more jigsaw-specific fields to say which template pool it starts from and how big it may grow: the start pool, a maximum size, a starting height, and a few placement switches. Those fields shift between game versions, so rather than memorize a list, open the Structure page on the wiki for your version, or copy a vanilla jigsaw structure (a village or pillager outpost) and read its fields off the real file. Everything above (type, biomes, step, terrain_adaptation, spawn_overrides) is universal to every structure and is shown here in full.

Step 4 — The structure set (where it appears)

The structure set decides where the structure shows up across the world and how far apart copies are. It lives in data/<namespace>/worldgen/structure_set. As we saw, just having this file is what turns generation on.

data/mypack/worldgen/structure_set/outpost.json

{
  "structures": [
    {
      "structure": "mypack:outpost",
      "weight": 1
    }
  ],
  "placement": {
    "type": "minecraft:random_spread",
    "salt": 165745296,
    "spacing": 32,
    "separation": 8,
    "spread_type": "linear"
  }
}

The Java root has two parts, structures and placement (from the Structure set JSON format):

  • structures — “weighted list of structures that can be placed.” Each entry names a structure (our definition, mypack:outpost) and a weight (“determines the chance of it being chosen over others. Must be a positive integer”). With one structure, it’s always the one chosen.
  • placement.type — the placement type, “one of minecraft:concentric_rings or minecraft:random_spread.” We use random_spread, which spreads structures “evenly throughout the entire world,” the same scheme vanilla uses for most structures.
  • placement.salt — “a number that assists in randomization… must be a non-negative integer.” Two structure sets with the same spacing but different salt won’t land on top of each other. Pick any number; we used a big arbitrary one.
  • placement.spacing — for random_spread, “average distance between two neighboring generation attempts” in chunks, 0–4096. 32 means roughly every 32 chunks the game tries to place one.
  • placement.separation — “minimum distance (in chunks) between two neighboring attempts,” 0–4096, and it “has to be strictly smaller than spacing.” 8 keeps outposts at least 8 chunks apart. (If you ever set separation equal to or larger than spacing, nothing generates; see What Can Go Wrong.)
  • placement.spread_type — “linear or triangular” (optional, defaults to linear). linear picks the offset uniformly; triangular clusters offsets toward the middle of each cell, giving a more even-looking spread. We use linear.

That’s the whole machine. Now make it bite.

Loading it — reopen, don’t reload

Save all four files, then:

  1. Exit the world completely, all the way back to the title screen or server stop, then open it again. Worldgen registries only re-read on load. /reload will not pick up these files.
  2. Explore new land: fly out to chunks you’ve never visited, since only newly generated chunks can contain your structure.
  3. Speed it up with a locate. In a function:

data/mypack/function/find_outpost.mcfunction

locate structure mypack:outpost

Run it (call the function, or for a one-off you can type the same locate structure in chat). It points you to the nearest copy. Travel there and your aged little outpost should be standing in the landscape.

Figure (to be captured). the chat output of locate structure mypack:outpost showing coordinates, then the outpost found at that spot

Closing the Chapter 34 loop

Back in Chapter 34 you built a “forge cache” (a chest of themed loot) and wrote its loot table, but we deliberately left the structure for Part XI. This is that moment. If your outpost.nbt was saved with a chest inside it, you can point that chest at the Chapter 34 loot table so every generated outpost comes stocked. The mechanism is the one you already know from loot tables: a chest’s block entity carries a LootTable field naming the table to roll. You can bake that into the saved .nbt (set the chest’s loot table before you save the structure in Chapter 40), or a rule processor’s block_entity_modifier with minecraft:append_loot can attach a loot_table to placed block entities during generation.

Attaching loot during generation. The rule processor can append a loot table to a block entity: a block_entity_modifier with type: minecraft:append_loot and a loot_table field. That’s the generation-time route. The simplest, most reliable approach for this book, though, is to set the chest’s loot table inside the saved structure in Chapter 40 and let it ride along. If you want the generation-time append_loot route instead, copy a vanilla structure that stocks its chests this way and adapt the rule you find there.

How Jigsaw Blocks Connect Pieces

Our outpost was one piece, so we never had to make two buildings snap together. Real villages and pillager outposts are built from many small pieces joined by jigsaw blocks: “technical blocks commonly used as a way to construct large structures from smaller sections.” You’ll meet them the moment you go past a single building, so here’s how they work, from the Jigsaw Block page.

Each jigsaw block placed inside a saved structure carries a few settings (shown in its in-game interface, and stored as block-entity data):

  • Target Pool (pool) — the template pool to pick the next connecting piece from. This is the link that lets one piece pull in another.
  • Name (name) — this jigsaw block’s own name. A jigsaw “gets aligned with another structure’s jigsaw block that has this value in the target tag.”
  • Target name (target) — the name a connecting piece’s jigsaw must have to dock with this one. In short: my target must match your name for our two pieces to join.
  • Turns into (final_state) — “the block that this jigsaw block becomes” once generation is done. Jigsaw blocks shouldn’t be left visible in the finished build, so they’re typically set to turn into minecraft:air or whatever fits.
  • Selection Priority (selection_priority) and Placement Priority (placement_priority) — when a piece has several jigsaws that could all connect, “jigsaw blocks with higher selection priority get selected first,” and placement priority controls the order pieces process their own children.
  • Joint type (joint, when the jigsaw faces up or down) — rollable (the connecting piece is placed with a random rotation) or aligned (rotations are forced to match).

The flow, then: piece A has a jigsaw whose Target Pool points at a pool; the game picks a piece B from that pool; B is rotated and slid so that its jigsaw (whose name matches A’s target name) lines up against A’s; the jigsaw blocks turn into their final_state; repeat outward until the pool’s fallback terminates the branch.

Multi-piece jigsaw assembly in depth. The fields and connection idea above are the vocabulary, but a full multi-piece example (how a village-sized graph branches, the size and recursion limits, the exact order pieces resolve) is a bigger topic than one outpost needs. This book teaches the single-piece outpost completely and the connection rules above; building a sprawling multi-piece structure (where selection/placement priority and matched name/target tags really come into play) is its own project. When you’re ready to build a village, the best teacher is a vanilla one: open the village template pools and structure on the wiki, or unpack the vanilla data pack, and trace how its pieces chain together.

Try It! — structure tags. Beyond generation, structures can be grouped with structure tags (data/<namespace>/tags/worldgen/structure/..., the same tag idea from Chapter 14). Vanilla uses tags like on_treasure_maps and eye_of_ender_located to decide which structures explorer maps point to and which an eye of ender flies toward. Adding your outpost to a tag won’t change where it generates (the structure set does that), but it can hook it into those map/locating features.

Practice

  1. Build the outpost. Create all four files exactly as above (using your own .nbt name), reopen the world, and locate structure mypack:outpost to find one. Confirm the aging from the processor list shows up: look for moss and a few missing blocks.

  2. Make it rarer. In the structure set, raise spacing from 32 to 64 (keep separation smaller). Reopen the world and explore fresh land; outposts should now be noticeably farther apart. Remember: already-generated chunks won’t change.

  3. Pin it to plains. Change the structure definition’s biomes field from #minecraft:is_overworld to a single plains biome ID, minecraft:plains (a plain string instead of a #tag). Reopen and confirm new outposts only appear on plains.

  4. Heavier ruin. In the processor list, lower block_rot’s integrity from 0.9 to 0.7 (more blocks removed) and raise block_age’s mossiness to 0.5. Reopen and compare; your outpost should look much more weathered.

  5. (Stretch) Wire in the Chapter 34 cache. If your .nbt has a chest, set its loot table to your Chapter 34 cache table before saving the structure in Chapter 40, so every generated outpost is stocked. Then locate one and open the chest.

What Can Go Wrong

“I edited a worldgen file and ran /reload, and nothing changed.” Worldgen files are dynamic registries; /reload doesn’t touch them. Exit the world and reopen it. And even then, only new chunks reflect the change, so fly out to unexplored land to see it.

“My structure never appears anywhere.” Two usual causes. First, a biome mismatch: the structure’s biomes field must include a biome that actually exists where you’re looking. If you set it to a biome you never visit, you’ll never see the structure. Second, bad placement math: in random_spread, separation must be strictly smaller than spacing. If separation is equal to or larger than spacing, the game can’t fit any attempts and nothing generates. Also double-check the structure set file is actually present: its mere existence is what switches generation on.

“I copied a structure-set example and the game rejected the file.” You may have grabbed the Bedrock form. The wiki shows a structure_set layout that starts with format_version and wraps everything in minecraft:structure_set with a description/identifier. That’s the Bedrock add-on format. A Java data pack uses the flat root shown in Step 4: a top-level structures list and a placement object, no format_version, no description. Make sure you’re using the Java shape.


What You Know Now (Part XI so far)

You can now make a saved building generate by itself in the world. You know the four worldgen files that cooperate to do it: structure definition (what/where-biome/step/terrain/spawns), template pool (which pieces, with weights and rigid/terrain_matching projection), processor list (aging and altering blocks as they place), and structure set (random spread vs. concentric rings, with spacing/separation/salt). You also know that just having the structure-set file turns generation on. You understand that all four are dynamic registries that need a world reopen, not /reload, and that only new chunks reflect changes. You can connect the jigsaw block vocabulary (pool / name / target / final_state / priorities) that larger multi-piece structures rely on. And you’ve closed the Chapter 34 loop: the forge-cache chest can finally live inside a real, self-generating outpost.

You can now build a custom self-generating structure in chosen biomes, at a spacing you control, made to look aged and lived-in. That is what every world-generation pack is built on.