Basic Modding - World Generation

Goal

I want to show you how to generate your Blocks in the world.

Difficulty

3/10 - Relatively Easy

Prerequisites

Forge Version

This Tutorial was created with Forge 10.13.0.1180 for Minecraft 1.7.10. If anything doesn't work with other versions, please contact me! This does not work with Minecraft 1.8.


Creating a World Generator

In the previous tutorial we created some ores. But what are ores good for if you can find the nowhere? So, we need to create a WorldGenerator to generate the ores into the world.

A world generator can be any class which implements the interface IWorldGenerator. This interface adds the method generate(…). For our WorldGenerator we will create a new class in the package com.yourname.yourmodid.world:

ModWorldGen.class:
public class ModWorldGen implements IWorldGenerator {
    @Override
    public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) {   
    }
}

The generate(…) method is called for every dimension. But you do not want to run the same generator in every dimension, so we create a switch statement with a case for every dimension ID: 

ModWorldGen.class:
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) {
    switch (world.provider.dimensionId) {
    case 0: //Overworld

        break;
    case -1: //Nether

        break;
    case 1: //End

        break;
    }
}

If we want to generate ores, we can use the class WorldGenMinable. This class works great for this purpose. Its also used for the generation of the vanilla ores.

The first ore we want to generate is the "tutorial ore" from the last tutorial.

This ore should be generated in the Overworld at any height between 0 and 64, should have a maximum vein size of 8 and should be generated in 20 veins per chunk.


The first thing we need to do is to create a new instance of a WorldGenMinable. The best place to do this is in the Constructor of our WorldGenerator class, so the initialization is automatically done when we create a new ModWorldGen.

ModWorldGen.class:
private WorldGenerator gen_tutorial_ore; //Generates Tutorial Ore (used in Overworld)

public ModWorldGen() {
    this.gen_tutorial_ore = new WorldGenMinable(ModBlocks.tutorial_ore, 8);
}

It is useful if you always write a comment behind each world generator telling what it does and especially where it is used.


This object is now a fully functional WorldGenerator which can generate a bunch of tutorial ores (up to 8 in one call) at the position we want to. But because this position should be randomized, we need to create a new method to calculate the random position. This method here is a standard method for running world generators at a random position within a chunk, so you can copy it and use it everywhere.

ModWorldGen.class:
private void runGenerator(WorldGenerator generator, World world, Random rand, int chunk_X, int chunk_Z, int chancesToSpawn, int minHeight, int maxHeight) {
    if (minHeight < 0 || maxHeight > 256 || minHeight > maxHeight)
        throw new IllegalArgumentException("Illegal Height Arguments for WorldGenerator");

    int heightDiff = maxHeight - minHeight + 1;
    for (int i = 0; i < chancesToSpawn; i ++) {
        int x = chunk_X * 16 + rand.nextInt(16);
        int y = minHeight + rand.nextInt(heightDiff);
        int z = chunk_Z * 16 + rand.nextInt(16);
        generator.generate(world, rand, x, y, z);
    }
}

This method takes all the position and calculates a random height with them (It is necessary to add 1 to the heightDiff because of the way the nextInt method works). Additionally, a random position inside of the chunk is calculated (The variables chunk_X and chunk_Z are multiplied by 16 because they define the chunk's coordinates, not the ones of the blocks). Finally, the generator is called at the special position.

The whole process is repeated as often as the argument chancesToSpawn defines.

Now we can add the runGenerator(…) method to our generate(…) method.

ModWorldGen.class:
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) {
    switch (world.provider.dimensionId) {
    case 0: //Overworld
        this.runGenerator(this.gen_tutorial_ore, world, random, chunkX, chunkZ, 20, 0, 64);
        break;
    […]
    }
}

Generating Ores in other Dimensions

It seems quite easy to generate an ore in another dimension, but if you try to place the generator for the ore in the case-statement for another dimension, nothing will happen because each instance of WorldGenMinable has a special block that it can replace. The default is stone, so its just fine for the overworld. If we want to replace something else, maybe if we want to generate cobblestone in the End, we have to add this to the Constructor of the WorldGenMinable:

