Using Jigsaw Structures
This guide explains how to generate complex, procedural structures using Jigsaw Blocks, similar to Minecraft's villages or bastions. We will stick to the patterns used in complex addon projects.
The system consists of three main parts:
- Processors: Rules for how blocks behave during placement (e.g., ignoring air, replacing water).
- Template Pools: Collections of structure files (
.mcstructure) that can spawn. - Structure Sets: The top-level definition that controls where and how the structures generate in the world.
1. Processors
Processors allow you to modify blocks as they are placed. For example, you might want a path to replace grass but not air.
from anvil.api.world.structures import _JigsawStructureProcess
from anvil.api.vanilla.blocks import MinecraftBlockTypes
# Create a processor that prevents structure air from overwriting world blocks
# and ensures paths replace dirt correctly.
def create_path_processor():
processor = _JigsawStructureProcess("ignore_air")
# Don't replace existing blocks with Structure Void/Air
processor.add_block_ignore_processor([MinecraftBlockTypes.Air()])
# Rule: Turn Grass Path -> Dirt when placed on top of certain blocks
processor.add_block_rule(MinecraftBlockTypes.GrassPath()).input_predicate.block_state_match(
MinecraftBlockTypes.Dirt()
)
return processor
ignore_air = create_path_processor()
2. Template Pools
Pools are groups of structure elements. You often have a "start" pool and then subsequent pools like "corridors", "rooms", or "loot_rooms".
from anvil.api.world.structures import JigsawStructureTemplatePool
pools = []
def dungeon_start_pool():
# The name "dungeon_start" corresponds to the pool name referenced in Jigsaw blocks
# Note: In the jigsaw block, you must use a unique NAMESPACE:POOL_NAME. Anvil will automaticall use the namespace of the addon.
pool = JigsawStructureTemplatePool("dungeon_start")
# Add an element pointing to world/structures/dungeon/start.mcstructure
# We apply the processor we created earlier
pool.add_structure_element("dungeon/start", ignore_air)
pools.append(pool)
return pool
def rooms_pool():
pool = JigsawStructureTemplatePool("dungeon_rooms")
# Add multiple variants with different weights
pool.add_structure_element("dungeon/room_large", ignore_air, weight=10)
pool.add_structure_element("dungeon/room_small", ignore_air, weight=5)
pools.append(pool)
return pool
3. Structure Sets
The Structure Set ties everything together. It defines the generation rules (biomes, spacing) and the starting point.
from anvil.api.world.structures import JigsawStructureSet
from anvil.api.vanilla.biomes import MinecraftBiomeTags
from anvil.api.actors.components import Filter
def generate_dungeon():
# 1. Initialize pools
start = dungeon_start_pool()
rooms_pool()
# 2. Create the set
# separation=4, spacing=10 means structures are 4-10 chunks apart
dungeon_set = JigsawStructureSet("my_dungeon", separation=4)
# 3. Add the main structure configuration
main_structure = dungeon_set.add_jigsaw_structure(
"my_dungeon_structure",
start_pool=start, # Start with this pool
max_depth=4, # How far out to branch
start_height=30, # Y level (underground)
placement_step="underground_structures",
start_height_from_sea=False,
)
# 4. Add Biome Filters
main_structure.add_biome_filters(
Filter.any_of(
Filter.has_biome_tag(MinecraftBiomeTags.Plains),
Filter.has_biome_tag(MinecraftBiomeTags.Forest)
)
)
# 6. Queue everything
for pool in pools:
pool.queue()
dungeon_set.queue()
Full Workflow
- Design in Game: Build your structures and save them with Jigsaw blocks.
- Set the Target Pool in Jigsaw blocks to match your pool names (e.g.,
my_namespace:dungeon_rooms). - Save
.mcstructurefiles toworld/structures/<category>/<name>.mcstructure.
- Set the Target Pool in Jigsaw blocks to match your pool names (e.g.,
- Define in Python: Use the code above to register your pools and sets.
- Run: Anvil will export the structure set, which the game uses to generate your dungeon.
Linking Pools
In your Jigsaw Block inside Minecraft: - Target Pool: my_namespace:dungeon_rooms (matches JigsawStructureTemplatePool("dungeon_rooms") + implicit namespace) - Name: connector_a (matches the name you connect to) - Target Name: connector_a (connection point on the other piece)