diff --git a/platforms/sponge/build.gradle.kts b/platforms/sponge/build.gradle.kts index feabcda9d..8173e9dff 100644 --- a/platforms/sponge/build.gradle.kts +++ b/platforms/sponge/build.gradle.kts @@ -3,6 +3,7 @@ import com.dfsek.terra.configureCommon plugins { java id("org.spongepowered.plugin").version("0.9.0") + id("org.spongepowered.gradle.vanilla").version("0.2") } configureCommon() @@ -25,4 +26,12 @@ sponge { plugin { id = "terra" } +} + +minecraft { + version("1.16.5") + runs { + server() + client() + } } \ No newline at end of file diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/SpongeAdapter.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/SpongeAdapter.java deleted file mode 100644 index 781b5f91e..000000000 --- a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/SpongeAdapter.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.dfsek.terra.sponge; - -import com.dfsek.terra.api.platform.block.BlockData; -import com.dfsek.terra.sponge.world.block.data.SpongeBlockData; -import org.spongepowered.api.block.BlockState; - -public final class SpongeAdapter { - public static BlockData adapt(BlockState state) { - return new SpongeBlockData(state); - } - - public static BlockState adapt(BlockData data) { - return ((SpongeBlockData) data).getHandle(); - } -} diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/TerraSpongePlugin.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/TerraSpongePlugin.java index d367bfc64..4b4c5138d 100644 --- a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/TerraSpongePlugin.java +++ b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/TerraSpongePlugin.java @@ -14,10 +14,12 @@ import com.dfsek.terra.api.event.events.config.ConfigPackPreLoadEvent; import com.dfsek.terra.api.platform.block.BlockData; import com.dfsek.terra.api.platform.handle.ItemHandle; import com.dfsek.terra.api.platform.handle.WorldHandle; +import com.dfsek.terra.api.platform.world.Biome; import com.dfsek.terra.api.platform.world.Tree; import com.dfsek.terra.api.platform.world.World; import com.dfsek.terra.api.registry.CheckedRegistry; import com.dfsek.terra.api.registry.LockedRegistry; +import com.dfsek.terra.api.util.generic.pair.Pair; import com.dfsek.terra.api.util.logging.DebugLogger; import com.dfsek.terra.config.GenericLoaders; import com.dfsek.terra.config.PluginConfig; @@ -30,19 +32,28 @@ import com.dfsek.terra.registry.master.AddonRegistry; import com.dfsek.terra.registry.master.ConfigRegistry; import com.dfsek.terra.sponge.handle.SpongeItemHandle; import com.dfsek.terra.sponge.handle.SpongeWorldHandle; +import com.dfsek.terra.sponge.intern.util.SpongeUtil; import com.dfsek.terra.sponge.world.SpongeTree; import com.dfsek.terra.world.TerraWorld; import com.google.inject.Inject; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.dimension.DimensionType; import org.spongepowered.api.Server; import org.spongepowered.api.Sponge; import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.lifecycle.StartedEngineEvent; +import org.spongepowered.api.event.lifecycle.StartingEngineEvent; +import org.spongepowered.api.registry.Registry; +import org.spongepowered.api.world.biome.Biomes; +import org.spongepowered.api.world.server.ServerWorld; import org.spongepowered.plugin.PluginContainer; import org.spongepowered.plugin.jvm.Plugin; import java.io.File; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; @Plugin("terra") public class TerraSpongePlugin implements TerraPlugin { @@ -53,7 +64,9 @@ public class TerraSpongePlugin implements TerraPlugin { private final AddonRegistry addonRegistry; private final LockedRegistry addonLockedRegistry; + private static TerraSpongePlugin INSTANCE; + private final Map> worldMap = new HashMap<>(); private final PluginContainer plugin; @@ -61,18 +74,22 @@ public class TerraSpongePlugin implements TerraPlugin { private final WorldHandle worldHandle = new SpongeWorldHandle(); - Profiler profiler = new ProfilerImpl(); + private final Profiler profiler = new ProfilerImpl(); @Inject public TerraSpongePlugin(PluginContainer plugin) { this.plugin = plugin; this.addonRegistry = new AddonRegistry(new SpongeAddon(this), this); this.addonLockedRegistry = new LockedRegistry<>(addonRegistry); + INSTANCE = this; } + public static TerraSpongePlugin getInstance() { + return INSTANCE; + } @Listener - public void initialize(org.spongepowered.api.event.lifecycle.StartingEngineEvent event) { + public void initialize(StartingEngineEvent event) { plugin.logger().info("Loading Terra..."); addonRegistry.loadAll(); configRegistry.loadAll(this); @@ -81,7 +98,8 @@ public class TerraSpongePlugin implements TerraPlugin { @Override public void register(TypeRegistry registry) { loaders.register(registry); - registry.registerLoader(BlockData.class, (t, o, l) -> worldHandle.createBlockData((String) o)); + registry.registerLoader(BlockData.class, (t, o, l) -> worldHandle.createBlockData((String) o)) + .registerLoader(Biome.class, (t, o, l) -> SpongeUtil.BIOME_FIXER.translate((String) o)); } @Override @@ -91,7 +109,13 @@ public class TerraSpongePlugin implements TerraPlugin { @Override public TerraWorld getWorld(World world) { - return null; + return getWorld(((LevelReader) world).dimensionType()); + } + + public TerraWorld getWorld(DimensionType type) { + TerraWorld world = worldMap.get(type).getRight(); + if(world == null) throw new IllegalArgumentException("No world exists with dimension type " + type); + return world; } @Override diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/SpongeChunkGeneratorWrapper.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/SpongeChunkGeneratorWrapper.java new file mode 100644 index 000000000..144ad4c27 --- /dev/null +++ b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/SpongeChunkGeneratorWrapper.java @@ -0,0 +1,200 @@ +package com.dfsek.terra.sponge.intern; + +import com.dfsek.terra.api.platform.world.World; +import com.dfsek.terra.api.platform.world.generator.ChunkData; +import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; +import com.dfsek.terra.api.util.FastRandom; +import com.dfsek.terra.api.world.biome.UserDefinedBiome; +import com.dfsek.terra.api.world.generation.TerraChunkGenerator; +import com.dfsek.terra.api.world.locate.AsyncStructureFinder; +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.sponge.TerraSpongePlugin; +import com.dfsek.terra.sponge.intern.util.SpongeAdapter; +import com.dfsek.terra.world.TerraWorld; +import com.dfsek.terra.world.generation.generators.DefaultChunkGenerator3D; +import com.dfsek.terra.world.generation.math.samplers.Sampler; +import com.dfsek.terra.world.population.items.TerraStructure; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.jafama.FastMath; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.NaturalSpawner; +import net.minecraft.world.level.NoiseColumn; +import net.minecraft.world.level.StructureFeatureManager; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.StructureSettings; +import net.minecraft.world.level.levelgen.WorldgenRandom; +import net.minecraft.world.level.levelgen.feature.StructureFeature; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.api.world.biome.provider.BiomeProvider; +import org.spongepowered.api.world.generation.config.structure.StructureGenerationConfig; +import org.spongepowered.api.world.server.ServerWorld; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; + +public class SpongeChunkGeneratorWrapper extends ChunkGenerator implements GeneratorWrapper { + private final long seed; + private final DefaultChunkGenerator3D delegate; + private final TerraBiomeSource biomeSource; + public static final Codec PACK_CODEC = (RecordCodecBuilder.create(config -> config.group( + Codec.STRING.fieldOf("pack").forGetter(pack -> pack.getTemplate().getID()) + ).apply(config, config.stable(TerraSpongePlugin.getInstance().getConfigRegistry()::get)))); + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + TerraBiomeSource.CODEC.fieldOf("biome_source").forGetter(generator -> generator.biomeSource), + Codec.LONG.fieldOf("seed").stable().forGetter(generator -> generator.seed), + PACK_CODEC.fieldOf("pack").stable().forGetter(generator -> generator.pack)) + .apply(instance, instance.stable(SpongeChunkGeneratorWrapper::new))); + private final ConfigPack pack; + + public ConfigPack getPack() { + return pack; + } + + private DimensionType dimensionType; + + public SpongeChunkGeneratorWrapper(TerraBiomeSource biomeSource, long seed, ConfigPack configPack) { + super(biomeSource, new StructureSettings(configPack.getTemplate().vanillaStructures())); + this.pack = configPack; + + this.delegate = new DefaultChunkGenerator3D(pack, TerraSpongePlugin.getInstance()); + delegate.getMain().logger().info("Loading world with config pack " + pack.getTemplate().getID()); + this.biomeSource = biomeSource; + + this.seed = seed; + } + + @Override + protected @NotNull Codec codec() { + return CODEC; + } + + @Override + public @NotNull ChunkGenerator withSeed(long seed) { + return new SpongeChunkGeneratorWrapper((TerraBiomeSource) this.biomeSource.withSeed(seed), seed, pack); + } + + @Override + public void buildSurfaceAndBedrock(WorldGenRegion worldGenRegion, ChunkAccess chunkAccess) { + + } + + @Nullable + @Override + public BlockPos findNearestMapFeature(ServerLevel world, StructureFeature feature, BlockPos center, int param3, boolean param4) { + if(!pack.getTemplate().disableStructures()) { + String name = Objects.requireNonNull(Registry.STRUCTURE_FEATURE.getKey(feature)).toString(); + TerraWorld terraWorld = TerraSpongePlugin.getInstance().getWorld((World) world); + TerraStructure located = pack.getStructure(pack.getTemplate().getLocatable().get(name)); + if(located != null) { + CompletableFuture result = new CompletableFuture<>(); + AsyncStructureFinder finder = new AsyncStructureFinder(terraWorld.getBiomeProvider(), located, SpongeAdapter.adapt(center).toLocation((World) world), 0, 500, location -> { + result.complete(SpongeAdapter.adapt(location)); + }, TerraSpongePlugin.getInstance()); + finder.run(); // Do this synchronously. + try { + return result.get(); + } catch(InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + } + return super.findNearestMapFeature(world, feature, center, param3, param4); + } + + + @Override + public boolean hasStronghold(@NotNull ChunkPos p_235952_1_) { + if(pack.getTemplate().vanillaStructures()) return super.hasStronghold(p_235952_1_); + return false; + } + + @Override + public void createStructures(RegistryAccess param0, StructureFeatureManager param1, ChunkAccess param2, StructureManager param3, long param4) { + if(pack.getTemplate().vanillaStructures()) super.createStructures(param0, param1, param2, param3, param4); + } + + @Override + public void applyCarvers(long param0, BiomeManager param1, ChunkAccess param2, GenerationStep.Carving param3) { + if(pack.getTemplate().vanillaCaves()) super.applyCarvers(param0, param1, param2, param3); + } + + @Override + public void fillFromNoise(@NotNull LevelAccessor world, @NotNull StructureFeatureManager p_230352_2_, @NotNull ChunkAccess chunk) { + delegate.generateChunkData((World) world, new FastRandom(), chunk.getPos().x, chunk.getPos().z, (ChunkData) chunk); + } + + @Override + public int getBaseHeight(int x, int z, Heightmap.@NotNull Types p_222529_3_) { + TerraWorld world = TerraSpongePlugin.getInstance().getWorld(dimensionType); + Sampler sampler = world.getConfig().getSamplerCache().getChunk(FastMath.floorDiv(x, 16), FastMath.floorDiv(z, 16)); + int cx = FastMath.floorMod(x, 16); + int cz = FastMath.floorMod(z, 16); + + int height = world.getWorld().getMaxHeight(); + + while(height >= 0 && sampler.sample(cx, height - 1, cz) < 0) height--; + + return height; + } + + @Override + public @NotNull BlockGetter getBaseColumn(int x, int z) { + TerraWorld world = TerraSpongePlugin.getInstance().getWorld(dimensionType); + int height = getBaseHeight(x, z, Heightmap.Types.WORLD_SURFACE); + BlockState[] array = new BlockState[256]; + for(int y = 255; y >= 0; y--) { + if(y > height) { + if(y > ((UserDefinedBiome) world.getBiomeProvider().getBiome(x, z)).getConfig().getSeaLevel()) { + array[y] = Blocks.AIR.defaultBlockState(); + } else { + array[y] = Blocks.WATER.defaultBlockState(); + } + } else { + array[y] = Blocks.STONE.defaultBlockState(); + } + } + + return new NoiseColumn(array); + } + + @Override + public void spawnOriginalMobs(WorldGenRegion region) { + if(pack.getTemplate().vanillaMobs()) { + int cx = region.getCenterX(); + int cy = region.getCenterZ(); + Biome biome = region.getBiome((new ChunkPos(cx, cy)).getWorldPosition()); + WorldgenRandom chunkRandom = new WorldgenRandom(); + chunkRandom.setDecorationSeed(region.getSeed(), cx << 4, cy << 4); + NaturalSpawner.spawnMobsForChunkGeneration(region, biome, cx, cy, chunkRandom); + } + } + + + @Override + public TerraChunkGenerator getHandle() { + return delegate; + } + + public void setDimensionType(DimensionType dimensionType) { + this.dimensionType = dimensionType; + } +} \ No newline at end of file diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/TerraBiomeSource.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/TerraBiomeSource.java new file mode 100644 index 000000000..26c96814f --- /dev/null +++ b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/TerraBiomeSource.java @@ -0,0 +1,63 @@ +package com.dfsek.terra.sponge.intern; + +import com.dfsek.terra.api.world.biome.TerraBiome; +import com.dfsek.terra.api.world.biome.UserDefinedBiome; +import com.dfsek.terra.api.world.biome.provider.BiomeProvider; +import com.dfsek.terra.config.pack.ConfigPack; +import com.dfsek.terra.config.pack.WorldConfig; +import com.dfsek.terra.sponge.TerraSpongePlugin; +import com.dfsek.terra.sponge.intern.util.SpongeUtil; +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.core.Registry; +import net.minecraft.resources.RegistryLookupCodec; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import org.jetbrains.annotations.NotNull; + + +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +public class TerraBiomeSource extends BiomeSource { + public static final Codec PACK_CODEC = (RecordCodecBuilder.create(config -> config.group( + Codec.STRING.fieldOf("pack").forGetter(pack -> pack.getTemplate().getID()) + ).apply(config, config.stable(TerraSpongePlugin.getInstance().getConfigRegistry()::get)))); + public static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + RegistryLookupCodec.create(Registry.BIOME_REGISTRY).forGetter(source -> source.biomeRegistry), + Codec.LONG.fieldOf("seed").stable().forGetter(source -> source.seed), + PACK_CODEC.fieldOf("pack").stable().forGetter(source -> source.pack)) + .apply(instance, instance.stable(TerraBiomeSource::new))); + + private final Registry biomeRegistry; + private final long seed; + private final BiomeProvider grid; + private final ConfigPack pack; + + public TerraBiomeSource(Registry biomes, long seed, ConfigPack pack) { + super(biomes.stream().collect(Collectors.toList())); + this.biomeRegistry = biomes; + this.seed = seed; + this.grid = pack.getBiomeProviderBuilder().build(seed); + this.pack = pack; + } + + @Override + protected @NotNull Codec codec() { + return CODEC; + } + + @Override + public BiomeSource withSeed(long seed) { + return new TerraBiomeSource(this.biomeRegistry, seed, pack); + } + + @Override + public @NotNull Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) { + UserDefinedBiome biome = (UserDefinedBiome) grid.getBiome(biomeX << 2, biomeZ << 2); + return Objects.requireNonNull(biomeRegistry.get(new ResourceLocation("terra", SpongeUtil.createBiomeID(pack, biome.getID())))); + } +} diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/package-info.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/package-info.java new file mode 100644 index 000000000..d10ad5282 --- /dev/null +++ b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/package-info.java @@ -0,0 +1,6 @@ +/** + * Stuff that accesses the internal server. + *

+ * Should be replaced with API stuff when API is complete. + */ +package com.dfsek.terra.sponge.intern; \ No newline at end of file diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/util/SpongeAdapter.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/util/SpongeAdapter.java new file mode 100644 index 000000000..a9c892933 --- /dev/null +++ b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/util/SpongeAdapter.java @@ -0,0 +1,14 @@ +package com.dfsek.terra.sponge.intern.util; + +import com.dfsek.terra.api.math.vector.Vector3; +import net.minecraft.core.BlockPos; + +public final class SpongeAdapter { + public static Vector3 adapt(BlockPos pos) { + return new Vector3(pos.getX(), pos.getY(), pos.getZ()); + } + + public static BlockPos adapt(Vector3 vec) { + return new BlockPos(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()); + } +} diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/util/SpongeUtil.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/util/SpongeUtil.java new file mode 100644 index 000000000..b0dbda616 --- /dev/null +++ b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/intern/util/SpongeUtil.java @@ -0,0 +1,21 @@ +package com.dfsek.terra.sponge.intern.util; + +import com.dfsek.terra.api.transform.Transformer; +import com.dfsek.terra.api.transform.Validator; +import com.dfsek.terra.config.pack.ConfigPack; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.biome.Biome; + +import java.util.Locale; + +public final class SpongeUtil { + public static String createBiomeID(ConfigPack pack, String biomeID) { + return pack.getTemplate().getID().toLowerCase() + "/" + biomeID.toLowerCase(Locale.ROOT); + } + + public static final Transformer BIOME_FIXER = new Transformer.Builder() + .addTransform(id -> BuiltinRegistries.BIOME.get(ResourceLocation.tryParse(id)), Validator.notNull()) + .addTransform(id -> BuiltinRegistries.BIOME.get(ResourceLocation.tryParse("minecraft:" + id.toLowerCase())), Validator.notNull()).build(); + +} diff --git a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/world/generator/SpongeChunkGeneratorWrapper.java b/platforms/sponge/src/main/java/com/dfsek/terra/sponge/world/generator/SpongeChunkGeneratorWrapper.java deleted file mode 100644 index e620259bb..000000000 --- a/platforms/sponge/src/main/java/com/dfsek/terra/sponge/world/generator/SpongeChunkGeneratorWrapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.dfsek.terra.sponge.world.generator; - -import com.dfsek.terra.api.platform.world.generator.GeneratorWrapper; -import com.dfsek.terra.api.world.generation.TerraChunkGenerator; -import org.spongepowered.api.world.biome.provider.BiomeProvider; -import org.spongepowered.api.world.generation.ChunkGenerator; -import org.spongepowered.api.world.generation.config.structure.StructureGenerationConfig; - -public class SpongeChunkGeneratorWrapper implements ChunkGenerator, GeneratorWrapper { - - @Override - public TerraChunkGenerator getHandle() { - return null; - } - - @Override - public BiomeProvider biomeProvider() { - return null; - } - - @Override - public StructureGenerationConfig structureConfig() { - return null; - } -}