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