rutgerkok / WorldGeneratorApi

Minecraft Spigot plugin that enables other plugins to customize world generation
MIT License
96 stars 9 forks source link

BaseChunkGenerator sets bedrock decorator to the one for Environment.NORMAL #19

Closed ChimneySwift closed 4 years ago

ChimneySwift commented 4 years ago

Describe the bug Pretty clear what's happening from my screenshot.

Not entirely sure what is occurring since I never touched bedrock generation, though possibly the api isn't designed to work with the end/nether, apologies if I missed something.

@EventHandler
public void onWorldGenInit(WorldGeneratorInitEvent e) {
    World world = e.getWorld();
    e.getWorldGenerator().getWorldDecorator().withCustomBaseDecoration(BaseDecorationType.SURFACE, new ChunkLogger(this.plugin, world));
}

If you couldn't already tell, ChunkLogger only logs coordinates and doesn't touch any generation. For the end this is a fairly simple fix as I can just disable the bedrock generator, however in the nether there is also no bedrock roof.

I could resolve this by just generating a flat roof, or trying to reverse engineer the vanilla bedrock generator, but that seems like a pretty ugly solution?

To Reproduce Steps to reproduce the behavior:

  1. Create BaseChunkGenerator
  2. Apply custom BaseChunkGenerator to a nether/end world generator
  3. Visit the end floor/nether roof.

Screenshots

ChimneySwift commented 4 years ago

Think I might have found the reason why and suspect this may already be known.

This method is used by the api to generate the bedrock as opposed to the vanilla method, which checks to see what bedrock needs to be generated:

    private void a(IChunkAccess ichunkaccess, Random random) {
        MutableBlockPosition blockposition_mutableblockposition = new MutableBlockPosition();
        int i = ichunkaccess.getPos().d();
        int j = ichunkaccess.getPos().e();
        GeneratorSettingBase generatorsettingbase = (GeneratorSettingBase)this.h.get();
        int k = generatorsettingbase.f();
        int floorHeight = k;
        int l = this.x - 1 - generatorsettingbase.e();
        int roofHeight = l;
        boolean flag = true;
        boolean flag1 = l + 4 >= 0 && l < this.x;
        boolean flag2 = k + 4 >= 0 && k < this.x;
        if (flag1 || flag2) {
            Iterator iterator = BlockPosition.b(i, 0, j, i + 15, 0, j + 15).iterator();

            while(true) {
                BlockPosition blockposition;
                int i1;
                do {
                    if (!iterator.hasNext()) {
                        return;
                    }

                    blockposition = (BlockPosition)iterator.next();
                    if (flag1) {
                        for(i1 = 0; i1 < 5; ++i1) {
                            if (i1 <= (ichunkaccess.generateFlatBedrock() ? roofHeight : random.nextInt(5))) {
                                ichunkaccess.setType(blockposition_mutableblockposition.d(blockposition.getX(), l - i1, blockposition.getZ()), Blocks.BEDROCK.getBlockData(), false);
                            }
                        }
                    }
                } while(!flag2);

                for(i1 = 4; i1 >= 0; --i1) {
                    if (i1 <= (ichunkaccess.generateFlatBedrock() ? floorHeight : random.nextInt(5))) {
                        ichunkaccess.setType(blockposition_mutableblockposition.d(blockposition.getX(), k + i1, blockposition.getZ()), Blocks.BEDROCK.getBlockData(), false);
                    }
                }
            }
        }
    }

Not sure if it's possible to implement this in the api but I'll see if I can get something working, otherwise I'll just replace the default generator with my own based off vanilla behaviour.

Edit: Note I'm using Paper, so some things like ichunkaccess.generateFlatBedrock() are exclusive to Paper.

rutgerkok commented 4 years ago

Suprising to see that it already kind of works with other dimensions. At least, I see that the chorus plants are generating.

A quick workaround would be to just disable bedrock (generator.getWorldDecorator().withoutDefaultBaseDecorations(BaseDecorationType.BEDROCK);). But the proper fix would be to let the terrain generator use the settings from the correct dimension. If you can get that working, that would be great, otherwise I can also try to find some time for this.

ChimneySwift commented 4 years ago

Yeah I didn't notice any abnormalities in either of the other dimensions, just the bedrock which was reimplemented.

Honestly the way I'm using this isn't really as intended anyway so I'm pretty happy it's working at all at this point.

ChimneySwift commented 4 years ago

I've found a solution, though it's not very pretty. Not sure if there's a better solution however.

Turns out the reimplementation for bedrock generation was perfectly fine, however it was receiving generator settings meant for the overworld. I logged k (floorHeight) and l (roofHeight) in the bedrock generation function and noticed they always returned 0, 265 for all world environments, even though vanilla behaviour has these values set to 0, 127 for the nether and -10, 137 for the end.

I traced the creation of the GeneratorSettingBase class which is passed into InjectedChunkGenerator to WorldGeneratorImpl.extractSettings and noticed that while the method attempts to extract the vanilla settings from the provided ChunkGenerator, if during that process an error occured it would create a default, vanilla overworld one. The addition of a stacktrace told me it always did this, and I noticed that the ChunkGenerator class has no fields containing a GeneratorSettingBase, possibly a change to NMS that wasn't noticed because the setting extractor just handled it silently.

Anyway with the help of deobfuscated NMS cause the mappings spigot uses don't really do much for GeneratorSettingBase, I made 2 additional functions which do the same as the createDefaultSettings method but for the end and the nether. Some minor modifications to extractSettings and it returned the correct GeneratorSettingBase for nether/end dimensions.

I'll make a pull request with the changes to get it to work, but issue is that for some reason there's a function to get the GeneratorSettingBase defaults for the normal env, but no similar methods for the end or the nether, so generating new ones is much more messy.

Anyway, here you can see them working correctly: