From 7288373dbc9fe4ad7ed0624448148063937c1805 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Sat, 28 Dec 2024 18:18:17 +0100 Subject: [PATCH] feat: cache generated chunks This commit is in preparation of multi-layered worldgen with minestom --- platforms/minestom/build.gradle.kts | 5 +- .../minestom/block/MinestomBlockState.java | 7 +- .../terra/minestom/chunk/CachedChunk.java | 57 +++++++++++++ .../minestom/chunk/GeneratedChunkCache.java | 45 ++++++++++ .../minestom/chunk/MinestomProtoChunk.java | 49 ----------- .../world/MinestomChunkGeneratorWrapper.java | 20 ++--- .../minestom/world/MinestomProtoWorld.java | 84 +++++++++++++++++++ .../minestom/world/TerraMinestomWorld.java | 15 ++-- 8 files changed, 211 insertions(+), 71 deletions(-) create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/CachedChunk.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/GeneratedChunkCache.java delete mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/MinestomProtoChunk.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomProtoWorld.java diff --git a/platforms/minestom/build.gradle.kts b/platforms/minestom/build.gradle.kts index ebc442209..b1fb58dfd 100644 --- a/platforms/minestom/build.gradle.kts +++ b/platforms/minestom/build.gradle.kts @@ -6,10 +6,7 @@ val javaMainClass = "com.dfsek.terra.minestom.TerraMinestomExample" dependencies { shadedApi(project(":common:implementation:base")) - - //shadedApi("commons-io", "commons-io", Versions.Libraries.Internal.apacheIO) - //shadedApi("com.github.Querz", "NBT", Versions.CLI.nbt) - + shadedApi("com.github.ben-manes.caffeine", "caffeine", Versions.Libraries.caffeine) shadedImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava) implementation("net.minestom", "minestom-snapshots", Versions.Minestom.minestom) diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockState.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockState.java index 9805be509..55da9dfa7 100644 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockState.java +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockState.java @@ -7,7 +7,6 @@ import com.dfsek.terra.api.block.state.properties.Property; import net.minestom.server.instance.block.Block; import java.util.HashMap; -import java.util.Objects; import java.util.stream.Collectors; @@ -15,7 +14,11 @@ public class MinestomBlockState implements BlockState { private final Block block; public MinestomBlockState(Block block) { - this.block = block; + if (block == null) { + this.block = Block.AIR; + } else { + this.block = block; + } } public MinestomBlockState(String data) { diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/CachedChunk.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/CachedChunk.java new file mode 100644 index 000000000..5a57f9830 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/CachedChunk.java @@ -0,0 +1,57 @@ +package com.dfsek.terra.minestom.chunk; + + +import com.dfsek.terra.api.block.state.BlockState; + +import com.dfsek.terra.api.world.chunk.generation.ProtoChunk; +import com.dfsek.terra.minestom.block.MinestomBlockState; +import net.minestom.server.instance.block.Block; +import net.minestom.server.instance.generator.UnitModifier; +import org.jetbrains.annotations.NotNull; + + +public class CachedChunk implements ProtoChunk { + private final int minHeight; + private final int maxHeight; + private final Block[][][] blocks; + + public CachedChunk(int minHeight, int maxHeight) { + this.minHeight = minHeight; + this.maxHeight = maxHeight; + this.blocks = new Block[16][maxHeight - minHeight + 1][16]; + + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < maxHeight - minHeight + 1; y++) { + blocks[x][y][z] = Block.AIR; + } + } + } + } + + public void writeRelative(UnitModifier modifier) { + modifier.setAllRelative((x, y, z) -> blocks[x][y][z]); + } + + @Override + public void setBlock(int x, int y, int z, @NotNull BlockState blockState) { + Block block = (Block) blockState.getHandle(); + if(block == null) return; + blocks[x][y - minHeight][z] = block; + } + + @Override + public @NotNull BlockState getBlock(int x, int y, int z) { + return new MinestomBlockState(blocks[x][y - minHeight][z]); + } + + @Override + public Object getHandle() { + return blocks; + } + + @Override + public int getMaxHeight() { + return maxHeight; + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/GeneratedChunkCache.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/GeneratedChunkCache.java new file mode 100644 index 000000000..db711d086 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/GeneratedChunkCache.java @@ -0,0 +1,45 @@ +package com.dfsek.terra.minestom.chunk; + +import com.dfsek.terra.api.util.generic.pair.Pair; + +import com.dfsek.terra.api.world.ServerWorld; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; +import net.minestom.server.world.DimensionType; + + +public class GeneratedChunkCache { + private final LoadingCache, CachedChunk> cache; + private final DimensionType dimensionType; + private final ChunkGenerator generator; + private final ServerWorld world; + private final BiomeProvider biomeProvider; + + public GeneratedChunkCache(DimensionType dimensionType, ChunkGenerator generator, ServerWorld world) { + this.dimensionType = dimensionType; + this.generator = generator; + this.world = world; + this.biomeProvider = world.getBiomeProvider(); + this.cache = Caffeine.newBuilder() + .maximumSize(32) + .build((Pair key) -> generateChunk(key.getLeft(), key.getRight())); + } + + private CachedChunk generateChunk(int x, int z) { + CachedChunk chunk = new CachedChunk(dimensionType.minY(), dimensionType.maxY()); + generator.generateChunkData( + chunk, + world, + biomeProvider, + x, z + ); + return chunk; + } + + public CachedChunk at(int x, int y) { + return cache.get(Pair.of(x, y)); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/MinestomProtoChunk.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/MinestomProtoChunk.java deleted file mode 100644 index 3e658c88a..000000000 --- a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/MinestomProtoChunk.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.dfsek.terra.minestom.chunk; - -import com.dfsek.terra.api.block.state.BlockState; -import com.dfsek.terra.api.world.chunk.generation.ProtoChunk; - -import net.minestom.server.instance.block.Block; -import net.minestom.server.instance.generator.UnitModifier; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -public class MinestomProtoChunk implements ProtoChunk { - private static final Logger log = LoggerFactory.getLogger(MinestomProtoChunk.class); - private final int minHeight; - private final int maxHeight; - private final UnitModifier modifier; - - public MinestomProtoChunk(int maxHeight, int minHeight, @NotNull UnitModifier modifier) { - this.minHeight = minHeight; - this.maxHeight = maxHeight; - this.modifier = modifier; - } - - @Override - public int getMaxHeight() { - return maxHeight; - } - - @Override - public void setBlock(int x, int y, int z, @NotNull BlockState blockState) { - try { - modifier.setRelative(x, y - minHeight, z, (Block) blockState.getHandle()); - } catch(Exception e) { - log.error("Failed setting Block at {} {} {} to {} (min={}, max={})", x, y, z, blockState.getHandle(), minHeight, maxHeight); - } - } - - @Override - public @NotNull BlockState getBlock(int x, int y, int z) { - System.out.println("Block access at " + x + ", " + y + ", " + z + " is not supported."); - return null; - } - - @Override - public Object getHandle() { - return modifier; - } -} 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 6c106d16d..dcb6c6f0b 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 @@ -2,7 +2,8 @@ package com.dfsek.terra.minestom.world; import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; -import com.dfsek.terra.minestom.chunk.MinestomProtoChunk; +import com.dfsek.terra.minestom.chunk.CachedChunk; +import com.dfsek.terra.minestom.chunk.GeneratedChunkCache; import net.minestom.server.coordinate.Point; import net.minestom.server.instance.generator.GenerationUnit; @@ -11,12 +12,12 @@ import org.jetbrains.annotations.NotNull; public class MinestomChunkGeneratorWrapper implements Generator { + private final GeneratedChunkCache cache; private final ChunkGenerator generator; - private final TerraMinestomWorld world; public MinestomChunkGeneratorWrapper(ChunkGenerator generator, TerraMinestomWorld world) { this.generator = generator; - this.world = world; + this.cache = new GeneratedChunkCache(world.getDimensionType(), generator, world); } public ChunkGenerator getGenerator() { @@ -24,13 +25,10 @@ public class MinestomChunkGeneratorWrapper implements Generator { } @Override - public void generate(@NotNull GenerationUnit generationUnit) { - MinestomProtoChunk protoChunk = new MinestomProtoChunk( - world.getMaxHeight(), - world.getMinHeight(), - generationUnit.modifier() - ); - Point start = generationUnit.absoluteStart(); - generator.generateChunkData(protoChunk, world, world.getBiomeProvider(), start.chunkX(), start.chunkZ()); + public void generate(@NotNull GenerationUnit unit) { + Point start = unit.absoluteStart(); + CachedChunk chunk = cache.at(start.chunkX(), start.chunkZ()); + + chunk.writeRelative(unit.modifier()); } } diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomProtoWorld.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomProtoWorld.java new file mode 100644 index 000000000..4299ee216 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomProtoWorld.java @@ -0,0 +1,84 @@ +package com.dfsek.terra.minestom.world; + +import com.dfsek.terra.api.block.entity.BlockEntity; +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.entity.Entity; +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.world.ServerWorld; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; +import com.dfsek.terra.api.world.chunk.generation.ProtoWorld; + + +public class MinestomProtoWorld implements ProtoWorld { + @Override + public int centerChunkX() { + return 0; + } + + @Override + public int centerChunkZ() { + return 0; + } + + @Override + public ServerWorld getWorld() { + return null; + } + + @Override + public void setBlockState(int x, int y, int z, BlockState data, boolean physics) { + + } + + @Override + public Entity spawnEntity(double x, double y, double z, EntityType entityType) { + return null; + } + + @Override + public BlockState getBlockState(int x, int y, int z) { + return null; + } + + @Override + public BlockEntity getBlockEntity(int x, int y, int z) { + return null; + } + + @Override + public ChunkGenerator getGenerator() { + return null; + } + + @Override + public BiomeProvider getBiomeProvider() { + return null; + } + + @Override + public ConfigPack getPack() { + return null; + } + + @Override + public long getSeed() { + return 0; + } + + @Override + public int getMaxHeight() { + return 0; + } + + @Override + public int getMinHeight() { + return 0; + } + + @Override + public Object getHandle() { + return null; + } +} 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 694e80b87..aac669a32 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 @@ -20,22 +20,23 @@ import net.minestom.server.world.DimensionType; public final class TerraMinestomWorld implements ServerWorld, WorldProperties { private final Instance instance; - private final MinestomChunkGeneratorWrapper wrapper; private final ConfigPack pack; private final long seed; private final DimensionType dimensionType; + private final MinestomChunkGeneratorWrapper wrapper; public TerraMinestomWorld(Instance instance, ConfigPack pack, long seed) { this.instance = instance; - this.wrapper = new MinestomChunkGeneratorWrapper( - pack.getGeneratorProvider().newInstance(pack), - this - ); this.pack = pack; this.seed = seed; this.dimensionType = MinecraftServer.getDimensionTypeRegistry().get(instance.getDimensionType()); + this.wrapper = new MinestomChunkGeneratorWrapper( + pack.getGeneratorProvider().newInstance(pack), + this + ); + instance.setGenerator(this.wrapper); } @@ -98,4 +99,8 @@ public final class TerraMinestomWorld implements ServerWorld, WorldProperties { public Object getHandle() { return instance; } + + public DimensionType getDimensionType() { + return dimensionType; + } }