Solved World Generation, trees not grouped together

Discussion in 'Spigot Plugin Development' started by Hex_27, Jan 14, 2020.

  1. upload_2020-1-14_16-31-4.png
    As shown above, the trees simply just don't bunch together, despite the whole region being in the right biome.
    Code (Java):
        @Override
        public void populate(World world, Random random, Chunk chunk) {
            if(!chunk.isLoaded()) return;
         
            FastNoise pathNoise = new FastNoise((int) (world.getSeed()*12));
            pathNoise.SetNoiseType(NoiseType.Simplex);
            pathNoise.SetFrequency(0.1f);
         
            boolean loaded = BlockUtils.areAdjacentChunksLoaded(chunk);
            for(int x = 0; x < 16; x++){
                for(int z = 0; z < 16; z++){
                    //Bukkit.broadcastMessage("1");
                    int y = GenUtils.getHighestGround(chunk, x, z);
                    Block base = chunk.getBlock(x,y,z);
                    if(pathNoise.GetNoise(x,z) > 0.5){
                        if(GenUtils.chance(random,99,100) &&
                                base.getBiome() == getBiome())
                            base.setType(Material.GRASS_PATH,false);
                    }
                    y++;
                    //Bukkit.broadcastMessage("2");
                    if(base.getType() == Material.GRASS_BLOCK){
                        //Bukkit.broadcastMessage("3");
                        if(loaded && GenUtils.chance(random,2,15)){
                            //Trees
                            Location loc = base.getRelative(0,1,0).getLocation();
                            world.generateTree(loc, TreeType.BIG_TREE);
                         
                        }else if(GenUtils.chance(random, 3, 10)){
                            //Grass & Flowers
                            chunk.getBlock(x, y, z).setType(Material.GRASS,false);
                            if(random.nextBoolean()){
                                BlockUtils.setDoublePlant(chunk, x, y, z, Material.TALL_GRASS);
                            }else{
                                chunk.getBlock(x, y, z).setType(pickFlower(), false);
                            }
                        }
                        if(GenUtils.chance(random,1,100) && loaded){

                            if(BlockUtils.isDirtLike(base.getType()) ||
                                    base.getType() == Material.COBBLESTONE||
                                    base.getType() == Material.MOSSY_COBBLESTONE||
                                    base.getType() == Material.STONE){
                                int ny = GenUtils.randInt(random, -1, 1);
                                spawnRock(random,base.getRelative(0,ny,0));
                                if(GenUtils.chance(random,1,3))
                                    spawnRock(random,base.getRelative(GenUtils.randInt(random, -1, 1),ny+1,GenUtils.randInt(random, -1, 1)));
                            }
                        }
                    }
                }
            }
        }
    I suspect it's because the loaded boolean is false, because the grasspaths and the grass still generate. But not the rocks and trees, which depend on that boolean. How do I get around it? Should I just make a runnable to keep trying or force a load of those chunks before generation?
    Code (Java):
        public static boolean areAdjacentChunksLoaded(Chunk middle){
            SimpleChunkLocation sc = new SimpleChunkLocation(middle);
         
            for(int nx = -1; nx <= 1; nx++){
                for(int nz = -1; nz <= 1; nz++){
                    int x = sc.getX() + nx;
                    int z = sc.getZ() + nz;
                    if(!middle.getWorld().isChunkLoaded(x, z))
                        return false;
                }
            }
         
            return true;
        }
     
    #1 Hex_27, Jan 14, 2020
    Last edited: Jan 14, 2020
  2. md_5

    Administrator Developer

    Can you explain the issue further, what is the expected result?
     
    • Friendly Friendly x 1
  3. According to the code that I wrote, in a forest biome, it should generate generate trees with a chance of 2/15 for every block on the surface. But based on the picture, it's only working for some chunks, and not all.

    I've checked the area with F3 in my client, and the blocks in those bare areas were indeed forest biomes, and my forest populator did generate the grass and flowers. However, the trees were grouped in dense areas with significant spaces between them, instead of being evenly distributed throughout the chunk
     
  4. md_5

    Administrator Developer

    Instead of a tree set a marker block to see if its an issue with your code or tree generation
     
  5. [​IMG]
    I think the problem lies with that adjacentchunksloaded method. Those chunks just didn't neighbour loaded chunks. It was a fix suggested to me from another thread, but it's the only fix I know regarding it. Is there something I can change about it to make it more reliable?
     
  6. Bump
     
    • Optimistic Optimistic x 1
  7. The areAdjacentChunksLoaded method doesn't really seem to make sense. If you're generating the world, a newly generated chunk will rarely have all 8 adjacent chunks load since they probably haven't even generated yet. I haven't really messed with chunk populating at all, but that's what I assume is happening.
     
  8. One would expect so, but apparently some really strange and inconsistent thing happens instead
    https://www.spigotmc.org/threads/bl...rror-when-setting-blocks.411745/#post-3656229.
    So it sort of DOES guarantee that the chunks nearby are generated but sometimes not loaded. Which is where that areAdjacentChunksLoaded check comes in.
     
    #13 Hex_27, Jan 19, 2020 at 5:59 AM
    Last edited: Jan 19, 2020 at 6:52 AM
  9. IMHO BlockPopulator should be removed, and instead have the lighting engine update the generated chunk right after the ChunkGenerator has provided it's ChunkData. And have generator plugins simply use the ChunkGenerator interface to generate everything. Maybe one use for the populator would be to spawn mobs and entities, but placing blocks with it is slow and useless.
     
    #14 the3rdnumber, Jan 19, 2020 at 9:27 PM
    Last edited: Jan 19, 2020 at 9:33 PM
  10. konsolas

    Supporter

    It would probably be a bad idea to remove parts of the API and break plugins which depend on it, but moving the BlockPopulator call to a stage where adjacent chunks can actually be accessed and modified (as the documentation suggests) and perhaps adding an option to recalculate lighting would be a good idea.
     
  11. Not actually remove it, but make it clear that it's not made for placing structures.

    In my world generator the only thing I use BlockPopulator is to refresh lava because the ChunkGenerator doesn't automatically calculate lighting, so generated lava was dark. Everything else is generated through the ChunkGenerator.
     
  12. konsolas

    Supporter

    It's a shame, because without BlockPopulator functioning as intended, there's currently no standard way to generate structures across chunk borders.
     
  13. md_5

    Administrator Developer

    Feel free to do this, much easier said than done
     
    • Agree Agree x 1
  14. konsolas

    Supporter

    @mbmorris claims to have found a workaround in this thread: https://www.spigotmc.org/threads/de...tetree-in-blockpopulator.370322/#post-3383196

    They add chunk coordinates to a queue when 'populate' is called on them, elements of which are consumed and then actually populated when all adjacent chunks have had 'populate' called.

    BlockPopulators are used by nms.Chunk.loadCallback(), which uses the flag needsDecoration to determine if a chunk is yet to be populated.

    mbmorris's workaround could be applied here by adding another flag, e.g. `loaded`, which is set to true on loadCallback. loadCallback could then check if the loading of the current chunk results in any adjacent chunks being surrounded by 4 loaded chunks, and then call populate on those chunks.

    Unfortunately I haven't signed the CLA yet, but it seems like this method would work.
     
  15. I don't think the API should have to take care of blocks going past the chunk border. If you're attempting to write a world generator then you should plan for everything so that all the API has to do is: API asks your plugin for a chunk -> you provided it -> and that's it.

    Applying things to already generated chunks, or ask the API to store things for you is too much asked imo.

    In my world generator project I solved this problem, but it required writing the generator's engine decoupled from Spigot. The idea was to have it produce final chunks at the API's request, without needing to fiddle with live chunks afterwards.
    In the end I'm glad I had to do that, it was the right way to design it (even if it took me 8 months). Since I had to design everything from scratch I decided to do it the best I could (flexibility performance, memory, etc.). I rewrote the thing 7 times by the end, but the current design is pretty good imo. My released demo generates a bit slower than Minecraft's generator, but that's with arguably much more performance intensive terrain and trees. If I was to generate something like vanilla terrain I think I'd surpass Minecraft's speed by quite a bit.

    I do want to release the engine as a framework, but I want it to be documented, mature and tested before I do so.
     
    #20 the3rdnumber, Jan 19, 2020 at 10:51 PM
    Last edited: Jan 19, 2020 at 11:16 PM