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; }
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
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?
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.
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.
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.
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.
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.
It's a shame, because without BlockPopulator functioning as intended, there's currently no standard way to generate structures across chunk borders.
@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.
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.