[Basic] Creating custom world generators (New API)

Discussion in 'Resources' started by MatorKaleen, Jun 4, 2012.

  1. Offline

    MatorKaleen

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    *WARNING* I'm from Germany, so my English won't be perfect. You are allowed to ask, if you don't understand something ;)

    In this tutorial I want to explain you the very basics of world generation. I assume, you know the basics to create a bukkit plugin. There are already tutorials for World Generation, but they're using the old API.

    Table of contents:
    • How to build a chunk
    • Coding of the plugin class
    • Coding of the generator class
    • Get it working
    How to build a chunk

    You know, the normal Minecraft chunk has a size of 16x16x256. To save memory, the chunk itself is split into 16 16x16x16 parts. The idea behind this is, that chunks in which is no block (or air) will be delivered with NULL.

    The complete chunk is represented by this array:
    Code:JAVA
    1. byte[][] result = new byte[world.getMaxHeight() / 16][];


    Before you can set blocks, you have to initialize the chunk part in which the block is:
    Code:JAVA
    1. result[partY] = new byte[4096];

    partY is the position of the part in the chunk from bottom to top.

    As you can see, each part consists of 12 bits. The first 4 Bits are the y value, the 4 in the middle the z value and the last 4 the x value. For example, we want to set a stone block at the position P(12|8|5):

    To set the block, you have to do:
    Code:JAVA
    1. result[2140] = (byte)Material.STONE.getId();




    Coding of the plugin class

    This is very simple. You only have to add one function too your main class:
    Code:JAVA
    1. import org.bukkit.generator.ChunkGenerator;
    2. import org.bukkit.plugin.java.JavaPlugin;
    3.  
    4. public class YourWorldGeneratorPlugin extends JavaPlugin
    5. {
    6. public ChunkGenerator getDefaultWorldGenerator(String worldName, String id)
    7. {
    8. return new YourGenerator();
    9. }
    10. }


    For more infos about getDefaultWorldGenerator(String worldName, String id) look here.



    Coding of the generator class

    We will code a very simple flat terrain generator.
    Take a look into the docu: click!
    You can see, to generate a very simple world, you need a class that extends
    Code:JAVA
    1. org.bukkit.generator.ChunkGenerator

    and has the function
    Code:JAVA
    1. byte[][] generateBlockSections(World world, Random random, int x, int z, BiomeGrid biomes)


    Your basic class structure will look like this:
    Code:JAVA
    1. import java.util.Random;
    2.  
    3. import org.bukkit.World;
    4. import org.bukkit.generator.ChunkGenerator;
    5.  
    6. public class YourGenerator extends ChunkGenerator {
    7. public byte[][] generateBlockSections(World world, Random random, int chunkX, int chunkZ, BiomeGrid biomeGrid)
    8. {
    9. byte[][] result = new byte[world.getMaxHeight() / 16][]; //world height / chunk part height (=16, look above)
    10. return result;
    11. }
    12. }


    This is your very first generator! Congratulation for doing nothing! :eek:

    But now let us add some blocks. To make it easier, we add an simple method, to set a block in the chunk:
    Code:JAVA
    1. void setBlock(byte[][] result, int x, int y, int z, byte blkid) {
    2. if (result[y >> 4] == null) //is this chunkpart already initialised?
    3. {
    4. result[y >> 4] = new byte[4096]; //initialise the chunk part
    5. }
    6. result[y >> 4][((y & 0xF) << 8) | (z << 4) | x] = blkid; //set the block (look above, how this is done)
    7. }


    Now it's very easy:
    First we generate a layer of bedrock at y=0:
    Code:JAVA
    1. for(x = 0; x < 16; x++)
    2. {
    3. for(z = 0; z < 16; z++)
    4. {
    5. setBlock(result, x, 0, z, (byte)Material.BEDROCK.getId());
    6. }
    7. }


    Then you generate two layers of dirt:
    Code:JAVA
    1. for(x = 0; x < 16; x++)
    2. {
    3. for(y = 1; y <= 2; y++)
    4. {
    5. for (z = 0; z < 16; z++)
    6. {
    7. setBlock(result, x, y, z, (byte)Material.DIRT.getId());
    8. }
    9. }
    10. }


    And at last you create a layer of grass:
    Code:JAVA
    1. for(x = 0; x < 16; x++)
    2. {
    3. for(z = 0; z < 16; z++)
    4. {
    5. setBlock(result, x, 3, z, (byte)Material.GRASS.getId());
    6. }
    7. }


    There is an advanced version of this code attached.



    Get it working

    Now we have a generator written. But there are a few things to do:

    First you have to make an entry in your plugin.yml
    Code:
    load: STARTUP
    If you don't add this line, bukkit will generate the world and THEN load your plugin!
    Now you're ready to export it as .jar (I expect, you know how to build a plugin) and copy it into your plugin folder.

    Now open your bukkit.yml and add this lines of code:
    Code:
    worlds:
      YourWorldNameHere:
        generator: YourWorldGeneratorPlugin
    You have only to modify your level name in server.properties and you're ready to start bukkit.



    End

    This is the end of my tutorial. If you have any questions and/or suggestions, write an answer.

    Here is the download link: BukkitFlatWorld.zip
    If you find any mistakes, please report them to me.
    I used bukkit-1.2.5-R3.0, and I don't promise, that it works flawless with newer versions.

    Mator

    This post has been edited 14 times. It was last edited by MatorKaleen Jan 3, 2013.
    gregthegeek and BrickCheez like this.
  2. Offline

    EDawg878

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I'm reposting a question from the PlotMe developer zachbora:
    Do you think you could explain how to make a generator that supports block values?
  3. Offline

    MatorKaleen

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I'll try it. But first, I will finish this tutorial here (maybe today ;), i hadn't much time in the last few days) and then i will look at that problem.
  4. Offline

    ferrybig

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    you can use the "ChunkMaker" class of the plugin "MultiWorld" to make those chunks whit cuboids and other stuff, whtout to code mutch by your own
  5. Offline

    sablednah BukkitDev Staff

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I cant see it!
  6. Offline

    MatorKaleen

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Yes, I just have to comment it, but at the moment, I have to concentrate at school (OMG Reallife! :D)

    This post has been edited 1 time. It was last edited by MatorKaleen Sep 8, 2012.
  7. Offline

    pivotgamer84

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Could you explain how to make biomes generate?

    I want to be able to specify that a specific biome generates at the spawn, but everything else has an ocean biome?

    An example of this would be a survival island generator, where the island is unique to every seed by using biomes instead of specific blocks and block locations.
  8. Offline

    MatorKaleen

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Example code uploaded. If you find errors, please report it to me ;)
  9. Offline

    heylookoverthere

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    I would also like to learn how to do this.
  10. Offline

    MatorKaleen

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Hm... Thats an intersting question. There are two possibilities: You handle it with the populator (The easy way), or the complex way: You create a delayed task. But i have to research this. Atm I'm working at another project. When I finished this, I will maybe make a tutorial about this.
  11. Offline

    sd5

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    @MatorKaleen: Your code is too difficult. Instead of the byte[][] you should use a byte[] with size 65536 (16 * 16 * 256) called blocks and add a method:
    Code:
    private int convert(int x, int y, int z) {
        return (x * 16 + z) * 256 + y;
    }
    To create a block simply do now:
    Code:
    blocks[convert(x, y, z)] = (byte) Material.STONE.getId();
    Finally just return blocks.
    JWhy likes this.
  12. Offline

    MatorKaleen

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    @sd5
    The way, you describe, is
    1. Deprecated (look here)
    2. Much more memory intensive: a byte[65536] is larger than a byte[16][], because you needn't to initialise every 16 arrays in the second dimension (it's hard to explain in english for me, sry)

    Maybe this is more difficult as your one, but I think, this is the more effecient way.

    Mator
  13. Offline

    ZachBora

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Very interesting. Wish I had known about this before I did my plot generator.

    I will change the code and see if it works. It's too bad that chunk gen still doesn't support block data.
  14. Offline

    Aza24

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Looks good! Just one tip, use the getMaxHeight(); method instead of specifying the height.

    Code:Java
    1. public byte[][] generateBlockSections(World world, Random random, int chunkX, int chunkZ, BiomeGrid biomeGrid) {
    2. byte[][] result = new byte[world.getMaxHeight() / 16][];
    3. return result;
    4. }
  15. Offline

    MatorKaleen

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Thanks for the tip, I will update the download the next few days ;)

    Here's a little new screenshot, what I'm doing atm: Mediafire
  16. Offline

    Aza24

    dev.bukkit.org profile:
    CFUSERNAME
    My Plugins (CFCOUNT)
    Cool, do you know how to make light update on generated chunks because I generated glowstone and no light is given off until the block is updated.

Share This Page