diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 6d0a1e2e0..09b2244af 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -50,8 +50,9 @@ object Versions { object Bukkit { const val paper = "1.18.2-R0.1-SNAPSHOT" const val paperLib = "1.0.5" - const val minecraft = "1.19" + const val minecraft = "1.19.2" const val reflectionRemapper = "0.1.0-SNAPSHOT" + const val paperDevBundle = "1.19.2-R0.1-SNAPSHOT" } object Sponge { diff --git a/platforms/bukkit/build.gradle.kts b/platforms/bukkit/build.gradle.kts index ee85af57a..f099cb4e3 100644 --- a/platforms/bukkit/build.gradle.kts +++ b/platforms/bukkit/build.gradle.kts @@ -29,11 +29,11 @@ tasks { } runServer { - minecraftVersion("1.19") + minecraftVersion(Versions.Bukkit.minecraft) dependsOn(shadowJar) pluginJars(shadowJar.get().archiveFile) } } -addonDir(project.file("./target/server/paper/plugins/Terra/addons"), tasks.named("runServer").get()) +addonDir(project.file("./run/plugins/Terra/addons"), tasks.named("runServer").get()) diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/BukkitAddon.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/BukkitAddon.java index ac215b83e..ee6249eb5 100644 --- a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/BukkitAddon.java +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/BukkitAddon.java @@ -5,8 +5,10 @@ import ca.solostudios.strata.version.Version; import com.dfsek.terra.api.addon.BaseAddon; import com.dfsek.terra.api.event.events.config.ConfigurationLoadEvent; +import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent; import com.dfsek.terra.api.event.functional.FunctionalEventHandler; import com.dfsek.terra.api.world.biome.Biome; +import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions; import com.dfsek.terra.bukkit.config.VanillaBiomeProperties; @@ -21,6 +23,12 @@ public class BukkitAddon implements BaseAddon { @Override public void initialize() { + terraBukkitPlugin.getEventManager() + .getHandler(FunctionalEventHandler.class) + .register(this, ConfigPackPreLoadEvent.class) + .then(event -> event.getPack().getContext().put(event.loadTemplate(new PreLoadCompatibilityOptions()))) + .global(); + terraBukkitPlugin.getEventManager() .getHandler(FunctionalEventHandler.class) .register(this, ConfigurationLoadEvent.class) diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/config/PreLoadCompatibilityOptions.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/config/PreLoadCompatibilityOptions.java new file mode 100644 index 000000000..c9c4de3f4 --- /dev/null +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/config/PreLoadCompatibilityOptions.java @@ -0,0 +1,60 @@ +/* + * This file is part of Terra. + * + * Terra is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Terra is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Terra. If not, see . + */ + +package com.dfsek.terra.bukkit.config; + +import com.dfsek.tectonic.api.config.template.ConfigTemplate; +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; + +import com.dfsek.terra.api.properties.Properties; + + +@SuppressWarnings("FieldMayBeFinal") +public class PreLoadCompatibilityOptions implements ConfigTemplate, Properties { + @Value("minecraft.use-vanilla-biomes") + @Default + private boolean vanillaBiomes = false; + + @Value("minecraft.beard.enable") + @Default + private boolean beard = true; + + @Value("minecraft.beard.threshold") + @Default + private double beardThreshold = 0.5; + + @Value("minecraft.beard.air-threshold") + @Default + private double airThreshold = -0.5; + + public boolean useVanillaBiomes() { + return vanillaBiomes; + } + + public boolean isBeard() { + return beard; + } + + public double getBeardThreshold() { + return beardThreshold; + } + + public double getAirThreshold() { + return airThreshold; + } +} diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/handles/BukkitWorldHandle.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/handles/BukkitWorldHandle.java index 33e2f6708..563a3b66e 100644 --- a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/handles/BukkitWorldHandle.java +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/handles/BukkitWorldHandle.java @@ -52,7 +52,12 @@ public class BukkitWorldHandle implements WorldHandle { @Override public @NotNull EntityType getEntity(@NotNull String id) { if(!id.startsWith("minecraft:")) throw new IllegalArgumentException("Invalid entity identifier " + id); - return new BukkitEntityType(org.bukkit.entity.EntityType.valueOf(id.toUpperCase(Locale.ROOT).substring(10))); + String entityID = id.toUpperCase(Locale.ROOT).substring(10); + + return new BukkitEntityType(switch(entityID) { + case "END_CRYSTAL" -> org.bukkit.entity.EntityType.ENDER_CRYSTAL; + case "ENDER_CRYSTAL" -> throw new IllegalArgumentException("Invalid entity identifier " + id); // make sure this issue can't happen the other way around. + default -> org.bukkit.entity.EntityType.valueOf(entityID); + }); } - } diff --git a/platforms/bukkit/nms/v1_19_R1/build.gradle.kts b/platforms/bukkit/nms/v1_19_R1/build.gradle.kts index e40d548dc..96f9038d8 100644 --- a/platforms/bukkit/nms/v1_19_R1/build.gradle.kts +++ b/platforms/bukkit/nms/v1_19_R1/build.gradle.kts @@ -6,7 +6,7 @@ repositories { dependencies { api(project(":platforms:bukkit:common")) - paperDevBundle("1.19-R0.1-SNAPSHOT") + paperDevBundle(Versions.Bukkit.paperDevBundle) implementation("xyz.jpenilla", "reflection-remapper", "0.1.0-SNAPSHOT") } diff --git a/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/NMSChunkGeneratorDelegate.java b/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/NMSChunkGeneratorDelegate.java index 81933be0f..c35bae020 100644 --- a/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/NMSChunkGeneratorDelegate.java +++ b/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/NMSChunkGeneratorDelegate.java @@ -1,5 +1,11 @@ package com.dfsek.terra.bukkit.nms.v1_19_R1; +import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions; + +import com.dfsek.terra.bukkit.world.BukkitWorldProperties; + +import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState; + import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Codec; import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; @@ -10,14 +16,21 @@ import net.minecraft.core.SectionPos; import net.minecraft.server.level.WorldGenRegion; import net.minecraft.util.RandomSource; import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelHeightAccessor; import net.minecraft.world.level.NoiseColumn; import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.WorldGenLevel; 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.levelgen.Beardifier; +import net.minecraft.world.level.levelgen.DensityFunction; +import net.minecraft.world.level.levelgen.DensityFunction.FunctionContext; +import net.minecraft.world.level.levelgen.DensityFunction.SinglePointContext; import net.minecraft.world.level.levelgen.GenerationStep.Carving; import net.minecraft.world.level.levelgen.Heightmap.Types; import net.minecraft.world.level.levelgen.RandomState; @@ -103,7 +116,43 @@ public class NMSChunkGeneratorDelegate extends ChunkGenerator { public @NotNull CompletableFuture fillFromNoise(@NotNull Executor executor, @NotNull Blender blender, @NotNull RandomState noiseConfig, @NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) { - return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk); + return vanilla.fillFromNoise(executor, blender, noiseConfig, structureAccessor, chunk) + .thenApply(c -> { + LevelAccessor level = Reflection.STRUCTURE_MANAGER.getLevel(structureAccessor); + BiomeProvider biomeProvider = pack.getBiomeProvider(); + PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class); + if(compatibilityOptions.isBeard()) { + beard(structureAccessor, chunk, new BukkitWorldProperties(level.getMinecraftWorld().getWorld()), biomeProvider, compatibilityOptions); + } + return c; + }); + } + + private void beard(StructureManager structureAccessor, ChunkAccess chunk, WorldProperties world, BiomeProvider biomeProvider, + PreLoadCompatibilityOptions compatibilityOptions) { + Beardifier structureWeightSampler = Beardifier.forStructuresInChunk(structureAccessor, chunk.getPos()); + double threshold = compatibilityOptions.getBeardThreshold(); + double airThreshold = compatibilityOptions.getAirThreshold(); + int xi = chunk.getPos().x << 4; + int zi = chunk.getPos().z << 4; + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + int depth = 0; + for(int y = world.getMaxHeight(); y >= world.getMinHeight(); y--) { + double noise = structureWeightSampler.compute(new SinglePointContext(x + xi, y, z + zi)); + if(noise > threshold) { + chunk.setBlockState(new BlockPos(x, y, z), ((CraftBlockData) ((BukkitBlockState) delegate + .getPalette(x + xi, y, z + zi, world, biomeProvider) + .get(depth, x + xi, y, z + zi, world.getSeed())).getHandle()).getState(), false); + depth++; + } else if(noise < airThreshold) { + chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), false); + } else { + depth = 0; + } + } + } + } } @Override diff --git a/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/Reflection.java b/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/Reflection.java index 8fa9a0b01..b704952d5 100644 --- a/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/Reflection.java +++ b/platforms/bukkit/nms/v1_19_R1/src/main/java/com/dfsek/terra/bukkit/nms/v1_19_R1/Reflection.java @@ -1,14 +1,18 @@ package com.dfsek.terra.bukkit.nms.v1_19_R1; import net.minecraft.core.MappedRegistry; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.StructureManager; import xyz.jpenilla.reflectionremapper.ReflectionRemapper; import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory; +import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter; import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter; import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies; public class Reflection { public static final MappedRegistryProxy MAPPED_REGISTRY; + public static final StructureManagerProxy STRUCTURE_MANAGER; static { ReflectionRemapper reflectionRemapper = ReflectionRemapper.forReobfMappingsInPaperJar(); @@ -16,6 +20,7 @@ public class Reflection { Reflection.class.getClassLoader()); MAPPED_REGISTRY = reflectionProxyFactory.reflectionProxy(MappedRegistryProxy.class); + STRUCTURE_MANAGER = reflectionProxyFactory.reflectionProxy(StructureManagerProxy.class); } @@ -24,4 +29,10 @@ public class Reflection { @FieldSetter("frozen") void setFrozen(MappedRegistry instance, boolean frozen); } + + @Proxies(StructureManager.class) + public interface StructureManagerProxy { + @FieldGetter("level") + LevelAccessor getLevel(StructureManager instance); + } }