How to Make a World Generation Algorithm in Bukkit/Spigot
This article will be a bit different than most Info Toast articles, as it’s aimed more towards people who know how to code and are familiar with everything that comes with coding. That being said, you should probably have several years of Java coding before you attempt this. The Spigot API is not the best for world generation, so you must recreate everything Minecraft has from scratch. (NOTE: Spigot API changes have been made since this article was written so you can enable things like cave gen, tree gen, bedrock gen, etc., from the vanilla game in the primary generator class). That being said, this article shows you how to make a world generation algorithm in Bukkit.
Step 1 of making a world generation algorithm in Bukkit: Planning
The first mistake I made when creating my world generation algorithm in Bukkit was making code that didn’t scale well as the algorithm got more advanced. While I was able to get basic noise in, it didn’t scale, so I was able to make different biomes. You must plan everything out before you start coding the world generator, otherwise you will run into a problem when it’s starting to get good and have to start from scratch. Here are some important questions you must ask yourself before coding.
- What biomes do I plan to have, and what features of each biome will there be?
- How do we determine biomes?
- How will I set up trees? Fractal algorithm? Use structure block files?
- How are caves going to work?
- How am I going to use noise?
Step 1.5: Determining the Order
Asking questions like this will help you figure out how to write code that scales properly for what you want to do. Next, you will want to determine the order in which stuff happens. Mine looks like this, and you may want to consider doing this, too:
- FastNoise creates a noise map.
- Rivers are added to the noise map.
- The noise map determines the biome type: flat, mountainous, ocean, deep ocean, or river.
- The biome is determined from the type of biome based on a noise grid of temperature and humidity.
- The world generator places surface blocks determined by the biome handler class.
- The generator places a line of stone all the way down, followed by bedrock
- The world generator places small items on top of the surface from a randomly generated Material in the biome handler class
- The worldgen runs a method in the biome handler class that allows the terrain to be transformed before the server runs BlockPopulators
- The BlockPopulators then populate large items, such as trees, based on a method in the biome handler class.
- Worldgen makes caves
Step 2 of making a world generation algorithm in Bukkit: Building the foundations
The next thing you will want to do to make a world generation algorithm in Bukkit is build the basic foundations for how the world generator will work. Make a normal plugin base: https://www.spigotmc.org/wiki/creating-a-blank-spigot-plugin-in-intellij-idea/. We will be using IntelliJ IDEA for this tutorial. Next, make a new class called CustomChunkGenerator. Then, add this line of code to your plugin class:
@Override
public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) {
return new CustomChunkGenerator();
}
CustomChunkGenerator extends ChunkGenerator. You need to create a new method called with the signature:
public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biome) {
This is a rough sketch of what the contents of that method is going to be:
public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biome) {
ChunkData chunk = createChunkData(world);
SimplexOctaveGenerator generator = new SimplexOctaveGenerator(new Random(world.getSeed()), 8);
generator.setScale(0.010D);
// List of biomes that need to be transformed
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
int rawX = chunkX * 16 + x;
int rawZ = chunkZ * 16 + z;
// Code that gets the block height based on the heightmap
// Code that calculates the and sets the biome
// Code that generates the actual blocks -- grass to bedrock
// Code that transforms the area based on what's set in the biome handler
}
}
return chunk;
}
NOTE: You must synchronize blocks with a lock variable; otherwise, it will create an error on Paper and other servers that thread plugins.
Step 3 of making a world generation algorithm in Bukkit: Setting up Biomes
Biomes are an extremely important part of this worldgen. Object orientation for biomes is extremely important. You don’t want to shove all that into one class. Start by making an interface for each of your biome handlers. I included the following methods, but you can do something different.
- boolean isOcean — pretty self-explanatory
- Biome getBiome — What biome this should look like for vanilla clients
- Material[] getSurface — A list of blocks on the surface (e.g. {GRASS_BLOCK, DIRT, DIRT}).
- Material populateGrass — Not just grass, but gets a random material to put on top of the surface of the world (e.g. DEAD_BUSH, GRASS, COBBLESTONE_SLAB)
- void populateLargeItems — Spawns stuff like trees
- void transformTerrain — Transforms the terrain before BlockPopulators
Now, here’s the fun part. Before you start doing all the other stuff, you only need to make a ton of biomes. Make sure you keep in mind the temperature and humidity of each of these biomes.
Step 3.5: Where do the biomes go?
Now, you need actually to place those biomes down. I’m not going to tell you how to do this, instead I’m going to refer you to some code that you should probably read. Check out https://github.com/Hex27/TerraformGenerator. I learned a lot about worldgen from this person’s code. You will be able to see and adapt this part to your code. However, I will tell you the very basis of what’s about to go on here because this is the most complex part.
- The noise algorithm creates a heightmap
- Heightmap adds rivers
- Anything below level 61 has water on top and is an ocean biome.
- If it’s below another level, it’s deep ocean.
- Above a certain level is a mountain biome
- Anything in the middle is a flat biome
- Anything that the height has listed as a river is a river biome
- Noise is based on the type of biome above, with erosion noise to make it a little less blocky.
- For oceans, deep oceans, flat biomes, or mountainous biomes, use a temperature and humidity noise map to look through an array of options. Access the proper biome by doing, for example, mountainous[temperature][moisture]. That’s your biome.
- Populate the chunk depending on what biome it is.
- Make your final modifications.
Step 4 of making a world generation algorithm in Bukkit: The Trouble with the Trees
Trees seem to be a problem with many modern Bukkit worldgen algorithms. Some of the more common ones, such as TerraformGenerator, use a fractal algorithm to generate trees, which looks good on paper but is a hassle for people to play survival with. That is why I recommend using pre-built trees. In fact, there is a datapack that you can use that has a ton of these trees. It’s called EasyTrees. You can download it here: https://www.planetminecraft.com/data-pack/easy-trees-4562308/. The way this datapack spawns trees is a bit weird. Basically, the player places an armor stand, which turns into an area effect cloud, which lasts a little while and then the datapack places a tree there.
You don’t want to automate placing the armor stands because while this doesn’t cause much of a problem when a player places one tree, it can become extremely laggy the longer it lasts. This is why I recommend spawning the area effect cloud and then using the NBT API found here: https://www.spigotmc.org/resources/nbt-api.7939/ to spawn an area effect cloud that the datapack will immediately place a tree at. (I typically let the area effect cloud last for 10 ms so as not to interfere with other stuff the worldgen might be doing first but also not cause a lot of lag.)
I am going to allow people to download the Standalone for my Lorax engine (that’s my nickname), which basically does all of that for you. I’d rather not have people steal my code, and I’d rather you distribute the Lorax engine jar with your worldgen algorithm if you use it, but I’m not going to obfuscate the jar, and I’m not going to take legal action if anyone copies and pastes the decompiled code.
Step 5: Caves and Structures
Caves and Structures are often not biome-dependent. This means instead of using the Biome classes, you most likely want to make completely new BlockPopulators for caves, ores, and non-biome-dependent structures. It is heavily up to the developer to structure stuff like this, so I’m not going to show you how to do anything other than just tell you it needs to be in its own BlockPopulator.
NOTE: In more recent versions, the Spigot API allows you to have the game generate the structures automatically, which makes it easier for the programmer, but for multiplayer, it also means the world could be susceptible to things like Chunkbase: How to Find Things in Your Minecraft World on Chunkbase
Step 6: Conclusion
Making a world generation algorithm in Bukkit is no small task. There’s a reason there are very few general-purpose world generation algorithms on the market right now. There is also poor documentation, which is why I wrote this article. If you’ve managed to do it, give yourself a tremendous pat on the back. I would also love to hear from you. Please comment below and advertise your world generator if you manage to make one, not because I want to be controlling, but because I’m nosy. I hope this article succeeds at bringing custom Spigot world generators back.