Village Building Checklist

These are the steps to add a new buildings to villages. Adding a new building is actually relatively simple as long as you understand how to use structure blocks.

Checklist


  1. Create the building
  2. Add building to structure pool(s)

Create the Building


To start with you’ll need to run the game and give yourself a structure block. You can give yourself one using the command:

/give @a minecraft:structure_block

These can be used to load and save structures to an NBT file. Full details of how to use them can be found here.

The easiest way to start is using a building that already exists and modify it. You don’t need to do this, but it saves you the trouble of figuring out Jigsaw Blocks. You can find the names of structures in the game by looking in the external dependencies of your project for net.minecraft:client:extra.

Then you can use a structure block in LOAD mode to load in the structure. The first time you click it will only show the bounding box. The second time you click it the structure will actually load.

Once you have modified the structure, set the structure block to SAVE mode and name the building whatever you want. Make sure Include Entities is set to ON if your build has any entities (e.g. bottled butterflies).

Add Building to Structure Pools


Next you need to add the building to the structure pools by listening for a TagsUpdatedEvent. A very simple implementation of this includes a helper function to add the buildings for you. The method presented here handles the building’s weight by adding the structure to the pool multiple times, which is how it is done in the base game.

/**
 * Listens for generic events on the Forge Bus.
 */
public class ForgeEventListener {

    /**
     * Construction
     * @param forgeEventBus The event bus to register with.
     */
    public ForgeEventListener(IEventBus forgeEventBus) {

        forgeEventBus.register(this);
        forgeEventBus.addListener(this::onTagsUpdated);
    }

    /**
     * Add the lepidopterist's buildings to villages.
     * @param event The event we respond to in order to add the villages.
     */
    private void onTagsUpdated(TagsUpdatedEvent event)
    {
        if(event.getUpdateCause() == TagsUpdatedEvent.UpdateCause.SERVER_DATA_LOAD) {

            addToPool(event.getRegistryAccess(),
                    new ResourceLocation("village/plains/houses"),
                    new ResourceLocation("butterflies", "village/plains/houses/plains_butterfly_house_1"),
                    3);
        }
    }

    /**
     * Adds a structure to the specified pool.
     * @param registryAccess Access to the registry, provided by the event.
     * @param structurePool The RL of the pool to add to.
     * @param structureToAdd The RL of the structure to add.
     * @param weight The likelihood this building will appear.
     */
    private static void addToPool(RegistryAccess registryAccess,
                                  ResourceLocation structurePool,
                                  ResourceLocation structureToAdd,
                                  int weight)
    {
        Registry<StructureTemplatePool> registry = registryAccess.registryOrThrow(Registries.TEMPLATE_POOL);
        StructureTemplatePool pool = Objects.requireNonNull(registry.get(structurePool), structurePool.getPath());

        if(!(pool.rawTemplates instanceof ArrayList)) {
            pool.rawTemplates = new ArrayList<>(pool.rawTemplates);
        }

        SinglePoolElement addedElement = SinglePoolElement
                .single(structureToAdd.toString())
                .apply(StructureTemplatePool.Projection.RIGID);

        pool.rawTemplates.add(Pair.of(addedElement, weight));

        for (int i = 0; i < weight; ++i) {
            pool.templates.add(addedElement);
        }
    }
}

Access Transformers

In StructureTemplatePool, both templates and rawTemplates are private. We need to use access transformers to change their access level. To start, we add, or uncomment, a line from build.gradle.

    // This property enables access transformers for use in development.
    // They will be applied to the Minecraft artifact.
    // The access transformer file can be anywhere in the project.
    // However, it must be at "META-INF/accesstransformer.cfg" in the final mod jar to be loaded by Forge.
    // This default location is a best practice to automatically put the file in the right place in the final jar.
    // See https://docs.minecraftforge.net/en/latest/advanced/accesstransformers/ for more information.
    accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')

Now we need to add the transformer.cfg file and give ourselves access to the private properties. This is simple enough to do.

public net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool f_210560_ # templates
public-f net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool f_210559_ # rawTemplates

Using public we can make the property public. Using public-f we also remove the final modifier from the property. Next is the full package path, in this case we want to modify StructureTemplatePool.

The strange numbers preceded by f_ are known as SRG names, and are needed so the mod knows where in the compiled code these methods will be. To get them in IntelliJ, you can right-click on the property you want to modify and choose Get SRG Name. This will show you the SRG name, and you can use it in this config file. They may need to be updated for different versions of your mod.