From 983cb1f012a80fcdf5a18e805a0065c0c007f0c1 Mon Sep 17 00:00:00 2001 From: Christian Bergschneider Date: Sat, 28 Dec 2024 12:22:07 +0100 Subject: [PATCH] feat: base terrain generation with minestom --- buildSrc/src/main/kotlin/Versions.kt | 4 + platforms/minestom/build.gradle.kts | 34 ++++++ .../terra/minestom/MinestomPlatform.java | 73 +++++++++++++ .../terra/minestom/TerraMinestomExample.java | 38 +++++++ .../minestom/block/MinestomBlockState.java | 95 ++++++++++++++++ .../minestom/block/MinestomBlockType.java | 35 ++++++ .../minestom/chunk/MinestomProtoChunk.java | 49 +++++++++ .../minestom/chunk/TerraMinestomChunk.java | 50 +++++++++ .../minestom/item/MinestomItemHandle.java | 25 +++++ .../world/MinestomChunkGeneratorWrapper.java | 36 +++++++ .../minestom/world/MinestomWorldHandle.java | 29 +++++ .../minestom/world/TerraMinestomWorld.java | 101 ++++++++++++++++++ .../world/TerraMinestomWorldBuilder.java | 58 ++++++++++ 13 files changed, 627 insertions(+) create mode 100644 platforms/minestom/build.gradle.kts create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/MinestomPlatform.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockState.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockType.java create 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/chunk/TerraMinestomChunk.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/item/MinestomItemHandle.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomWorldHandle.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java create mode 100644 platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorldBuilder.java diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 1e48e716a..c78008cb3 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -75,4 +75,8 @@ object Versions { const val nbt = "6.1" const val logback = "1.5.8" } + + object Minestom { + const val minestom = "187931e50b" + } } \ No newline at end of file diff --git a/platforms/minestom/build.gradle.kts b/platforms/minestom/build.gradle.kts new file mode 100644 index 000000000..ebc442209 --- /dev/null +++ b/platforms/minestom/build.gradle.kts @@ -0,0 +1,34 @@ +plugins { + application +} + +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) + + shadedImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava) + + implementation("net.minestom", "minestom-snapshots", Versions.Minestom.minestom) + implementation("org.slf4j", "slf4j-simple", Versions.Libraries.slf4j) +} + +tasks.withType { + entryCompression = ZipEntryCompression.STORED + manifest { + attributes( + "Main-Class" to javaMainClass, + ) + } +} + +application { + mainClass.set(javaMainClass) +} + +tasks.getByName("run").setProperty("workingDir", file("./run")) + +addonDir(project.file("./run/terra/addons"), tasks.named("run").get()) diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/MinestomPlatform.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/MinestomPlatform.java new file mode 100644 index 000000000..2cac94575 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/MinestomPlatform.java @@ -0,0 +1,73 @@ +package com.dfsek.terra.minestom; + +import com.dfsek.tectonic.api.TypeRegistry; + +import com.dfsek.tectonic.api.loader.type.TypeLoader; + +import com.dfsek.terra.AbstractPlatform; +import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent; +import com.dfsek.terra.api.handle.ItemHandle; +import com.dfsek.terra.api.handle.WorldHandle; + +import com.dfsek.terra.api.world.biome.PlatformBiome; + +import com.dfsek.terra.minestom.item.MinestomItemHandle; + +import com.dfsek.terra.minestom.world.MinestomWorldHandle; + +import org.jetbrains.annotations.NotNull; + +import java.io.File; + + +public final class MinestomPlatform extends AbstractPlatform { + private static MinestomPlatform INSTANCE = null; + private final MinestomWorldHandle worldHandle = new MinestomWorldHandle(); + private final MinestomItemHandle itemHandle = new MinestomItemHandle(); + + private MinestomPlatform() { + load(); + getEventManager().callEvent(new PlatformInitializationEvent()); + } + + @Override + public void register(TypeRegistry registry) { + super.register(registry); + registry.registerLoader(PlatformBiome.class, (TypeLoader) (annotatedType, o, configLoader, depthTracker) -> () -> o); + } + + @Override + public boolean reload() { + return false; + } + + @Override + public @NotNull WorldHandle getWorldHandle() { + return worldHandle; + } + + @Override + public @NotNull ItemHandle getItemHandle() { + return itemHandle; + } + + @Override + public @NotNull String platformName() { + return "Minestom"; + } + + @Override + public @NotNull File getDataFolder() { + File file = new File("./terra/"); + if (!file.exists()) file.mkdirs(); + return file; + } + + + public static MinestomPlatform getInstance() { + if (INSTANCE == null) { + INSTANCE = new MinestomPlatform(); + } + return INSTANCE; + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java new file mode 100644 index 000000000..934f5a220 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/TerraMinestomExample.java @@ -0,0 +1,38 @@ +package com.dfsek.terra.minestom; + +import com.dfsek.terra.minestom.world.TerraMinestomWorldBuilder; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.coordinate.Pos; +import net.minestom.server.entity.GameMode; +import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; +import net.minestom.server.instance.Instance; + + +public class TerraMinestomExample { + public static void main(String[] args) { + MinecraftServer server = MinecraftServer.init(); + + Instance instance = MinecraftServer + .getInstanceManager() + .createInstanceContainer(); + + TerraMinestomWorldBuilder.from(instance) + .packById("OVERWORLD") + .attach(); + + for(int x = -1; x <= 1; x++) { + for(int z = -1; z <= 1; z++) { + instance.loadChunk(x, z); + } + } + + MinecraftServer.getGlobalEventHandler().addListener(AsyncPlayerConfigurationEvent.class, event -> { + event.setSpawningInstance(instance); + event.getPlayer().setGameMode(GameMode.CREATIVE); + event.getPlayer().setRespawnPoint(new Pos(0.0, 100.0, 0.0)); + }); + + server.start("localhost", 25565); + } +} 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 new file mode 100644 index 000000000..9805be509 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockState.java @@ -0,0 +1,95 @@ +package com.dfsek.terra.minestom.block; + +import com.dfsek.terra.api.block.BlockType; +import com.dfsek.terra.api.block.state.BlockState; +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; + + +public class MinestomBlockState implements BlockState { + private final Block block; + + public MinestomBlockState(Block block) { + this.block = block; + } + + public MinestomBlockState(String data) { + if (!data.contains("[")) { + block = Block.fromNamespaceId(data); + return; + } + + String[] split = data.split("\\["); + String namespaceId = split[0]; + String properties = split[1].substring(0, split[1].length() - 1); + Block block = Block.fromNamespaceId(namespaceId); + HashMap propertiesMap = new HashMap<>(); + + for (String property : properties.split(",")) { + String[] kv = property.split("="); + propertiesMap.put(kv[0].strip(), kv[1].strip()); + } + + assert block != null; + this.block = block.withProperties(propertiesMap); + } + + @Override + public boolean matches(BlockState other) { + return ((MinestomBlockState) other).block == block; + } + + @Override + public > boolean has(Property property) { + return false; + } + + @Override + public > T get(Property property) { + return null; + } + + @Override + public > BlockState set(Property property, T value) { + return null; + } + + @Override + public BlockType getBlockType() { + return new MinestomBlockType(block); + } + + @Override + public String getAsString(boolean properties) { + String name = block.namespace().asString(); + if (!properties) { + return name; + } + + name += "[" + block + .properties() + .entrySet() + .stream() + .map(entry -> + entry.getKey() + "=" + entry.getValue() + ) + .collect(Collectors.joining(",")) + "]"; + + return name; + } + + @Override + public boolean isAir() { + return block.isAir(); + } + + @Override + public Object getHandle() { + return block; + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockType.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockType.java new file mode 100644 index 000000000..fd447649f --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/block/MinestomBlockType.java @@ -0,0 +1,35 @@ +package com.dfsek.terra.minestom.block; + +import com.dfsek.terra.api.block.BlockType; +import com.dfsek.terra.api.block.state.BlockState; + +import net.minestom.server.instance.block.Block; + + +public class MinestomBlockType implements BlockType { + private final Block block; + + public MinestomBlockType(Block block) { + this.block = block; + } + + @Override + public BlockState getDefaultState() { + return new MinestomBlockState(block); + } + + @Override + public boolean isSolid() { + return block.isSolid(); + } + + @Override + public boolean isWater() { + return block.isLiquid(); + } + + @Override + public Object getHandle() { + return block; + } +} 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 new file mode 100644 index 000000000..3e658c88a --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/MinestomProtoChunk.java @@ -0,0 +1,49 @@ +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/chunk/TerraMinestomChunk.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/TerraMinestomChunk.java new file mode 100644 index 000000000..12f38ced3 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/chunk/TerraMinestomChunk.java @@ -0,0 +1,50 @@ +package com.dfsek.terra.minestom.chunk; + +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.world.ServerWorld; +import com.dfsek.terra.api.world.chunk.Chunk; + +import com.dfsek.terra.minestom.block.MinestomBlockState; +import net.minestom.server.instance.block.Block; +import org.jetbrains.annotations.NotNull; + + +public class TerraMinestomChunk implements Chunk { + private net.minestom.server.instance.Chunk delegate; + private final ServerWorld world; + + public TerraMinestomChunk(net.minestom.server.instance.Chunk delegate, ServerWorld world) { + this.delegate = delegate; + this.world = world; + } + + @Override + public void setBlock(int x, int y, int z, BlockState data, boolean physics) { + delegate.setBlock(x, y, z, (Block) data.getHandle()); + } + + @Override + public @NotNull BlockState getBlock(int x, int y, int z) { + return new MinestomBlockState(delegate.getBlock(x, y, z)); + } + + @Override + public int getX() { + return delegate.getChunkX(); + } + + @Override + public int getZ() { + return delegate.getChunkZ(); + } + + @Override + public ServerWorld getWorld() { + return world; + } + + @Override + public Object getHandle() { + return delegate; + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/item/MinestomItemHandle.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/item/MinestomItemHandle.java new file mode 100644 index 000000000..65ddb1605 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/item/MinestomItemHandle.java @@ -0,0 +1,25 @@ +package com.dfsek.terra.minestom.item; + +import com.dfsek.terra.api.handle.ItemHandle; +import com.dfsek.terra.api.inventory.Item; +import com.dfsek.terra.api.inventory.item.Enchantment; + +import java.util.Set; + + +public class MinestomItemHandle implements ItemHandle { + @Override + public Item createItem(String data) { + return null; + } + + @Override + public Enchantment getEnchantment(String id) { + return null; + } + + @Override + public Set getEnchantments() { + return Set.of(); + } +} 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 new file mode 100644 index 000000000..6c106d16d --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomChunkGeneratorWrapper.java @@ -0,0 +1,36 @@ +package com.dfsek.terra.minestom.world; + +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; + +import com.dfsek.terra.minestom.chunk.MinestomProtoChunk; + +import net.minestom.server.coordinate.Point; +import net.minestom.server.instance.generator.GenerationUnit; +import net.minestom.server.instance.generator.Generator; +import org.jetbrains.annotations.NotNull; + + +public class MinestomChunkGeneratorWrapper implements Generator { + private final ChunkGenerator generator; + private final TerraMinestomWorld world; + + public MinestomChunkGeneratorWrapper(ChunkGenerator generator, TerraMinestomWorld world) { + this.generator = generator; + this.world = world; + } + + public ChunkGenerator getGenerator() { + return 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()); + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomWorldHandle.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomWorldHandle.java new file mode 100644 index 000000000..eb5731bb4 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/MinestomWorldHandle.java @@ -0,0 +1,29 @@ +package com.dfsek.terra.minestom.world; + +import com.dfsek.terra.api.block.state.BlockState; +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.handle.WorldHandle; + +import com.dfsek.terra.minestom.block.MinestomBlockState; +import net.minestom.server.instance.block.Block; +import org.jetbrains.annotations.NotNull; + + +public class MinestomWorldHandle implements WorldHandle { + private static final MinestomBlockState AIR = new MinestomBlockState(Block.AIR); + + @Override + public @NotNull BlockState createBlockState(@NotNull String data) { + return new MinestomBlockState(data); + } + + @Override + public @NotNull BlockState air() { + return AIR; + } + + @Override + public @NotNull EntityType getEntity(@NotNull String id) { + 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 new file mode 100644 index 000000000..694e80b87 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorld.java @@ -0,0 +1,101 @@ +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.Chunk; + +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; + +import com.dfsek.terra.api.world.info.WorldProperties; + +import net.minestom.server.MinecraftServer; +import net.minestom.server.instance.Instance; +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; + + 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()); + + instance.setGenerator(this.wrapper); + } + + @Override + public Chunk getChunkAt(int x, int z) { + 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 wrapper.getGenerator(); + } + + @Override + public BiomeProvider getBiomeProvider() { + return pack.getBiomeProvider(); + } + + @Override + public ConfigPack getPack() { + return pack; + } + + @Override + public long getSeed() { + return seed; + } + + @Override + public int getMaxHeight() { + return dimensionType.maxY(); + } + + @Override + public int getMinHeight() { + return dimensionType.minY(); + } + + @Override + public Object getHandle() { + return instance; + } +} diff --git a/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorldBuilder.java b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorldBuilder.java new file mode 100644 index 000000000..b50d9c9c5 --- /dev/null +++ b/platforms/minestom/src/main/java/com/dfsek/terra/minestom/world/TerraMinestomWorldBuilder.java @@ -0,0 +1,58 @@ +package com.dfsek.terra.minestom.world; + +import com.dfsek.terra.api.config.ConfigPack; + +import com.dfsek.terra.api.registry.CheckedRegistry; + +import com.dfsek.terra.minestom.MinestomPlatform; +import net.minestom.server.MinecraftServer; +import net.minestom.server.instance.Instance; + +import java.util.Random; +import java.util.function.Function; + + +public class TerraMinestomWorldBuilder { + private final Instance instance; + private ConfigPack pack; + private long seed = new Random().nextLong(); + + private TerraMinestomWorldBuilder(Instance instance) { this.instance = instance; } + + public static TerraMinestomWorldBuilder from(Instance instance) { + return new TerraMinestomWorldBuilder(instance); + } + + public static TerraMinestomWorldBuilder builder() { + return new TerraMinestomWorldBuilder(MinecraftServer.getInstanceManager().createInstanceContainer()); + } + + public TerraMinestomWorldBuilder pack(ConfigPack pack) { + this.pack = pack; + return this; + } + + public TerraMinestomWorldBuilder packById(String id) { + this.pack = MinestomPlatform + .getInstance() + .getConfigRegistry() + .getByID(id) + .orElseThrow(); + + return this; + } + + public TerraMinestomWorldBuilder findPack(Function, ConfigPack> fn) { + this.pack = fn.apply(MinestomPlatform.getInstance().getConfigRegistry()); + return this; + } + + public TerraMinestomWorldBuilder seed(long seed) { + this.seed = seed; + return this; + } + + public TerraMinestomWorld attach() { + return new TerraMinestomWorld(instance, pack, seed); + } +}