From b12fe77f32c5012842edd84217d74f332976688b Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Wed, 4 Jun 2025 21:25:35 +0200 Subject: [PATCH] feat: add fine-grained biome control to Minestom world builder Introduced a `doFineGrainedBiomes` flag to allow fine-grained biome control per chunk. This helps mitigate client disconnection issues caused by a Minestom biome encoding bug, with a plan to deprecate once the bug is resolved. Adjusted relevant classes and the example implementation to support this feature. --- .../terra/minestom/TerraMinestomExample.java | 5 +++- .../api/TerraMinestomWorldBuilder.java | 16 ++++++++++- .../biome/MinestomUserDefinedBiomePool.java | 5 ++-- .../world/MinestomChunkGeneratorWrapper.java | 27 ++++++++++++++++--- .../minestom/world/TerraMinestomWorld.java | 6 +++-- 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/platforms/minestom/example/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java b/platforms/minestom/example/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java index afa1f0627..2da66d2c9 100644 --- a/platforms/minestom/example/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java +++ b/platforms/minestom/example/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java @@ -6,6 +6,7 @@ import net.minestom.server.command.builder.Command; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.GameMode; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; +import net.minestom.server.event.player.PlayerDisconnectEvent; import net.minestom.server.event.player.PlayerSpawnEvent; import net.minestom.server.instance.Instance; import net.minestom.server.instance.LightingChunk; @@ -23,6 +24,7 @@ public class TerraMinestomExample { private final MinecraftServer server = MinecraftServer.init(); private Instance instance; private TerraMinestomWorld world; + private final TerraMinestomPlatform platform = new TerraMinestomPlatform(); public void createNewInstance() { instance = MinecraftServer.getInstanceManager().createInstanceContainer(); @@ -30,9 +32,9 @@ public class TerraMinestomExample { } public void attachTerra() { - TerraMinestomPlatform platform = new TerraMinestomPlatform(); world = platform.worldBuilder(instance) .defaultPack() + .doFineGrainedBiomes(false) .attach(); } @@ -122,6 +124,7 @@ public class TerraMinestomExample { private void regenerate() { instance.sendMessage(Component.text("Regenerating world")); Instance oldInstance = instance; + platform.reload(); createNewInstance(); attachTerra(); preloadWorldAndMeasure(); diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/TerraMinestomWorldBuilder.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/TerraMinestomWorldBuilder.java index a3f3354d3..c572458b7 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/TerraMinestomWorldBuilder.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/api/TerraMinestomWorldBuilder.java @@ -24,6 +24,7 @@ public class TerraMinestomWorldBuilder { private EntityFactory entityFactory = new DefaultEntityFactory(); private BlockEntityFactory blockEntityFactory = new DefaultBlockEntityFactory(); private BiomeFactory biomeFactory = new MinestomUserDefinedBiomeFactory(); + private boolean doFineGrainedBiomes = true; public TerraMinestomWorldBuilder(TerraMinestomPlatform platform, Instance instance) { this.platform = platform; @@ -70,7 +71,20 @@ public class TerraMinestomWorldBuilder { return this; } + /** + * Due to a current bug with the minestom biome encoder, sometimes, the client gets kicked when decoding a chunk + * packet with more than one biome. Until this is fixed in minestom, one can disable fine-grained biomes to prevent + * this issue. + * + * @deprecated Scheduled for removal once Minestom rolls out a fix + */ + @Deprecated + public TerraMinestomWorldBuilder doFineGrainedBiomes(boolean doFineGrainedBiomes) { + this.doFineGrainedBiomes = doFineGrainedBiomes; + return this; + } + public TerraMinestomWorld attach() { - return new TerraMinestomWorld(platform, instance, pack, seed, entityFactory, blockEntityFactory, biomeFactory); + return new TerraMinestomWorld(platform, instance, pack, seed, entityFactory, blockEntityFactory, biomeFactory, doFineGrainedBiomes); } } diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomePool.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomePool.java index 9eff185b3..138d98887 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomePool.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/biome/MinestomUserDefinedBiomePool.java @@ -2,16 +2,17 @@ package com.dfsek.terra.minestom.biome; import com.dfsek.terra.api.config.ConfigPack; import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.minestom.api.BiomeFactory; import java.util.HashMap; public class MinestomUserDefinedBiomePool { private final HashMap biomes = new HashMap<>(); - private final MinestomUserDefinedBiomeFactory factory; + private final BiomeFactory factory; private final ConfigPack configPack; - public MinestomUserDefinedBiomePool(ConfigPack configPack, MinestomUserDefinedBiomeFactory factory) { + public MinestomUserDefinedBiomePool(ConfigPack configPack, BiomeFactory factory) { this.configPack = configPack; this.factory = factory; } diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java index 0e2109b99..6c72a3326 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java @@ -26,6 +26,7 @@ public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrappe private ChunkGenerator generator; private final TerraMinestomWorld world; private final BiomeProvider biomeProvider; + private final boolean doFineGrainedBiomes; private ConfigPack pack; private final MinestomUserDefinedBiomePool biomePool; @@ -34,13 +35,15 @@ public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrappe ChunkGenerator generator, TerraMinestomWorld world, ConfigPack pack, - MinestomUserDefinedBiomePool biomePool + MinestomUserDefinedBiomePool biomePool, + boolean doFineGrainedBiomes ) { this.generator = generator; this.world = world; this.pack = pack; this.biomePool = biomePool; - biomeProvider = pack.getBiomeProvider().caching(platform); + this.biomeProvider = pack.getBiomeProvider(); + this.doFineGrainedBiomes = doFineGrainedBiomes; this.cache = new GeneratedChunkCache(world.getDimensionType(), generator, world, biomeProvider); preloadBiomes(); } @@ -56,12 +59,28 @@ public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrappe int z = start.chunkZ(); int blockX = start.blockX(); int blockZ = start.blockZ(); + int minY = world.getMinHeight(); + int maxY = world.getMaxHeight(); CachedChunk chunk = cache.at(x, z); UnitModifier modifier = unit.modifier(); chunk.writeRelative(modifier); - UserDefinedBiome userDefinedBiome = biomePool.getBiome(biomeProvider.getBiome(blockX, 100, blockZ, world.getSeed())); - modifier.fillBiome(userDefinedBiome.registry()); + if(doFineGrainedBiomes) { + for(int y = minY; y < maxY; y++) { + for(int absoluteX = blockX; absoluteX < blockX + 16; absoluteX++) { + for(int absoluteZ = blockZ; absoluteZ < blockZ + 16; absoluteZ++) { + UserDefinedBiome userDefinedBiome = biomePool.getBiome( + biomeProvider.getBiome(absoluteX, y, absoluteZ, world.getSeed()) + ); + modifier.setBiome(absoluteX, y, absoluteZ, userDefinedBiome.registry()); + } + } + } + } else { + // TODO: remove with feature flag once minestom fixed biome encoding + UserDefinedBiome userDefinedBiome = biomePool.getBiome(biomeProvider.getBiome(blockX, 100, blockZ, world.getSeed())); + modifier.fillBiome(userDefinedBiome.registry()); + } unit.fork(setter -> { MinestomProtoWorld protoWorld = new MinestomProtoWorld(cache, x, z, world, setter); diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java index 88691e362..df38b5dd2 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java @@ -48,7 +48,8 @@ public final class TerraMinestomWorld implements ServerWorld, WorldProperties { long seed, EntityFactory entityFactory, BlockEntityFactory blockEntityFactory, - BiomeFactory factory + BiomeFactory factory, + boolean doFineGrainedBiomes ) { this.instance = instance; this.pack = pack; @@ -62,7 +63,8 @@ public final class TerraMinestomWorld implements ServerWorld, WorldProperties { pack.getGeneratorProvider().newInstance(pack), this, pack, - new MinestomUserDefinedBiomePool(pack, new MinestomUserDefinedBiomeFactory()) + new MinestomUserDefinedBiomePool(pack, factory), + doFineGrainedBiomes ); this.entityFactory = entityFactory;