Merge pull request #513 from everbuild-org/fix/minestom-fine-grained-biome

minestom: remove fine-grained biome control from minestom
This commit is contained in:
Zoë Gidiere 2025-07-13 16:44:25 -06:00 committed by GitHub
commit 2d42810ba3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 82 additions and 47 deletions

View File

@ -89,6 +89,6 @@ object Versions {
}
object Minestom {
const val minestom = "1_21_6-c3ccee696b"
const val minestom = "1_21_6-a40d7115d4"
}
}

View File

@ -44,7 +44,6 @@ public class TerraMinestomExample {
public void attachTerra() {
world = platform.worldBuilder(instance)
.defaultPack()
.doFineGrainedBiomes(false)
.attach();
}

View File

@ -22,7 +22,6 @@ public class TerraMinestomWorldBuilder {
private EntityFactory entityFactory = new DefaultEntityFactory();
private BlockEntityFactory blockEntityFactory;
private BiomeFactory biomeFactory = new MinestomUserDefinedBiomeFactory();
private boolean doFineGrainedBiomes = true;
public TerraMinestomWorldBuilder(TerraMinestomPlatform platform, Instance instance) {
this.platform = platform;
@ -70,20 +69,7 @@ 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, doFineGrainedBiomes);
return new TerraMinestomWorld(platform, instance, pack, seed, entityFactory, blockEntityFactory, biomeFactory);
}
}

View File

