rutgerkok / WorldGeneratorApi

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

Using getBiome/TerrainGenerator in world creator at beginning fails. #16

Closed RingOfStorms closed 4 years ago

RingOfStorms commented 4 years ago

Describe the bug In the wiki it describes modifying a vanilla generator: https://github.com/rutgerkok/WorldGeneratorApi/wiki/Biome-generators#modifying-the-vanilla-biome-generator

To do so you are supposed to just call the getBiomeGenerator before the set is called to get the vanilla generator.

This does not seem to work when working within a WorldCreator, only on a World that has already been made.

The working example below is not good because the server will have already generated a handful of chunks with the vanilla generator before WGApi is able to inject the custom one in, thus resulting in several invalid chunks.

To Reproduce Working code:

World w = WorldCreator
        .name("test")
        .createWorld();
var wi = WorldGeneratorApi.getInstance(PLUGIN.instance(), 0, 5).getForWorld(w);
var vanillaBiomes = wi.getBiomeGenerator();
wi.setBiomeGenerator((x, y, z) -> vanillaBiomes.getZoomedOutBiome(x, y, z) == Biome.FOREST ? Biome.ICE_SPIKES : Biome.DARK_FOREST_HILLS);

Not working code:

World w = WorldCreator
        .name("test")
        .generator(WorldGeneratorApi
                .getInstance(EmpiresGeneratorPlugin.instance(), 0, 5)
                .createCustomGenerator(WorldRef.ofName(name), generator -> {
                    var vanillaBiomes = generator.getBiomeGenerator();
                    generator.setBiomeGenerator((x, y, z) -> vanillaBiomes.getZoomedOutBiome(x, y, z) == Biome.FOREST ? Biome.ICE_SPIKES : Biome.DARK_FOREST_HILLS);
                })
        )
        .createWorld();

The not working code throws the error

[21:22:45 ERROR]: [PLUGIN] [ACF] Caused by: java.lang.UnsupportedOperationException: Cannot extract base chunk generator from class org.bukkit.craftbukkit.v1_15_R1.generator.CustomChunkGenerator. 
[21:22:45 ERROR]: [PLUGIN] [ACF] You can only customize worlds where the base terrain is generated by Minecraft or using the WorldGeneratorApi methods. If you are using the WorldGeneratorApi methods, make sure that a BaseChunkGenerator was set before other aspects of terrain generation were modified.
RingOfStorms commented 4 years ago

Forgot to mention. The above bug extends to getBaseTerrainGenerator as well, not just the biome generator

rutgerkok commented 4 years ago

The issue is actually in setBiomeGenerator. If you use WorldGeneratorApi.createCustomGenerator you need to set a BaseTerrainGenerator before you set a BiomeGenerator. This is an annoying techical limitation. Basically, when supplying a custom ChunkGenerator to a world (for example with WorldCreator.generator(...), the vanilla chunk generator is deleted. This is a problem, as then there is nothing to inject your custom BiomeGenerator into. (And besides, the world won't generate at all.)

If you only want to change the biome generator, then the best way would be to listen to the WorldGeneratorInitEvent. The workaround you found should work too, but I guess the WorldGeneratorInitEvent will be fired a bit earlier.

I've now changed the error message to make things more clear. I'll also update the wiki.

RingOfStorms commented 4 years ago

Thanks, the listener approach worked for me. I made a (somewhat ugly) fairly portable class you can use for this:

private static class WorldGeneratorInjector implements Listener, AutoCloseable {
    private final String world;
    private final Consumer<WorldGenerator> generatorConsumer;

    WorldGeneratorInjector(String world, Consumer<WorldGenerator> generatorConsumer) {
        this.world = world;
        this.generatorConsumer = generatorConsumer;
        PLUGIN.instance().getServer().getPluginManager().registerEvents(this, PLUGIN.instance());
    }

    @EventHandler
    public void onWorldGen(WorldGeneratorInitEvent e) {
        if (e.getWorld().getName().equals(world)) {
            Bukkit.broadcastMessage("world gen init event");
            generatorConsumer.accept(e.getWorldGenerator());
        }
    }

    @Override
    public void close() {
        HandlerList.unregisterAll(this);
    }
}

and to use it

try (var ignored = new WorldGeneratorInjector(
        worldName,
        worldGenerator ->
                // Kind of loaded line, but this will set the biome generator to NoOceans, which is a biome generator that expects the vanilla generator passed into it.
                worldGenerator.setBiomeGenerator(new NoOceans(worldGenerator.getBiomeGenerator()))
)) {
    world = WorldCreator
            .name(worldName)
            .createWorld();
}
RingOfStorms commented 4 years ago

@rutgerkok I take it back. The initial handful of chunks still generate even when using the event above :(

Trying to figure out alternatives to get in before those initial chunks are made and still override default behavior.

The event above will let me use the vanilla generators, but I end up with corrupt chunks around the spawn.

rutgerkok commented 4 years ago

Could that be related to #13 ? If yes, then using the latest dev build should fix it.

RingOfStorms commented 4 years ago

@rutgerkok Thanks, latest versions seem to be fixing the issues!