feat: base terrain generation with minestom

This commit is contained in:
Christian Bergschneider 2024-12-28 12:22:07 +01:00
parent 0a952cff4c
commit 983cb1f012
13 changed files with 627 additions and 0 deletions

View File

@ -75,4 +75,8 @@ object Versions {
const val nbt = "6.1"
const val logback = "1.5.8"
}
object Minestom {
const val minestom = "187931e50b"
}
}

View File

@ -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<Jar> {
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())

View File

@ -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<PlatformBiome>) (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;
}
}

View File

@ -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);
}
}

View File

@ -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<String, String> 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 <T extends Comparable<T>> boolean has(Property<T> property) {
return false;
}
@Override
public <T extends Comparable<T>> T get(Property<T> property) {
return null;
}
@Override
public <T extends Comparable<T>> BlockState set(Property<T> 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<Enchantment> getEnchantments() {
return Set.of();
}
}

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<CheckedRegistry<ConfigPack>, 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);
}
}