@ -1,6 +1,8 @@
package com.dfsek.terra.minestom.biome;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.world.biome.Biome;
@ -8,7 +10,8 @@ import com.dfsek.terra.minestom.api.BiomeFactory;
public class MinestomUserDefinedBiomePool {
private final HashMap<String, UserDefinedBiome> biomes = new HashMap<>();
private final IdentityHashMap<Biome, UserDefinedBiome> biomes = new IdentityHashMap<>();
private final HashSet<String> createdBiomes = new HashSet<>();
private final BiomeFactory factory;
private final ConfigPack configPack;
@ -18,18 +21,21 @@ public class MinestomUserDefinedBiomePool {
}
public UserDefinedBiome getBiome(Biome source) {
UserDefinedBiome userDefinedBiome = biomes.get(source.getID());
UserDefinedBiome userDefinedBiome = biomes.get(source);
if(userDefinedBiome != null) return userDefinedBiome;
userDefinedBiome = factory.create(configPack, source);
biomes.put(source.getID(), userDefinedBiome);
biomes.put(source, userDefinedBiome);
createdBiomes.add(source.getID());
return userDefinedBiome;
}
public void preloadBiomes(Iterable<Biome> biomesToLoad) {
biomesToLoad
.forEach(biome -> {
if(!this.biomes.containsKey(biome.getID())) {
this.biomes.put(biome.getID(), factory.create(configPack, biome));
if(!this.createdBiomes.contains(biome.getID())) {
UserDefinedBiome udf = factory.create(configPack, biome);
this.biomes.put(biome, udf);
this.createdBiomes.add(biome.getID());
}
});
}

View File

@ -1,9 +1,49 @@
package com.dfsek.terra.minestom.biome;
import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.RegistryKey;
import net.minestom.server.world.biome.Biome;
public record UserDefinedBiome(Key key, RegistryKey<Biome> registry, String id, Biome biome) {
public class UserDefinedBiome {
private static final DynamicRegistry<Biome> BIOME_REGISTRY = MinecraftServer.getBiomeRegistry();
private final Key key;
private final RegistryKey<Biome> registry;
private final String id;
private final Biome biome;
private int registryId = -1;
public UserDefinedBiome(Key key, RegistryKey<Biome> registry, String id, Biome biome) {
this.key = key;
this.registry = registry;
this.id = id;
this.biome = biome;
}
public Key key() {
return key;
}
public RegistryKey<Biome> registryKey() {
return registry;
}
public String id() {
return id;
}
public Biome biome() {
return biome;
}
public int registryId() {
if(registryId == -1) {
registryId = BIOME_REGISTRY.getId(registry);
}
return registryId;
}
}

View File

@ -1,9 +1,13 @@
package com.dfsek.terra.minestom.world;
import net.minestom.server.coordinate.Point;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import net.minestom.server.instance.generator.Generator;
import net.minestom.server.instance.generator.GeneratorImpl.AreaModifierImpl;
import net.minestom.server.instance.generator.GeneratorImpl.SectionModifierImpl;
import net.minestom.server.instance.generator.UnitModifier;
import net.minestom.server.instance.palette.Palette;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.api.Platform;
@ -22,25 +26,21 @@ public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrappe
private final GeneratedChunkCache cache;
private final TerraMinestomWorld world;
private final BiomeProvider biomeProvider;
private final boolean doFineGrainedBiomes;
private final MinestomUserDefinedBiomePool biomePool;
private ChunkGenerator generator;
private ConfigPack pack;
public MinestomChunkGeneratorWrapper(
Platform platform,
ChunkGenerator generator,
TerraMinestomWorld world,
ConfigPack pack,
MinestomUserDefinedBiomePool biomePool,
boolean doFineGrainedBiomes
MinestomUserDefinedBiomePool biomePool
) {
this.generator = generator;
this.world = world;
this.pack = pack;
this.biomePool = biomePool;
this.biomeProvider = pack.getBiomeProvider();
this.doFineGrainedBiomes = doFineGrainedBiomes;
this.cache = new GeneratedChunkCache(world.getDimensionType(), generator, world, biomeProvider);
preloadBiomes();
}
@ -49,6 +49,7 @@ public class MinestomChunkGeneratorWrapper implements Generator, GeneratorWrappe
return generator;
}
@SuppressWarnings("UnstableApiUsage")
@Override
public void generate(@NotNull GenerationUnit unit) {
Point start = unit.absoluteStart();
@ -56,27 +57,33 @@ 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);
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());
AreaModifierImpl areaModifiers = (AreaModifierImpl) modifier;
for(GenerationUnit section : areaModifiers.sections()) {
SectionModifierImpl sectionModifier = (SectionModifierImpl) section.modifier();
Palette biomes = sectionModifier.genSection().biomes();
int minY = section.absoluteStart().blockY();
for(int relativeX = 0; relativeX < 16; relativeX += 1) {
int absoluteX = blockX + relativeX;
for(int relativeZ = 0; relativeZ < 16; relativeZ += 1) {
int absoluteZ = blockZ + relativeZ;
for(int relativeY = 0; relativeY < 16; relativeY += 1) {
int absoluteY = minY + relativeY;
if (relativeX % 4 == 0 && relativeY % 4 == 0 && relativeZ % 4 == 0) {
UserDefinedBiome userDefinedBiome = biomePool.getBiome(
biomeProvider.getBiome(absoluteX, absoluteY, absoluteZ, world.getSeed())
);
int registryId = userDefinedBiome.registryId();
biomes.set(relativeX / 4, relativeY / 4, relativeZ / 4, registryId);
}
}
}
}
} 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 -> {

View File

@ -44,8 +44,7 @@ public final class TerraMinestomWorld implements ServerWorld, WorldProperties {
long seed,
EntityFactory entityFactory,
BlockEntityFactory blockEntityFactory,
BiomeFactory factory,
boolean doFineGrainedBiomes
BiomeFactory factory
) {
this.instance = instance;
this.pack = pack;
@ -55,12 +54,10 @@ public final class TerraMinestomWorld implements ServerWorld, WorldProperties {
this.blockEntityFactory = blockEntityFactory;
this.wrapper = new MinestomChunkGeneratorWrapper(
platform,
pack.getGeneratorProvider().newInstance(pack),
this,
pack,
new MinestomUserDefinedBiomePool(pack, factory),
doFineGrainedBiomes
new MinestomUserDefinedBiomePool(pack, factory)
);
this.entityFactory = entityFactory;