ModWorldGen.class:
private WorldGenerator gen_cobblestone; //Generates Cobblestone (used in End)

public ModWorldGen() {
    […]
    this.gen_cobblestone = new WorldGenMinable(Blocks.cobblestone, 16, Blocks.end_stone);
}

gen_cobblestone is now a WorldGenerator which generates Cobblestone while replacing Endstone, so its suitable for being used in the End. Let's add it to the End's generation part now.

ModWorldGen.class:
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) {
    switch (world.provider.dimensionId) {
    […]
    case 1: //End
        this.runGenerator(this.gen_cobblestone, world, random, chunkX, chunkZ, 10, 0, 80);
        break;
    }
}

Generating small veins of ores

The class WorldGenMinable is great for generating larger amounts of ore blocks at one place. Unfortunately, this class doesn't work well for smaller vein sizes. If you set a size of 6, you will get a few veins of six blocks, a lot with five to three and few with less.

Basically it's useful to set the maximum vein size one larger than you expect common veins to be, but if you enter a vein size below 5 even this method doesn't work, because then a lot of the veins will have a size of zero, thus they won't be generated at all.

So, if you want to have a vein size of four or lower, you need to create your own WorldGenerator. I will create one to generate single blocks, just like emerald ore, but of course you can cerate your own generators as well.

As always, we first need a new class:

WorldGenSingleMinable.class:
package com.bedrockminer.tutorial.world;

public class WorldGenSingleMinable extends WorldGenerator {
}

Of course, we need some variables, Constructors and the generate(…) method.

WorldGenSingleMinable.class:
public class WorldGenSingleMinable extends WorldGenerator {

    private Block block;
    private int blockmeta;
    private Block target;

    public WorldGenSingleMinable(Block block, int meta, Block target) {
        this.block = block;
        this.blockmeta = meta;
        this.target = target;
    }

    public WorldGenSingleMinable(Block block, Block target) {
        this(block, 0, target);
    }

    public WorldGenSingleMinable(Block block) {
        this(block, Blocks.stone);
    }

    @Override
    public boolean generate(World world, Random random, int x, int y, int z) {

    }
}

Now we can fill the generate(…) method with code.

WorldGenSingleMinable.class:
@Override
public boolean generate(World world, Random random, int x, int y, int z) {
    if (world.getBlock(x, y, z).isReplaceableOreGen(world, x, y, z, this.target))
        world.setBlock(x, y, z, this.block, this.blockmeta, 2);
    return true;
}

This method first checks whether it can replace the block and then places the new block.

With this WorldGenerator we now generate the multi ore from the last tutorial.

ModWorldGen.class:
private WorldGenerator gen_multi_ore; //Generates Multi Ore (used in Overworld)

public ModWorldGen() {
    […]
    this.gen_multi_ore = new WorldGenSingleMinable(ModBlocks.multi_ore);
}

@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider) {
    switch (world.provider.dimensionId) {
    case 0: //Overworld
        […]
        this.runGenerator(this.gen_multi_ore, world, random, chunkX, chunkZ, 10, 0, 16);
        break;
    […]
    }
}

This method will now generate single blocks of multi ore. Note that they now generate very rarely. I found four veins of diamond before I spotted my ore. But then I was lucky and found two multi ores right next to each other.

Registration of the WorldGenerator

The World Generator we just created needs to be registered in the init(…) method of the CommonProxy class.

CommonProxy.class:
public void init(FMLInitializationEvent e) {
    GameRegistry.registerWorldGenerator(new ModWorldGen(), 0);
}

Testing World Generators

Note that a WorldGenerator doesn't change existing terrain. If you want to test a WorldGenerator, you either have to teleport far away or create a complete new world.


You can download the code used in this tutorial as a .zip file from here.


Recommended tutorials to continue with

Take a look at the tutorial overview and find out what you want to do next!


Comments and Questions:

If you want to report modding problems, please make sure to include the code in a pastebin link or something else! Don't just write "It doesn't work", otherwise your post will be deleted. For more complicated problems, please use the troubleshooter form.