Skip to content

Your first world generator

Rutger Kok edited this page Oct 26, 2021 · 15 revisions
Software tools usage
Uses Bukkit Yes
Uses resource packs No
Uses WorldGeneratorApi No

This tutorial assumes you have some basic knowledge in creating a Bukkit plugin. You should be able to create a Bukkit plugin that adds a command named /hi, that simply prints "Hello!" when executed. If not, please head over to the Plugin Tutorial page at the Bukkit wiki.

  • This tutorial can be edited by anyone, you just need a (free) GitHub account. So if you want to improve it, please do!
  • If you have any questions or comments, or get stuck somewhere, please contact me!
    :octocat: rutgerkok

Setting up your plugin

We will create a very basic plugin, called Pancake. It is used to create a flat landscape.

Let's use the following code for the plugin.yml:

name: Pancake
version: 1.0
main: your.package.here.PancakeMain
load: startup
api-version: 1.17

This plugin.yml file creates a plugin named Pancake, with the main class PancakeMain placed in the package your.package.here. Feel free to change the names. The plugin is set to load at startup, so before the worlds are active. That is necessary: if your plugin would be loaded after the initial terrain had generated, it would be useless!

Our main class starts out empty:

// This is the PancakeMain.java file
package your.package.here;

import org.bukkit.plugin.java.JavaPlugin;

public class PancakeMain extends JavaPlugin {
    // Pretty empty, isn't it?
}

If you see an error about JavaPlugin missing, then you forgot to add the Bukkit API.

Creating a flat world generator

Ok, let's add some code. First, we create a new class called PancakeGenerator:

// PancakeGenerator.java
package your.package.here;

import java.util.Random;

import org.bukkit.HeightMap;
import org.bukkit.Material;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.WorldInfo;


/**
 * This the actual terrain generator.
 *
 */
public class PancakeGenerator extends ChunkGenerator {

    private static final int CHUNK_SIZE = 16;
    private static final int TERRAIN_HEIGHT = 66;

    @Override
    public void generateNoise(WorldInfo world, Random random, int x, int z, ChunkData chunk) {
        chunk.setRegion(0, chunk.getMinHeight(), 0, CHUNK_SIZE, TERRAIN_HEIGHT, CHUNK_SIZE, Material.STONE);
    }

    @Override
    public int getBaseHeight(WorldInfo world, Random random, int x, int z, HeightMap type) {
        return TERRAIN_HEIGHT;
    }

    @Override
    public boolean shouldGenerateBedrock() {
        return true;
    }

    @Override
    public boolean shouldGenerateCaves() {
        return true;
    }

    @Override
    public boolean shouldGenerateDecorations() {
        return true;
    }

    @Override
    public boolean shouldGenerateMobs() {
        return true;
    }

    @Override
    public boolean shouldGenerateNoise() {
        return false; // We do this ourselves
    }

    @Override
    public boolean shouldGenerateStructures() {
        return true;
    }

    @Override
    public boolean shouldGenerateSurface() {
        return true;
    }

}

This is quite a lot of code, so let's go through it. The method generateNoise will be called once for every chunk that is being generated. It must decide the general shape of the terrain. To do this, it must place only stone and water, and leave all other blocks as air. (In the Nether and End, instead of stone you must place Netherrack and End Stone, respectively.) Usually, this is done by using the results of a so-called noise function. However, here we won't be generating noisy terrain, we'll just provide superflat terrain.

The chunk starts out completely empty. This is because we have overridden the method shouldGenerateNoise to make it return false. In our generateNoise method, we fill a number of layers (y = 0 to TERRAIN_HEIGHT) of the chunk with stone. Because this method is called for every newly generated chunk, the entire world will become superflat.

We have also overridden a bunch of other methods: shouldGenerateSurface, shouldGenerateStructures, etc. They all return true. This makes Bukkit automatically add "decorations" like caves, ores, trees, villages and strongholds in exactly the same way as regular Minecraft would. This can be disabled by making the appropriate methods return false.

We're not done yet. To actually use this flat terrain generator, we first need to tell the server it exists. That is done by updating the PancakeMain class. Update the file so that it now looks like this:

// PancakeMain.java
package your.package.here;

import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.java.JavaPlugin;

import nl.rutgerkok.worldgeneratorapi.WorldGeneratorApi;
import nl.rutgerkok.worldgeneratorapi.WorldRef;

public class PancakeMain extends JavaPlugin {

    // The class is no longer empty, we now add this method
    // It is like onEnable, but for world generators
    @Override
    public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
        getLogger().info("Activated for world \"" + worldName + "\"");
        return new PancakeGenerator();
    }
    // End of addition to class

}

We see a new method called getDefaultWorldGenerator. This method is part of the Bukkit API. It is very similar to onEnable, except it is called once for every world, instead of just once for the whole server. You can still add an onEnable method if your plugin needs to do other things besides terrain generation (like listening for events), but for now we won't need an onEnable method.

The method consists of just two lines. The first simply logs a message, so that the server admin knows that the Pancake plugin is in use for that world. The second actually returns an instance of our chunk generator class.

Running the plugin

You can now compile your plugin, and then place the JAR file of your plugin in the plugins folder of your server. Once you start or restart the server (not reload!), your plugins will be active.

If you have installed Multiverse, you can type the following command to enter your world:

/mv create world_name_here normal -g Pancake

Here, Pancake must be the name of your plugin, as supplied in the plugin.yml file. So if you changed the name there, also change it here. You can now teleport to this world (use /mv tp world_name_here) and see your world generator.

If you have not installed Multiverse, and/or if you want to modify one of the default worlds, you'll need to edit the bukkit.yml file. Add the following at the end of the file:

worlds:
  world:
    generator: Pancake

If your default world isn't simply called world (so if you have changed the name in the server.properties file), you need to change world in this file as well.

If you restart your server, any new chunks in the default world should consist of flat terrain.

Example terrain
Example terrain. Note that the ground is completely flat. You may of course spawn in a different biome, so the terrain may look different on your server.