Modify new chunks generating resources (ore) count.

Discussion in 'Spigot Plugin Development' started by Dionysus, Sep 26, 2014.

  1. Dionysus

    Supporter

    Hello, how may I modify for example Coal or Iron ores resource generating ratio for new chunks? I tried to use TerrainControl, but that plugin had too many other features, which changes whole map generating objects/structure so it doesn't fit for me. I just need to edit ore rates.

    I tried watching at JavaDocs, but I didn't have any luck reading there, for example I want to decrease Iron/Coal generating 2 times is it BlokcPopulateEvent or ..?

    I really need help and I'm willing to donate for solution.
     
  2. @iBo3oF you can find the amount of veins generating in the BiomeDecorator class
    Code (Java):

    // The first parameter is the minable type
    // The second parameter is the amount of blocks each vein can have
    //
    // Do note, internally they are programmed to only replace
    // stone blocks
    protected WorldGenerator h = new WorldGenMinable(Blocks.DIRT, 32);
    protected WorldGenerator i = new WorldGenMinable(Blocks.GRAVEL, 32);
    protected WorldGenerator j = new WorldGenMinable(Blocks.COAL_ORE, 16);
    protected WorldGenerator k = new WorldGenMinable(Blocks.IRON_ORE, 8);
    protected WorldGenerator l = new WorldGenMinable(Blocks.GOLD_ORE, 8);
    protected WorldGenerator m = new WorldGenMinable(Blocks.REDSTONE_ORE, 7);
    protected WorldGenerator n = new WorldGenMinable(Blocks.DIAMOND_ORE, 7);
    protected WorldGenerator o = new WorldGenMinable(Blocks.LAPIS_ORE, 6);

    /**
    * Places an amount of veins in the chunk
    * @param paramInt1 The amount of veins in the chunk
    * @param paramWorldGenerator The WorldGenerator used - see the comments in a()
    *                            for more information on which exact Material
    * @param paramInt2 The minimum height in the chunk
    * @param paramInt3 The maximum height in the chunk
    */

    protected void a(int paramInt1, WorldGenerator paramWorldGenerator, int paramInt2, int paramInt3)
    {
        for (int i1 = 0; i1 < paramInt1; i1++)
        {
            // Randomised X (world coordinates! NOT chunk coordinates)
            int i2 = this.c + this.b.nextInt(16);
            // Randomised Y
            int i3 = this.b.nextInt(paramInt3 - paramInt2) + paramInt2;
            // Randomised Z (world coordinates! NOT chunk coordinates)
            int i4 = this.d + this.b.nextInt(16);
            paramWorldGenerator.generate(this.a, this.b, i2, i3, i4);
        }
    }

    /**
    * Places an amount of veins in the chunk
    * @param paramInt1 The amount of veins in the chunk
    * @param paramWorldGenerator The WorldGenerator used - see the comments in a()
    *                            for more information on which exact Material
    * @param paramInt2 The difference between paramInt2 and paramInt3 is the
    *                  minimum height
    * @param paramInt3 Twice this value plus the difference between paramInt2
    *                  and paramInt3 is the maximum height
    */

    protected void b(int paramInt1, WorldGenerator paramWorldGenerator, int paramInt2, int paramInt3)
    {
        for (int i1 = 0; i1 < paramInt1; i1++)
        {
            // Randomised X (world coordinates! NOT chunk coordinates)
            int i2 = this.c + this.b.nextInt(16);
            // Randomised Y
            int i3 = this.b.nextInt(paramInt3) + this.b.nextInt(paramInt3) + (paramInt2 - paramInt3);
            // Randomised Z (world coordinates! NOT chunk coordinates)
            int i4 = this.d + this.b.nextInt(16);
            paramWorldGenerator.generate(this.a, this.b, i2, i3, i4);
        }
    }


    protected void a()
    {
        // Applying the info from before, this will attempt to place
        // 20 dirt veins between y=0 and y=255 (since randoms are from 0 to n-1)
        a(20, this.h, 0, 256); // Dirt
        a(10, this.i, 0, 256); // Gravel
        a(20, this.j, 0, 128); // Coal
        a(20, this.k, 0, 64);  // Iron ore
        a(2, this.l, 0, 32);   // Gold ore
        a(8, this.m, 0, 16);   // Redstone ore
        a(1, this.n, 0, 16);   // Diamond ore
        b(1, this.o, 16, 16);  // Lapis Lazuli ore
    }
    So what can we achieve with this pile of information? It seems that each BiomeBase extending class has a field 'ar' of type BiomeDecorator. Inside we will find the method a() and the WorldGenMinable fields which we can override/reassign since they are not final. This allows us to use NMS (or reflection if you are into that kind of stuff - it will be required) to access these fields and modify them \o/.

    So assume an arbitrary BiomeBase, yes? To gain it's BiomeDecorator, you can just access that field since it's public
    Code (Java):
    public void moarDiamonds(BiomeBase biome)
    {
        BiomeDecorator decorator = biome.ar; // The assignment is only for show :P
    }
    Next up we want to (as my choice of method name suggests) have bigger diamond ore veins. Since I want to write this in a way that we can override some methods, we are going to need our extension of BiomeDecorator.
    Code (Java):

    public class MyBiomeDecorator extends BiomeDecorator
    {
        public MyBiomeDecorator()
        {
            this.n = new WorldGenMinable(Blocks.DIAMOND_ORE, 14);
        }
    }
     
    In this snippet I let my constructor - which should be called after the class body inits - override n with a newly created WorldGenMinable that has a max vein size of 14 blocks. (The fact that all veins use the same class leads me to believe that this is enough - if that assumption is wrong, feel free to correct me)

    Now we assign it in the method we declared earlier
    Code (Java):
    public void moarDiamonds(BiomeBase biome)
    {
        biome.ar = new MyBiomeDecorator();
    }
    Easy, right? Now what if we want to generate diamonds everywhere, like dirt. Simply override 'protected void a()' - but since we don't want to generate the diamonds twice, we will be copying the method
    Code (Java):

    public class MyBiomeDecorator extends BiomeDecorator
    {
        public MyBiomeDecorator()
        {
            this.n = new WorldGenMinable(Blocks.DIAMOND_ORE, 14);
        }

        @Override
        public void a()
        {
            a(20, this.h, 0, 256);
            a(10, this.i, 0, 256);
            a(20, this.j, 0, 128);
            a(20, this.k, 0, 64);
            a(2, this.l, 0, 32);
            a(8, this.m, 0, 16);
            // Like shown in the first huge snippet: these are diamonds
            // We modify the max vein count to generate more veins, and
            // the max height to get it to generate higher up
            // O look, diamonds -should- be almost as common as
            // dirt at this point
            a(20, this.n, 0, 256);
            b(1, this.o, 16, 16);
        }
    }
    Now this time, we should use public since that Minecraft will attempt to access it while it's outside the package.

    I think that pretty much covers it. I do need to note that the end has it's own BiomeDecorator (BiomeTheEndDecorator) and that I haven't tested all of this code (so feedback would be nice, lol. If I get nothing I'll be testing it in the weekend).

    O, and one last thing: set your plugin to run on STARTUP instead of POSTWORLD, since you would like to intercept all world generating code (or so I assume).

    [Note #1] If you use BiomeBase.getBiomes() to iterate, note that the majority if the entries will be null (due to an overflow in allocation by Minecraft). So don't forget to check for null to prevent NullPointerExceptions!
    [Note #2] Now tested and confirmed working:
    [​IMG]
    This is a screenshot from the effects of the ore override - diamonds are now available in the top layers and the first vein I inspected had 13 ores.
     
    #2 DarkSeraphim, Sep 26, 2014
    Last edited: Sep 27, 2014
    • Like Like x 4
    • Winner Winner x 4
    • Useful Useful x 3