Solved Bukkit world generation gives up

Discussion in 'Plugin Development' started by MCMastery, Jan 30, 2020.

Thread Status:
Not open for further replies.
  1. Offline

    MCMastery

    Hello, I am trying to make a simple mining world with stone and ores. Here is my chunk generator:
    Code:
    public class MiningWorld extends ChunkGenerator {
        // terrain levels. uncommon to actually reach these extremes
        public static final int MIN_LEVEL = 30, MAX_LEVEL = 150;
        public static final String WORLD_NAME = "mining";
    
        // loads the world; if the world has not been created yet, this generates it and then loads it
        public static World load() {
            return Bukkit.createWorld(new WorldCreator(WORLD_NAME).generator(new MiningWorld()));
        }
    
        @Override
        public ChunkData generateChunkData(World world, Random random, int chunkX, int chunkZ, BiomeGrid biome) {
            SimplexOctaveGenerator generator = new SimplexOctaveGenerator(world, 8);
            ChunkData chunk = createChunkData(world);
            generator.setScale(0.003D);
    
            for (int x = 0; x < 16; x++) {
                for (int z = 0; z < 16; z++) {
                    int worldX = x + chunkX * 16, worldZ = z + chunkZ * 16;
                    double d = (generator.noise(worldX, worldZ, 0.5, 0.5, true) + 1) / 2; // convert to [0,1]
                    int height = (int) (d * (MAX_LEVEL - MIN_LEVEL) + MIN_LEVEL);
    
                    // ground
                    boolean gravel = random.nextBoolean();
                    Material ground = gravel ? Material.GRAVEL : Material.STONE;
                    chunk.setBlock(x, height, z, ground);
    
                    // underground
                    chunk.setBlock(x, height - 1, z, Material.STONE);
                    for (int i = height - 2; i > 0; i--)
                        chunk.setBlock(x, i, z, Material.STONE);
                    chunk.setBlock(x, 0, z, Material.BEDROCK);
                }
            }
            return chunk;
        }
    
        @Override
        public List<BlockPopulator> getDefaultPopulators(World world) {
            return List.of(new MiningOrePopulator());
        }
    }

    Here is MiningOrePopulator:
    Code:
    public class MiningOrePopulator extends BlockPopulator {
        // y-coordinates
        private static final int DIAMOND_MIN = 5, DIAMOND_MAX = 30;
        // vein chance: chance of continuing the vein
        private static final double DIAMOND_CHANCE = 0.10, DIAMOND_VEIN_CHANCE = 0.40;
        private static final int GOLD_MIN = 20, GOLD_MAX = 60;
        private static final double GOLD_CHANCE = 0.30, GOLD_VEIN_CHANCE = 0.60;
        private static final int IRON_MIN = 30, IRON_MAX = 90;
        private static final double IRON_CHANCE = 0.60, IRON_VEIN_CHANCE = 0.8;
        private static final int COAL_MIN = 40, COAL_MAX = 150;
        private static final double COAL_CHANCE = 1.00, COAL_VEIN_CHANCE = 0.9;
        public static int diamonds = 0, gold = 0, iron = 0, coal = 0, chunks = 0;
    
        private void generateVein(Random random, Chunk chunk, int minY, int maxY, double veinChance, Material material) {
            int x, y, z;
            x = random.nextInt(16);
            z = random.nextInt(16);
            y = random.nextInt(maxY - minY) + minY;  // Get randomized coordinates
            int maxVeinSize = 16;
            int i = 0;
            if (chunk.getBlock(x, y, z).getType() == Material.STONE) {
                boolean isStone = true;
                while (isStone && i++ < maxVeinSize) {
                    chunk.getBlock(x, y, z).setType(material);
                    if (material == Material.COAL_ORE)
                        coal++;
                    else if (material == Material.IRON_ORE)
                        iron++;
                    else if (material == Material.GOLD_ORE)
                        gold++;
                    else if (material == Material.DIAMOND_ORE)
                        diamonds++;
    
                    if (random.nextDouble() < veinChance)  {   // The chance of continuing the vein
                        switch (random.nextInt(6)) {  // The direction chooser
                            case 0: x++; break;
                            case 1: y++; break;
                            case 2: z++; break;
                            case 3: x--; break;
                            case 4: y--; break;
                            case 5: z--; break;
                        }
                        isStone = (chunk.getBlock(x, y, z).getType() == Material.STONE) && (chunk.getBlock(x, y, z).getType() != material);
                    } else isStone = false;
                }
            }
        }
    
        @Override
        public void populate(World world, Random random, Chunk chunk) {
            chunks++;
            for (int i = 0; i < 32; i++) {
                double d = random.nextDouble();
                if (d < DIAMOND_CHANCE)
                    generateVein(random, chunk, DIAMOND_MIN, DIAMOND_MAX, DIAMOND_VEIN_CHANCE, Material.DIAMOND_ORE);
                else if (d < GOLD_CHANCE)
                    generateVein(random, chunk, GOLD_MIN, GOLD_MAX, GOLD_VEIN_CHANCE, Material.GOLD_ORE);
                else if (d < IRON_CHANCE)
                    generateVein(random, chunk, IRON_MIN, IRON_MAX, IRON_VEIN_CHANCE, Material.IRON_ORE);
                else if (d < COAL_CHANCE)
                    generateVein(random, chunk, COAL_MIN, COAL_MAX, COAL_VEIN_CHANCE, Material.COAL_ORE);
            }
            System.out.println("Populated chunk " + chunk.getX() + ", " + chunk.getZ());
        }
    }
    It generates chunks for about 5 seconds, then slows down and stops. If I look at the memory usage, it hits a ceiling (even though it's only like 40% memory usage) and stops responding. Eventually it says Service-Worker-1 stopped working.

    In the populate method, if I change it to loop only 8 times, it does finish generating it, but does not have nearly enough ore for the chunk.

    There must be some way to get this working. Maybe I could optimize it even further but I don't see anything obvious. Thank you



    EDIT: Here's a picture of the server GUI after it freezes: [​IMG]

    Solved.
    BlockPopulators are slow. Updated:
    Code:
    public class MiningOrePopulator {
        // y-coordinates
        private static final int DIAMOND_MIN = 5, DIAMOND_MAX = 30;
        // vein chance: chance of continuing the vein
        private static final double DIAMOND_CHANCE = 0.10, DIAMOND_VEIN_CHANCE = 0.40;
        private static final int GOLD_MIN = 20, GOLD_MAX = 60;
        private static final double GOLD_CHANCE = 0.30, GOLD_VEIN_CHANCE = 0.60;
        private static final int IRON_MIN = 30, IRON_MAX = 90;
        private static final double IRON_CHANCE = 0.60, IRON_VEIN_CHANCE = 0.8;
        private static final int COAL_MIN = 40, COAL_MAX = 150;
        private static final double COAL_CHANCE = 1.00, COAL_VEIN_CHANCE = 0.9;
        public static int diamonds = 0, gold = 0, iron = 0, coal = 0, chunks = 0;
    
        public void generateVein(Random random, ChunkGenerator.ChunkData chunk, int minY, int maxY, double veinChance, Material material) {
            int x, y, z;
            x = random.nextInt(16);
            z = random.nextInt(16);
            y = random.nextInt(maxY - minY) + minY;  // Get randomized coordinates
            int maxVeinSize = 16;
            int i = 0;
            if (chunk.getType(x, y, z) == Material.STONE) {
                boolean isStone = true;
                while (isStone && i++ < maxVeinSize) {
                    chunk.setBlock(x, y, z, material);
                    if (material == Material.COAL_ORE)
                        coal++;
                    else if (material == Material.IRON_ORE)
                        iron++;
                    else if (material == Material.GOLD_ORE)
                        gold++;
                    else if (material == Material.DIAMOND_ORE)
                        diamonds++;
    
                    if (random.nextDouble() < veinChance)  {   // The chance of continuing the vein
                        switch (random.nextInt(6)) {  // The direction chooser
                            case 0: x++; break;
                            case 1: y++; break;
                            case 2: z++; break;
                            case 3: x--; break;
                            case 4: y--; break;
                            case 5: z--; break;
                        }
                        isStone = (chunk.getType(x, y, z) == Material.STONE) && (chunk.getType(x, y, z) != material);
                    } else isStone = false;
                }
            }
        }
    
        public void populate(World world, Random random, ChunkGenerator.ChunkData chunk) {
            chunks++;
            for (int i = 0; i < 32; i++) {
                double d = random.nextDouble();
                if (d < DIAMOND_CHANCE)
                    generateVein(random, chunk, DIAMOND_MIN, DIAMOND_MAX, DIAMOND_VEIN_CHANCE, Material.DIAMOND_ORE);
                else if (d < GOLD_CHANCE)
                    generateVein(random, chunk, GOLD_MIN, GOLD_MAX, GOLD_VEIN_CHANCE, Material.GOLD_ORE);
                else if (d < IRON_CHANCE)
                    generateVein(random, chunk, IRON_MIN, IRON_MAX, IRON_VEIN_CHANCE, Material.IRON_ORE);
                else if (d < COAL_CHANCE)
                    generateVein(random, chunk, COAL_MIN, COAL_MAX, COAL_VEIN_CHANCE, Material.COAL_ORE);
            }
        }
    }
    
    I added this after the loop in the chunk generator:

    Code:
    orePopulator.populate(world, random, chunk);
    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jan 30, 2020
Thread Status:
Not open for further replies.

Share This Page