diff --git a/.github/workflows/gradle-build.yml b/.github/workflows/gradle-build.yml index b289ca1aa5..959a6a81f5 100644 --- a/.github/workflows/gradle-build.yml +++ b/.github/workflows/gradle-build.yml @@ -18,10 +18,10 @@ jobs: steps: - uses: actions/checkout@v4.2.2 - - name: Set up JDK 21 + - name: Set up JDK 25 uses: actions/setup-java@v4.7.1 with: - java-version: '21' + java-version: '25' distribution: 'temurin' server-id: github # Value of the distributionManagement/repository/id field of the pom.xml settings-path: ${{ github.workspace }} # location for the settings.xml file diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 5ca04d11a5..c0a763e2d9 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { //TODO Allow pulling from Versions.kt implementation("com.gradleup.shadow", "shadow-gradle-plugin", "8.3.9") - implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "2.0.0-beta.18") + implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "2.0.0-beta.21") implementation("org.ow2.asm", "asm", "9.9") implementation("org.ow2.asm", "asm-tree", "9.9") implementation("com.dfsek.tectonic", "common", "4.3.1") diff --git a/buildSrc/src/main/kotlin/Utils.kt b/buildSrc/src/main/kotlin/Utils.kt index 679786788b..b786d0645c 100644 --- a/buildSrc/src/main/kotlin/Utils.kt +++ b/buildSrc/src/main/kotlin/Utils.kt @@ -7,19 +7,14 @@ var isPrerelease = false fun Project.getGitHash(): String { - val stdout = ByteArrayOutputStream() - exec { + return providers.exec { commandLine = mutableListOf("git", "rev-parse", "--short", "HEAD") - standardOutput = stdout - } - return stdout.toString().trim() + }.standardOutput.asText.get().trim() } fun Project.gitClone(name: String) { - val stdout = ByteArrayOutputStream() - exec { + providers.exec { commandLine = mutableListOf("git", "clone", name) - standardOutput = stdout } } diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index cb593331ab..ee394083dc 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -12,7 +12,7 @@ object Versions { const val strata = "1.3.2" const val seismic = "2.5.7" - const val cloud = "2.0.0" + const val cloud = "2.1.0" const val caffeine = "3.2.2" @@ -60,16 +60,17 @@ object Versions { // } object Bukkit { - const val minecraft = "1.21.10" + const val minecraft = "26.1.2" const val nms = "$minecraft-R0.1" - const val paperBuild = "$nms-20251012.013929-7" + const val paperBuild = "26.1.2.build.60-stable" const val paper = paperBuild const val paperLib = "1.0.8" const val reflectionRemapper = "0.1.3" const val paperDevBundle = paperBuild - const val runPaper = "2.3.1" - const val paperWeight = "2.0.0-beta.19" - const val cloud = "2.0.0-beta.12" + const val paperDevBundle26_3 = "26.3.build.156-beta" + const val runPaper = "3.0.2" + const val paperWeight = "2.0.0-beta.21" + const val cloud = "2.0.1" const val multiverse = "5.3.0" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baa..b1b8ef56b4 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9128c7d428..2b7a686b44 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=845952a9d6afa783db70bb3b0effaae45ae5542ca2bb7929619e8af49cb634cf -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionSha256Sum=553c78f50dafcd54d65b9a444649057857469edf836431389695608536d6b746 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/platforms/bukkit/build.gradle.kts b/platforms/bukkit/build.gradle.kts index b55db49b62..8de3d72281 100644 --- a/platforms/bukkit/build.gradle.kts +++ b/platforms/bukkit/build.gradle.kts @@ -8,10 +8,16 @@ dependencies { paperweight.paperDevBundle(Versions.Bukkit.paperDevBundle) shaded(project(":platforms:bukkit:common")) - shaded(project(":platforms:bukkit:nms")) + shaded(project(path = ":platforms:bukkit:nms:common", configuration = "runtimeElements")) + shaded(project(path = ":platforms:bukkit:nms:26.1", configuration = "runtimeElements")) + shaded(project(path = ":platforms:bukkit:nms:26.3", configuration = "runtimeElements")) shaded("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper) } +tasks.withType().configureEach { + options.release = 25 +} + tasks { shadowJar { relocate("io.papermc.lib", "com.dfsek.terra.lib.paperlib") diff --git a/platforms/bukkit/common/build.gradle.kts b/platforms/bukkit/common/build.gradle.kts index 57c7ba5a92..11673f8fca 100644 --- a/platforms/bukkit/common/build.gradle.kts +++ b/platforms/bukkit/common/build.gradle.kts @@ -1,6 +1,9 @@ repositories { } +tasks.withType().configureEach { + options.release = 25 +} dependencies { shadedApi(project(":common:implementation:base")) diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/NMSInitializer.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/NMSInitializer.java index f15beea0a6..6bb64ca3a4 100644 --- a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/NMSInitializer.java +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/NMSInitializer.java @@ -6,10 +6,12 @@ import java.util.List; import com.dfsek.terra.bukkit.util.VersionUtil; +import com.dfsek.terra.bukkit.util.BukkitNMSAccess; +import com.dfsek.terra.bukkit.util.BukkitUtils; public interface NMSInitializer { - List SUPPORTED_VERSIONS = List.of("v1.21.9", "v1.21.10"); + List SUPPORTED_VERSIONS = List.of("v26.1.2", "v26.2", "v26.3"); String MINECRAFT_VERSION = VersionUtil.getMinecraftVersionInfo().toString(); String TERRA_PACKAGE = NMSInitializer.class.getPackageName(); @@ -44,10 +46,19 @@ static PlatformImpl init(TerraBukkitPlugin plugin) { private static PlatformImpl constructPlatform(TerraBukkitPlugin plugin) { try { - Class platformClass = Class.forName(TERRA_PACKAGE + ".nms.NMSPlatform"); - return (PlatformImpl) platformClass + String bindingsVersion = switch(MINECRAFT_VERSION) { + case "v26.3" -> "v26_3"; + case "v26.1.2", "v26.2" -> "v26_1"; + default -> "v26_1"; + }; + Class platformClass = Class.forName(TERRA_PACKAGE + ".nms." + bindingsVersion + ".NMSPlatform"); + PlatformImpl platform = (PlatformImpl) platformClass .getConstructor(TerraBukkitPlugin.class) .newInstance(plugin); + if(platform instanceof BukkitNMSAccess nmsAccess) { + BukkitUtils.setNMSAccess(nmsAccess); + } + return platform; } catch(ReflectiveOperationException e) { throw new RuntimeException("Error initializing NMS bindings. Report this to Terra.", e); } 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 0136d4389a..3ad138d837 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 @@ -19,6 +19,7 @@ import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.block.data.BlockData; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +30,9 @@ import com.dfsek.terra.bukkit.util.BukkitUtils; import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + public class BukkitWorldHandle implements WorldHandle { private static final Logger logger = LoggerFactory.getLogger(BukkitWorldHandle.class); @@ -40,9 +44,19 @@ public BukkitWorldHandle() { @Override public synchronized @NotNull BlockState createBlockState(@NotNull String data) { - org.bukkit.block.data.BlockData bukkitData = Bukkit.createBlockData( - data); // somehow bukkit managed to make this not thread safe! :) - return BukkitBlockState.newInstance(bukkitData); + try { + org.bukkit.block.data.BlockData bukkitData = Bukkit.createBlockData(data); // somehow bukkit managed to make this not thread safe! :) + return BukkitBlockState.newInstance(bukkitData); + } catch (Exception ignored) { + try { + Class hacks = Class.forName("com.dfsek.terra.bukkit.nms.AwfulBukkitHacks"); + Method method = hacks.getMethod("createBlockState", String.class); + Object result = method.invoke(null, data); + return BukkitBlockState.newInstance((BlockData) result); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new IllegalArgumentException("Invalid block state data: " + data); + } + } } @Override diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/util/BukkitNMSAccess.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/util/BukkitNMSAccess.java new file mode 100644 index 0000000000..b9a88bf504 --- /dev/null +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/util/BukkitNMSAccess.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.bukkit.util; + +import org.bukkit.Location; +import org.bukkit.block.CreatureSpawner; + +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.entity.EntityTypeExtended; + + +public interface BukkitNMSAccess { + EntityType getEntityType(String data); + + org.bukkit.entity.Entity spawnEntity(Object target, Location location, EntityType type); + + void setSpawnerEntity(CreatureSpawner spawner, EntityTypeExtended type); +} diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/util/BukkitUtils.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/util/BukkitUtils.java index eb5d622708..1a450e74fb 100644 --- a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/util/BukkitUtils.java +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/util/BukkitUtils.java @@ -1,33 +1,62 @@ package com.dfsek.terra.bukkit.util; import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.block.CreatureSpawner; import org.bukkit.block.data.BlockData; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Locale; import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.entity.EntityTypeExtended; import com.dfsek.terra.bukkit.world.entity.BukkitEntityType; public class BukkitUtils { - private static final Logger logger = LoggerFactory.getLogger(BukkitUtils.class); + private static volatile BukkitNMSAccess nmsAccess; public static boolean isLiquid(BlockData blockState) { Material material = blockState.getMaterial(); return material == Material.WATER || material == Material.LAVA; } + public static void setNMSAccess(BukkitNMSAccess access) { + nmsAccess = access; + } + public static EntityType getEntityType(String id) { - if(!id.startsWith("minecraft:")) throw new IllegalArgumentException("Invalid entity identifier " + id); - String entityID = id.toUpperCase(Locale.ROOT).substring(10); - - return new BukkitEntityType(switch(entityID) { - case "END_CRYSTAL" -> org.bukkit.entity.EntityType.END_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); - }); + BukkitNMSAccess access = nmsAccess; + return access == null ? getBukkitEntityType(id) : access.getEntityType(id); + } + + public static org.bukkit.entity.Entity spawnEntity(Object region, org.bukkit.Location location, EntityType entityType) { + if(entityType instanceof EntityTypeExtended) { + BukkitNMSAccess access = nmsAccess; + if(access == null) throw new IllegalStateException("Entity NBT loading requires Terra's NMS bindings"); + return access.spawnEntity(region, location, entityType); + } + + org.bukkit.entity.EntityType bukkitType = ((BukkitEntityType) entityType).getHandle(); + if(region instanceof org.bukkit.generator.LimitedRegion limitedRegion) { + return limitedRegion.spawnEntity(location, bukkitType); + } + return ((org.bukkit.World) region).spawnEntity(location, bukkitType); + } + + public static void setSpawnerEntity(CreatureSpawner spawner, EntityTypeExtended type) { + BukkitNMSAccess access = nmsAccess; + if(access == null) throw new IllegalStateException("Entity NBT loading requires Terra's NMS bindings"); + access.setSpawnerEntity(spawner, type); + } + + private static EntityType getBukkitEntityType(String data) { + int nbtStart = data.indexOf('{'); + if(nbtStart >= 0) { + throw new IllegalArgumentException("Entity NBT loading requires Terra's NMS bindings: " + data); + } + + NamespacedKey key = NamespacedKey.fromString(data); + org.bukkit.entity.EntityType type = key == null ? null : Registry.ENTITY_TYPE.get(key); + if(type == null) throw new IllegalArgumentException("Invalid entity identifier " + data); + return new BukkitEntityType(type); } } diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/BukkitProtoWorld.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/BukkitProtoWorld.java index 3de237bb35..67fbf1ee2e 100644 --- a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/BukkitProtoWorld.java +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/BukkitProtoWorld.java @@ -24,7 +24,6 @@ import com.dfsek.terra.bukkit.util.BukkitUtils; import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState; import com.dfsek.terra.bukkit.world.block.state.BukkitBlockEntity; -import com.dfsek.terra.bukkit.world.entity.BukkitEntityType; public class BukkitProtoWorld implements ProtoWorld { @@ -89,7 +88,7 @@ public int getMinHeight() { @Override public Entity spawnEntity(double x, double y, double z, EntityType entityType) { return access((int) x, (int) y, (int) z, () -> new BukkitEntity( - delegate.spawnEntity(new Location(delegate.getWorld(), x, y, z), ((BukkitEntityType) entityType).getHandle()))).orElse( + BukkitUtils.spawnEntity(delegate, new Location(delegate.getWorld(), x, y, z), entityType))).orElse( null); } diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/BukkitServerWorld.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/BukkitServerWorld.java index 9ddf8ae87f..cc3693734e 100644 --- a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/BukkitServerWorld.java +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/BukkitServerWorld.java @@ -30,8 +30,8 @@ import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; import com.dfsek.terra.bukkit.BukkitEntity; import com.dfsek.terra.bukkit.generator.BukkitChunkGeneratorWrapper; +import com.dfsek.terra.bukkit.util.BukkitUtils; import com.dfsek.terra.bukkit.world.block.state.BukkitBlockEntity; -import com.dfsek.terra.bukkit.world.entity.BukkitEntityType; public class BukkitServerWorld implements ServerWorld { @@ -43,8 +43,7 @@ public BukkitServerWorld(org.bukkit.World delegate) { @Override public Entity spawnEntity(double x, double y, double z, EntityType entityType) { - return new BukkitEntity( - delegate.spawnEntity(new Location(delegate, x, y, z), ((BukkitEntityType) entityType).getHandle())); + return new BukkitEntity(BukkitUtils.spawnEntity(delegate, new Location(delegate, x, y, z), entityType)); } @Override diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitMobSpawner.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitMobSpawner.java index dc9d1ecb6c..9302a61782 100644 --- a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitMobSpawner.java +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/block/state/BukkitMobSpawner.java @@ -23,6 +23,7 @@ import com.dfsek.terra.api.block.entity.MobSpawner; import com.dfsek.terra.api.block.entity.SerialState; import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.entity.EntityTypeExtended; import com.dfsek.terra.bukkit.util.BukkitUtils; import com.dfsek.terra.bukkit.world.entity.BukkitEntityType; @@ -39,7 +40,12 @@ public EntityType getSpawnedType() { @Override public void setSpawnedType(@NotNull EntityType creatureType) { - ((CreatureSpawner) getHandle()).setSpawnedType(((BukkitEntityType) creatureType).getHandle()); + CreatureSpawner spawner = (CreatureSpawner) getHandle(); + if(creatureType instanceof EntityTypeExtended extended) { + BukkitUtils.setSpawnerEntity(spawner, extended); + } else { + spawner.setSpawnedType(((BukkitEntityType) creatureType).getHandle()); + } } @Override diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/entity/BukkitEntityData.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/entity/BukkitEntityData.java new file mode 100644 index 0000000000..40de748251 --- /dev/null +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/entity/BukkitEntityData.java @@ -0,0 +1,16 @@ +package com.dfsek.terra.bukkit.world.entity; + +import com.dfsek.terra.api.data.ExtendedData; + + +public record BukkitEntityData(String snbt) implements ExtendedData { + @Override + public Object getHandle() { + return snbt; + } + + @Override + public String toString() { + return snbt; + } +} diff --git a/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/entity/BukkitEntityTypeExtended.java b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/entity/BukkitEntityTypeExtended.java new file mode 100644 index 0000000000..f1ba5a400d --- /dev/null +++ b/platforms/bukkit/common/src/main/java/com/dfsek/terra/bukkit/world/entity/BukkitEntityTypeExtended.java @@ -0,0 +1,34 @@ +package com.dfsek.terra.bukkit.world.entity; + +import com.dfsek.terra.api.data.ExtendedData; +import com.dfsek.terra.api.entity.EntityType; +import com.dfsek.terra.api.entity.EntityTypeExtended; + + +public final class BukkitEntityTypeExtended extends BukkitEntityType implements EntityTypeExtended { + private final BukkitEntityData data; + + public BukkitEntityTypeExtended(org.bukkit.entity.EntityType delegate, String snbt) { + super(delegate); + this.data = new BukkitEntityData(snbt); + } + + @Override + public ExtendedData getData() { + return data; + } + + @Override + public EntityTypeExtended setData(ExtendedData data) { + Object handle = data.getHandle(); + if(!(handle instanceof String snbt)) { + throw new IllegalArgumentException("Bukkit entity data must contain SNBT text"); + } + return new BukkitEntityTypeExtended(getHandle(), snbt); + } + + @Override + public EntityType getType() { + return new BukkitEntityType(getHandle()); + } +} diff --git a/platforms/bukkit/nms/26.1/build.gradle.kts b/platforms/bukkit/nms/26.1/build.gradle.kts new file mode 100644 index 0000000000..d5aa248369 --- /dev/null +++ b/platforms/bukkit/nms/26.1/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("io.papermc.paperweight.userdev") +} + +tasks.withType().configureEach { + options.release = 25 +} + +dependencies { + compileOnly(project(":platforms:bukkit:common")) + compileOnly(project(":platforms:bukkit:nms:common")) + paperweight.paperDevBundle(Versions.Bukkit.paperDevBundle) +} diff --git a/platforms/bukkit/nms/26.1/src/main/java/com/dfsek/terra/bukkit/nms/v26_1/Bindings.java b/platforms/bukkit/nms/26.1/src/main/java/com/dfsek/terra/bukkit/nms/v26_1/Bindings.java new file mode 100644 index 0000000000..bb0e5203d8 --- /dev/null +++ b/platforms/bukkit/nms/26.1/src/main/java/com/dfsek/terra/bukkit/nms/v26_1/Bindings.java @@ -0,0 +1,72 @@ +package com.dfsek.terra.bukkit.nms.v26_1; + +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.levelgen.Beardifier; +import net.minecraft.world.level.levelgen.DensityFunction.SinglePointContext; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.attribute.modifier.AttributeModifier; + +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; +import com.dfsek.terra.api.world.info.WorldProperties; +import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.bukkit.nms.NMSStructureBeardifier; +import com.dfsek.terra.bukkit.nms.NMSVersionBindings; +import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties; + + +public final class Bindings implements NMSVersionBindings { + @Override + public void addSpawn(MobSpawnSettings.Builder builder, MobCategory category, EntityType type, int weight, + int minCount, int maxCount) { + builder.addSpawn(category, weight, new SpawnerData(type, minCount, maxCount)); + } + + @Override + public void addSpawnCost(MobSpawnSettings.Builder builder, EntityType type, double mass, double gravity) { + builder.addMobCharge(type, mass, gravity); + } + + @Override + public boolean setCreatureGenerationProbability(MobSpawnSettings.Builder builder, float probability) { + builder.creatureGenerationProbability(probability); + return true; + } + + @Override + public void copyVanillaSpawnSettings(Biome.BiomeBuilder builder, Biome biome) { + builder.mobSpawnSettings(biome.getMobSettings()); + } + + @Override + public void setSpawnSettings(Biome.BiomeBuilder builder, MobSpawnSettings settings) { + builder.mobSpawnSettings(settings); + } + + @Override + public void applyBiomeColorOverrides(Biome.BiomeBuilder builder, VanillaBiomeProperties properties) { + if(properties.getFogColor() != null) { + builder.modifyAttribute(EnvironmentAttributes.FOG_COLOR, AttributeModifier.override(), properties.getFogColor()); + } + if(properties.getWaterFogColor() != null) { + builder.modifyAttribute(EnvironmentAttributes.WATER_FOG_COLOR, AttributeModifier.override(), properties.getWaterFogColor()); + } + if(properties.getSkyColor() != null) { + builder.modifyAttribute(EnvironmentAttributes.SKY_COLOR, AttributeModifier.override(), properties.getSkyColor()); + } + } + + @Override + public void applyStructureBeard(ChunkGenerator generator, StructureManager structures, ChunkAccess chunk, WorldProperties world, + BiomeProvider biomeProvider, PreLoadCompatibilityOptions options) { + Beardifier beardifier = Beardifier.forStructuresInChunk(structures, chunk.getPos()); + NMSStructureBeardifier.apply(generator, chunk, world, biomeProvider, options, + (x, y, z) -> beardifier.compute(new SinglePointContext(x, y, z))); + } +} diff --git a/platforms/bukkit/nms/26.1/src/main/java/com/dfsek/terra/bukkit/nms/v26_1/NMSPlatform.java b/platforms/bukkit/nms/26.1/src/main/java/com/dfsek/terra/bukkit/nms/v26_1/NMSPlatform.java new file mode 100644 index 0000000000..753a6cda1a --- /dev/null +++ b/platforms/bukkit/nms/26.1/src/main/java/com/dfsek/terra/bukkit/nms/v26_1/NMSPlatform.java @@ -0,0 +1,11 @@ +package com.dfsek.terra.bukkit.nms.v26_1; + +import com.dfsek.terra.bukkit.TerraBukkitPlugin; +import com.dfsek.terra.bukkit.nms.NMSPlatformBase; + + +public final class NMSPlatform extends NMSPlatformBase { + public NMSPlatform(TerraBukkitPlugin plugin) { + super(plugin, new Bindings()); + } +} diff --git a/platforms/bukkit/nms/26.3/build.gradle.kts b/platforms/bukkit/nms/26.3/build.gradle.kts new file mode 100644 index 0000000000..7f91b59a5c --- /dev/null +++ b/platforms/bukkit/nms/26.3/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("io.papermc.paperweight.userdev") +} + +tasks.withType().configureEach { + options.release = 25 +} + +dependencies { + compileOnly(project(":platforms:bukkit:common")) + compileOnly(project(":platforms:bukkit:nms:common")) + paperweight.paperDevBundle(Versions.Bukkit.paperDevBundle26_3) +} diff --git a/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/Bindings.java b/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/Bindings.java new file mode 100644 index 0000000000..55c8f552f1 --- /dev/null +++ b/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/Bindings.java @@ -0,0 +1,95 @@ +package com.dfsek.terra.bukkit.nms.v26_3; + +import net.minecraft.util.valueproviders.UniformInt; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.attribute.modifier.AttributeModifier; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.levelgen.Beardifier; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.levelgen.densityfunction.SamplerContext; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; +import com.dfsek.terra.api.world.info.WorldProperties; +import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.bukkit.nms.NMSBiomeProvider; +import com.dfsek.terra.bukkit.nms.NMSChunkGeneratorDelegate; +import com.dfsek.terra.bukkit.nms.NMSStructureBeardifier; +import com.dfsek.terra.bukkit.nms.NMSVersionBindings; +import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties; + + +public final class Bindings implements NMSVersionBindings { + @Override + public NMSBiomeProvider createBiomeProvider(BiomeProvider delegate, long seed) { + return new BiomeProvider26_3(delegate, seed); + } + + @Override + public NMSChunkGeneratorDelegate createChunkGenerator( + net.minecraft.world.level.chunk.ChunkGenerator vanilla, ConfigPack pack, + NMSBiomeProvider biomeProvider, long seed) { + return new ChunkGenerator26_3(vanilla, pack, biomeProvider, seed, this); + } + + @Override + public void addSpawn(MobSpawnSettings.Builder builder, MobCategory category, EntityType type, int weight, + int minCount, int maxCount) { + builder.addSpawn(type, category, weight, UniformInt.of(minCount, maxCount)); + } + + @Override + public void addSpawnCost(MobSpawnSettings.Builder builder, EntityType type, double mass, double gravity) { + builder.addMobSpawnCost(type, mass, gravity); + } + + @Override + public boolean setCreatureGenerationProbability(MobSpawnSettings.Builder builder, float probability) { + return false; + } + + @Override + public void copyVanillaSpawnSettings(Biome.BiomeBuilder builder, Biome biome) { + // 26.3 stores natural spawn settings in the biome's environment attributes, + // which NMSBiomeInjector already copies into its builder. + } + + @Override + public void setSpawnSettings(Biome.BiomeBuilder builder, MobSpawnSettings settings) { + builder.setAttribute(EnvironmentAttributes.NATURAL_MOB_SPAWNS, settings); + } + + @Override + public void applyBiomeColorOverrides(Biome.BiomeBuilder builder, VanillaBiomeProperties properties) { + if(properties.getFogColor() != null) { + builder.modifyAttribute(EnvironmentAttributes.FOG_COLOR, AttributeModifier.override(), color(properties.getFogColor())); + } + if(properties.getWaterFogColor() != null) { + builder.modifyAttribute(EnvironmentAttributes.WATER_FOG_COLOR, AttributeModifier.override(), color(properties.getWaterFogColor())); + } + if(properties.getSkyColor() != null) { + builder.modifyAttribute(EnvironmentAttributes.SKY_COLOR, AttributeModifier.override(), color(properties.getSkyColor())); + } + } + + private static org.joml.Vector3f color(int rgb) { + return new org.joml.Vector3f( + ((rgb >>> 16) & 0xFF) / 255.0f, + ((rgb >>> 8) & 0xFF) / 255.0f, + (rgb & 0xFF) / 255.0f + ); + } + + @Override + public void applyStructureBeard(ChunkGenerator generator, StructureManager structures, ChunkAccess chunk, WorldProperties world, + BiomeProvider biomeProvider, PreLoadCompatibilityOptions options) { + Beardifier beardifier = Beardifier.forStructuresInChunk(structures, chunk.getPos()); + NMSStructureBeardifier.apply(generator, chunk, world, biomeProvider, options, + (x, y, z) -> beardifier.sampleValue(SamplerContext.EMPTY_UNCACHED, x, y, z)); + } +} diff --git a/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/BiomeProvider26_3.java b/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/BiomeProvider26_3.java new file mode 100644 index 0000000000..423725dd52 --- /dev/null +++ b/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/BiomeProvider26_3.java @@ -0,0 +1,20 @@ +package com.dfsek.terra.bukkit.nms.v26_3; + +import net.minecraft.world.level.biome.BiomeResolver; +import net.minecraft.world.level.biome.Climate.Sampler; +import org.jetbrains.annotations.NotNull; + +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.bukkit.nms.NMSBiomeProvider; + + +public final class BiomeProvider26_3 extends NMSBiomeProvider { + public BiomeProvider26_3(BiomeProvider delegate, long seed) { + super(delegate, seed); + } + + @Override + public @NotNull BiomeResolver createResolver(@NotNull Sampler sampler) { + return (x, y, z) -> getNoiseBiome(x, y, z, sampler); + } +} diff --git a/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/ChunkGenerator26_3.java b/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/ChunkGenerator26_3.java new file mode 100644 index 0000000000..0043479328 --- /dev/null +++ b/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/ChunkGenerator26_3.java @@ -0,0 +1,48 @@ +package com.dfsek.terra.bukkit.nms.v26_3; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Holder; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.levelgen.RandomState; +import net.minecraft.world.level.levelgen.blending.Blender; +import net.minecraft.world.level.levelgen.densityfunction.SamplerContext; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.bukkit.nms.NMSBiomeProvider; +import com.dfsek.terra.bukkit.nms.NMSChunkGeneratorDelegate; +import com.dfsek.terra.bukkit.nms.NMSVersionBindings; + + +public final class ChunkGenerator26_3 extends NMSChunkGeneratorDelegate { + private final ChunkGenerator vanilla; + + public ChunkGenerator26_3(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed, + NMSVersionBindings bindings) { + super(vanilla, pack, biomeProvider, seed, bindings); + this.vanilla = vanilla; + } + + @Override + public CompletableFuture buildTerrain(ChunkAccess chunk, Blender blender, RandomState randomState, + StructureManager structures, BiomeManager biomeManager, + WorldGenRegion region, Set> biomes) { + return vanilla.buildTerrain(chunk, blender, randomState, structures, biomeManager, region, biomes) + .thenApply(result -> { + applyStructureBeard(structures, result); + return result; + }); + } + + @Override + public void addDebugScreenInfo(List text, RandomState randomState, BlockPos pos, SamplerContext context) { + } +} diff --git a/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/NMSPlatform.java b/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/NMSPlatform.java new file mode 100644 index 0000000000..3da0e29113 --- /dev/null +++ b/platforms/bukkit/nms/26.3/src/main/java/com/dfsek/terra/bukkit/nms/v26_3/NMSPlatform.java @@ -0,0 +1,11 @@ +package com.dfsek.terra.bukkit.nms.v26_3; + +import com.dfsek.terra.bukkit.TerraBukkitPlugin; +import com.dfsek.terra.bukkit.nms.NMSPlatformBase; + + +public final class NMSPlatform extends NMSPlatformBase { + public NMSPlatform(TerraBukkitPlugin plugin) { + super(plugin, new Bindings()); + } +} diff --git a/platforms/bukkit/nms/build.gradle.kts b/platforms/bukkit/nms/build.gradle.kts index b521d372ab..22b4a74bb0 100644 --- a/platforms/bukkit/nms/build.gradle.kts +++ b/platforms/bukkit/nms/build.gradle.kts @@ -1,9 +1,17 @@ plugins { - id("io.papermc.paperweight.userdev") + `java-library` +} + +subprojects { + group = "com.dfsek.terra.bukkit.nms" +} + +tasks.withType().configureEach { + options.release = 25 } dependencies { - api(project(":platforms:bukkit:common")) - paperweight.paperDevBundle(Versions.Bukkit.paperDevBundle) - implementation("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper) -} \ No newline at end of file + api(project(path = ":platforms:bukkit:nms:common", configuration = "runtimeElements")) + api(project(path = ":platforms:bukkit:nms:26.1", configuration = "runtimeElements")) + api(project(path = ":platforms:bukkit:nms:26.3", configuration = "runtimeElements")) +} diff --git a/platforms/bukkit/nms/common/build.gradle.kts b/platforms/bukkit/nms/common/build.gradle.kts new file mode 100644 index 0000000000..d1816ef132 --- /dev/null +++ b/platforms/bukkit/nms/common/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("io.papermc.paperweight.userdev") +} + +tasks.withType().configureEach { + options.release = 25 +} + +dependencies { + compileOnly(project(":platforms:bukkit:common")) + compileOnly("xyz.jpenilla", "reflection-remapper", Versions.Bukkit.reflectionRemapper) + paperweight.paperDevBundle(Versions.Bukkit.paperDevBundle) +} + +tasks.withType().configureEach { + archiveBaseName.set("terra-bukkit-nms-common") +} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/AwfulBukkitHacks.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/AwfulBukkitHacks.java similarity index 51% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/AwfulBukkitHacks.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/AwfulBukkitHacks.java index beea1c536d..973737d6b7 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/AwfulBukkitHacks.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/AwfulBukkitHacks.java @@ -1,18 +1,42 @@ package com.dfsek.terra.bukkit.nms; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.commands.arguments.blocks.BlockStateParser; +import net.minecraft.commands.arguments.blocks.BlockStateParser.BlockResult; import net.minecraft.core.Holder; import net.minecraft.core.Holder.Reference; -import net.minecraft.core.HolderSet; +import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderSet.Named; import net.minecraft.core.MappedRegistry; import net.minecraft.core.RegistrationInfo; +import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.Registries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.TagParser; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.TagKey; -import net.minecraft.world.entity.npc.VillagerType; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.npc.villager.VillagerType; import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.block.Block; +import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; +import org.bukkit.Registry; +import org.bukkit.World; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.block.data.BlockData; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.craftbukkit.block.data.CraftBlockData; +import org.bukkit.craftbukkit.entity.CraftEntitySnapshot; +import org.bukkit.craftbukkit.generator.CraftLimitedRegion; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,18 +49,21 @@ import java.util.Set; import java.util.stream.Collectors; -import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties; +import com.dfsek.terra.api.entity.EntityTypeExtended; import com.dfsek.terra.bukkit.world.BukkitBiomeInfo; import com.dfsek.terra.bukkit.world.BukkitPlatformBiome; +import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties; +import com.dfsek.terra.bukkit.world.entity.BukkitEntityType; +import com.dfsek.terra.bukkit.world.entity.BukkitEntityTypeExtended; import com.dfsek.terra.registry.master.ConfigRegistry; public class AwfulBukkitHacks { private static final Logger LOGGER = LoggerFactory.getLogger(AwfulBukkitHacks.class); - private static final Map> terraBiomeMap = new HashMap<>(); + private static final Map> terraBiomeMap = new HashMap<>(); - public static void registerBiomes(ConfigRegistry configRegistry) { + public static void registerBiomes(ConfigRegistry configRegistry, NMSVersionBindings bindings) { try { LOGGER.info("Hacking biome registry..."); MappedRegistry biomeRegistry = (MappedRegistry) RegistryFetcher.biomeRegistry(); @@ -50,15 +77,15 @@ public static void registerBiomes(ConfigRegistry configRegistry) { BukkitPlatformBiome platformBiome = (BukkitPlatformBiome) biome.getPlatformBiome(); NamespacedKey vanillaBukkitKey = platformBiome.getHandle().getKey(); - ResourceLocation vanillaMinecraftKey = ResourceLocation.fromNamespaceAndPath(vanillaBukkitKey.getNamespace(), + Identifier vanillaMinecraftKey = Identifier.fromNamespaceAndPath(vanillaBukkitKey.getNamespace(), vanillaBukkitKey.getKey()); VanillaBiomeProperties vanillaBiomeProperties = biome.getContext().get(VanillaBiomeProperties.class); Biome platform = NMSBiomeInjector.createBiome(biomeRegistry.get(vanillaMinecraftKey).orElseThrow().value(), - vanillaBiomeProperties); + vanillaBiomeProperties, bindings); - ResourceLocation delegateMinecraftKey = ResourceLocation.fromNamespaceAndPath("terra", + Identifier delegateMinecraftKey = Identifier.fromNamespaceAndPath("terra", NMSBiomeInjector.createBiomeID(pack, key)); NamespacedKey delegateBukkitKey = NamespacedKey.fromString(delegateMinecraftKey.toString()); ResourceKey delegateKey = ResourceKey.create(Registries.BIOME, delegateMinecraftKey); @@ -75,7 +102,7 @@ public static void registerBiomes(ConfigRegistry configRegistry) { Objects.requireNonNullElse(vanillaBiomeProperties.getVillagerType(), villagerMap.getOrDefault(delegateKey, VillagerType.PLAINS))); - terraBiomeMap.computeIfAbsent(vanillaMinecraftKey, i -> new ArrayList<>()).add(delegateKey.location()); + terraBiomeMap.computeIfAbsent(vanillaMinecraftKey, i -> new ArrayList<>()).add(delegateKey.identifier()); LOGGER.debug("Registered biome: " + delegateKey); } catch(NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { @@ -100,8 +127,8 @@ public static void registerBiomes(ConfigRegistry configRegistry) { tb -> NMSBiomeInjector.getEntry(biomeRegistry, tb).ifPresentOrElse( terra -> { LOGGER.debug("{} (vanilla for {}): {}", - vanilla.unwrapKey().orElseThrow().location(), - terra.unwrapKey().orElseThrow().location(), + vanilla.unwrapKey().orElseThrow().identifier(), + terra.unwrapKey().orElseThrow().identifier(), vanilla.tags().toList()); vanilla.tags() .forEach(tag -> collect @@ -120,7 +147,7 @@ public static void registerBiomes(ConfigRegistry configRegistry) { } private static void bindTags(MappedRegistry registry, Map, List>> tagEntries) { - Map, List>> map = new IdentityHashMap<>(); + Map, List>> map = new IdentityHashMap<>(); Reflection.MAPPED_REGISTRY.getByKey(registry).values().forEach(entry -> map.put(entry, new ArrayList<>())); tagEntries.forEach((tag, entries) -> { for(Holder holder : entries) { @@ -145,7 +172,7 @@ private static void bindTags(MappedRegistry registry, Map, List // ); // } - Map, HolderSet.Named> map2 = new IdentityHashMap<>(registry.getTags().collect(Collectors.toMap( + Map, Named> map2 = new IdentityHashMap<>(registry.getTags().collect(Collectors.toMap( Named::key, (named) -> named ))); @@ -160,5 +187,97 @@ private static void resetTags(MappedRegistry registry) { Reflection.MAPPED_REGISTRY.getByKey(registry).values().forEach( entry -> Reflection.HOLDER_REFERENCE.invokeBindTags(entry, Set.of())); } -} + // used by BukkitWorldHandle + public static BlockData createBlockState(@NotNull String data) throws CommandSyntaxException { + MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer(); + HolderLookup.Provider lookup = server.registryAccess(); + final HolderLookup blocks = lookup.lookupOrThrow(Registries.BLOCK); + BlockResult result = BlockStateParser.parseForBlock(blocks, new StringReader(data), true); + return CraftBlockData.createData(result.blockState()); + } + + public static com.dfsek.terra.api.entity.EntityType getEntityType(@NotNull String data) { + StringReader reader = new StringReader(data); + Identifier identifier; + CompoundTag entityNbt = null; + try { + identifier = Identifier.read(reader); + if(reader.canRead()) { + if(reader.peek() != '{') throw new IllegalArgumentException("Unexpected data after entity identifier: " + data); + entityNbt = TagParser.parseCompoundAsArgument(reader); + if(reader.canRead()) throw new IllegalArgumentException("Unexpected data after entity NBT: " + data); + entityNbt.putString("id", identifier.toString()); + } + } catch(CommandSyntaxException e) { + throw new IllegalArgumentException("Invalid entity data: " + data, e); + } + + if(BuiltInRegistries.ENTITY_TYPE.getOptional(identifier).isEmpty()) { + throw new IllegalArgumentException("Unknown entity type: " + identifier); + } + + NamespacedKey key = NamespacedKey.fromString(identifier.toString()); + org.bukkit.entity.EntityType bukkitType = key == null ? null : Registry.ENTITY_TYPE.get(key); + if(bukkitType == null) throw new IllegalArgumentException("Bukkit has no entity type for " + identifier); + + if(entityNbt == null) return new BukkitEntityType(bukkitType); + return new BukkitEntityTypeExtended(bukkitType, entityNbt.toString()); + } + + public static org.bukkit.entity.Entity spawnEntity(Object target, org.bukkit.Location location, + com.dfsek.terra.api.entity.EntityType type) { + if(!(type instanceof BukkitEntityTypeExtended extended)) { + throw new IllegalArgumentException("Expected a Bukkit entity type with NBT data"); + } + CompoundTag nbt = parseEntityNbt(extended); + + CraftLimitedRegion limitedRegion = target instanceof CraftLimitedRegion region ? region : null; + World world = limitedRegion == null ? (World) target : limitedRegion.getWorld(); + if(!(world instanceof CraftWorld craftWorld)) throw new IllegalArgumentException("Unsupported Bukkit world: " + world); + ServerLevel level = craftWorld.getHandle(); + + String entityIDText = nbt.getString("id") + .orElseThrow(() -> new IllegalArgumentException("Missing entity identifier in NBT")); + Identifier entityID; + try { + entityID = Identifier.read(new StringReader(entityIDText)); + } catch(CommandSyntaxException e) { + throw new IllegalArgumentException("Invalid entity identifier in NBT: " + entityIDText, e); + } + EntityType entityType = BuiltInRegistries.ENTITY_TYPE.getOptional(entityID) + .orElseThrow(() -> new IllegalArgumentException("Unknown entity type in NBT: " + entityID)); + + net.minecraft.world.entity.Entity entity = EntityType.loadEntityRecursive(entityType, nbt, level, EntitySpawnReason.STRUCTURE, loaded -> { + loaded.snapTo(location.getX(), location.getY(), location.getZ(), loaded.getYRot(), loaded.getXRot()); + return loaded; + }); + if(entity == null) throw new IllegalArgumentException("Could not load entity from NBT: " + nbt); + + if(limitedRegion != null) { + limitedRegion.addEntityWithPassengers(entity, CreatureSpawnEvent.SpawnReason.CUSTOM); + } else if(!level.tryAddFreshEntityWithPassengers(entity, CreatureSpawnEvent.SpawnReason.CUSTOM)) { + throw new IllegalStateException("Could not add entity to world: " + nbt); + } + + return entity.getBukkitEntity(); + } + + public static void setSpawnerEntity(CreatureSpawner spawner, EntityTypeExtended type) { + if(!(type instanceof BukkitEntityTypeExtended extended)) { + throw new IllegalArgumentException("Expected a Bukkit entity type with NBT data"); + } + spawner.setSpawnedEntity(CraftEntitySnapshot.create(parseEntityNbt(extended), extended.getHandle())); + } + + private static CompoundTag parseEntityNbt(BukkitEntityTypeExtended extended) { + Object rawData = extended.getData().getHandle(); + if(!(rawData instanceof String snbt)) throw new IllegalArgumentException("Bukkit entity data must contain SNBT text"); + + try { + return TagParser.parseCompoundFully(snbt); + } catch(CommandSyntaxException e) { + throw new IllegalArgumentException("Invalid entity NBT: " + snbt, e); + } + } +} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSAddon.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSAddon.java similarity index 100% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSAddon.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSAddon.java diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInfo.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInfo.java similarity index 100% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInfo.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInfo.java diff --git a/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java new file mode 100644 index 0000000000..ccfba8cc08 --- /dev/null +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java @@ -0,0 +1,131 @@ +package com.dfsek.terra.bukkit.nms; + +import com.dfsek.terra.api.registry.key.RegistryKey; + +import net.minecraft.core.Holder; +import net.minecraft.core.Registry; +import net.minecraft.resources.Identifier; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.attribute.AmbientAdditionsSettings; +import net.minecraft.world.attribute.AmbientMoodSettings; +import net.minecraft.world.attribute.AmbientSounds; +import net.minecraft.world.attribute.BackgroundMusic; +import net.minecraft.world.attribute.EnvironmentAttribute; +import net.minecraft.world.attribute.EnvironmentAttributeMap; +import net.minecraft.world.attribute.EnvironmentAttributeMap.Entry; +import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.attribute.modifier.AttributeModifier; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biome.BiomeBuilder; +import net.minecraft.world.level.biome.BiomeGenerationSettings; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.biome.BiomeSpecialEffects; + +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties; + + +public class NMSBiomeInjector { + + public static Optional> getEntry(Registry registry, Identifier identifier) { + return registry.getOptional(identifier).flatMap(registry::getResourceKey).flatMap(registry::get); + } + + public static Biome createBiome(Biome vanilla, VanillaBiomeProperties vanillaBiomeProperties, NMSVersionBindings bindings) + throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + Biome.BiomeBuilder builder = new Biome.BiomeBuilder(); + + BiomeSpecialEffects.Builder effects = new BiomeSpecialEffects.Builder(); + EnvironmentAttributeMap attributes = vanilla.getAttributes(); + builder.putAttributes(attributes); + + bindings.applyBiomeColorOverrides(builder, vanillaBiomeProperties); + if(vanillaBiomeProperties.getMusicVolume() != null) { + builder.modifyAttribute(EnvironmentAttributes.MUSIC_VOLUME, AttributeModifier.override(), vanillaBiomeProperties.getMusicVolume()); + } + + effects.waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor())); + effects.grassColorModifier( + Objects.requireNonNullElse(vanillaBiomeProperties.getGrassColorModifier(), vanilla.getSpecialEffects().grassColorModifier())); + + if(vanillaBiomeProperties.getGrassColor() == null) { + vanilla.getSpecialEffects().grassColorOverride().ifPresent(effects::grassColorOverride); + } else { + effects.grassColorOverride(vanillaBiomeProperties.getGrassColor()); + } + + if(vanillaBiomeProperties.getFoliageColor() == null) { + vanilla.getSpecialEffects().foliageColorOverride().ifPresent(effects::foliageColorOverride); + } else { + effects.foliageColorOverride(vanillaBiomeProperties.getFoliageColor()); + } + + if(vanillaBiomeProperties.getParticleConfig() != null) { + builder.modifyAttribute(EnvironmentAttributes.AMBIENT_PARTICLES, AttributeModifier.override(), + List.of(vanillaBiomeProperties.getParticleConfig())); + } + + Optional> loop = Optional.empty(); + Optional mood = Optional.empty(); + List additions = Collections.emptyList(); + + AmbientSounds sounds = getAttribute(attributes, EnvironmentAttributes.AMBIENT_SOUNDS); + if(sounds != null) { + loop = sounds.loop(); + mood = sounds.mood(); + additions = sounds.additions(); + } + + if(vanillaBiomeProperties.getLoopSound() != null) { + loop = RegistryFetcher.soundEventRegistry() + .get(vanillaBiomeProperties.getLoopSound().location()) + .map(ref -> ref); + } + + if(vanillaBiomeProperties.getMoodSound() != null) { + mood = Optional.ofNullable(vanillaBiomeProperties.getMoodSound()); + } + + if(vanillaBiomeProperties.getAdditionsSound() != null) { + additions = Collections.singletonList(vanillaBiomeProperties.getAdditionsSound()); + } + + builder.modifyAttribute(EnvironmentAttributes.AMBIENT_SOUNDS, AttributeModifier.override(), + new AmbientSounds(loop, mood, additions)); + + if(vanillaBiomeProperties.getMusic() != null) { + builder.modifyAttribute(EnvironmentAttributes.BACKGROUND_MUSIC, AttributeModifier.override(), + new BackgroundMusic(vanillaBiomeProperties.getMusic())); + } + + builder.hasPrecipitation(Objects.requireNonNullElse(vanillaBiomeProperties.getPrecipitation(), vanilla.hasPrecipitation())); + builder.temperature(Objects.requireNonNullElse(vanillaBiomeProperties.getTemperature(), vanilla.getBaseTemperature())); + builder.downfall(Objects.requireNonNullElse(vanillaBiomeProperties.getDownfall(), vanilla.climateSettings.downfall())); + builder.temperatureAdjustment( + Objects.requireNonNullElse(vanillaBiomeProperties.getTemperatureModifier(), vanilla.climateSettings.temperatureModifier())); + MobSpawnSettings spawnSettings = vanillaBiomeProperties.getSpawnSettings(); + if(spawnSettings == null) { + bindings.copyVanillaSpawnSettings(builder, vanilla); + } else { + bindings.setSpawnSettings(builder, spawnSettings); + } + + return builder.specialEffects(effects.build()).generationSettings(new BiomeGenerationSettings.PlainBuilder().build()).build(); + } + + public static String createBiomeID(ConfigPack pack, RegistryKey biomeID) { + return pack.getID().toLowerCase() + "/" + biomeID.getNamespace().toLowerCase(Locale.ROOT) + "/" + biomeID.getID().toLowerCase( + Locale.ROOT); + } + + private static T getAttribute(EnvironmentAttributeMap attributes, EnvironmentAttribute key) { + Entry entry = attributes.get(key); + return entry == null ? null : entry.applyModifier(key.defaultValue()); + } +} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeProvider.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeProvider.java similarity index 100% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeProvider.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeProvider.java diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java similarity index 63% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java index 0b21b9d128..82592a5d8a 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSChunkGeneratorDelegate.java @@ -9,19 +9,14 @@ import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.WorldGenLevel; 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.SinglePointContext; import net.minecraft.world.level.levelgen.Heightmap.Types; import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.blending.Blender; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -30,25 +25,25 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider; import com.dfsek.terra.api.world.info.WorldProperties; import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions; -import com.dfsek.terra.bukkit.world.BukkitWorldProperties; -import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState; public class NMSChunkGeneratorDelegate extends ChunkGenerator { - private static final Logger LOGGER = LoggerFactory.getLogger(NMSChunkGeneratorDelegate.class); private final com.dfsek.terra.api.world.chunk.generation.ChunkGenerator delegate; private final ChunkGenerator vanilla; private final ConfigPack pack; + private final NMSVersionBindings bindings; private final long seed; - public NMSChunkGeneratorDelegate(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed) { + public NMSChunkGeneratorDelegate(ChunkGenerator vanilla, ConfigPack pack, NMSBiomeProvider biomeProvider, long seed, + NMSVersionBindings bindings) { super(biomeProvider); this.delegate = pack.getGeneratorProvider().newInstance(pack); this.vanilla = vanilla; this.pack = pack; this.seed = seed; + this.bindings = bindings; } @Override @@ -90,41 +85,19 @@ public CompletableFuture fillFromNoise(@NotNull Blender blender, @NotNull StructureManager structureAccessor, @NotNull ChunkAccess chunk) { return vanilla.fillFromNoise(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); - } + applyStructureBeard(structureAccessor, chunk); 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(), 0); - depth++; - } else if(noise < airThreshold) { - chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), 0); - } else { - depth = 0; - } - } - } + protected void applyStructureBeard(StructureManager structureAccessor, ChunkAccess chunk) { + LevelAccessor level = Reflection.STRUCTURE_MANAGER.getLevel(structureAccessor); + BiomeProvider biomeProvider = pack.getBiomeProvider(); + PreLoadCompatibilityOptions compatibilityOptions = pack.getContext().get(PreLoadCompatibilityOptions.class); + if(compatibilityOptions.isBeard()) { + bindings.applyStructureBeard(delegate, structureAccessor, chunk, + new com.dfsek.terra.bukkit.world.BukkitWorldProperties(level.getMinecraftWorld().getWorld()), + biomeProvider, compatibilityOptions); } } @@ -143,7 +116,7 @@ public int getBaseHeight(int x, int z, @NotNull Types heightmap, @NotNull LevelH WorldProperties properties = new NMSWorldProperties(seed, world); int y = properties.getMaxHeight(); BiomeProvider biomeProvider = pack.getBiomeProvider(); - while(y >= getMinY() && !heightmap.isOpaque().test( + while(y > properties.getMinHeight() && !heightmap.isOpaque().test( ((CraftBlockData) delegate.getBlock(properties, x, y - 1, z, biomeProvider).getHandle()).getState())) { y--; } @@ -155,11 +128,11 @@ public int getBaseHeight(int x, int z, @NotNull Types heightmap, @NotNull LevelH BlockState[] array = new BlockState[world.getHeight()]; WorldProperties properties = new NMSWorldProperties(seed, world); BiomeProvider biomeProvider = pack.getBiomeProvider(); - for(int y = properties.getMaxHeight(); y >= properties.getMinHeight(); y--) { + for(int y = properties.getMaxHeight() - 1; y >= properties.getMinHeight(); y--) { array[y - properties.getMinHeight()] = ((CraftBlockData) delegate.getBlock(properties, x, y, z, biomeProvider) .getHandle()).getState(); } - return new NoiseColumn(getMinY(), array); + return new NoiseColumn(properties.getMinHeight(), array); } @Override diff --git a/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java new file mode 100644 index 0000000000..e5dd57916c --- /dev/null +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java @@ -0,0 +1,66 @@ +package com.dfsek.terra.bukkit.nms; + +import net.minecraft.server.level.ChunkMap; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.status.WorldGenContext; +import org.bukkit.World; +import org.bukkit.craftbukkit.CraftWorld; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldInitEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.locks.ReentrantLock; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.bukkit.generator.BukkitChunkGeneratorWrapper; + + +public class NMSInjectListener implements Listener { + private static final Logger LOGGER = LoggerFactory.getLogger(NMSInjectListener.class); + private static final Set INJECTED = new HashSet<>(); + private static final ReentrantLock INJECT_LOCK = new ReentrantLock(); + private final NMSVersionBindings bindings; + + public NMSInjectListener(NMSVersionBindings bindings) { + this.bindings = bindings; + } + + @EventHandler + public void onWorldInit(WorldInitEvent event) { + if(!INJECTED.contains(event.getWorld()) && + event.getWorld().getGenerator() instanceof BukkitChunkGeneratorWrapper bukkitChunkGeneratorWrapper) { + INJECT_LOCK.lock(); + try { + LOGGER.info("Preparing to take over the world: {}", event.getWorld().getName()); + CraftWorld craftWorld = (CraftWorld) event.getWorld(); + ServerLevel serverWorld = craftWorld.getHandle(); + + ConfigPack pack = bukkitChunkGeneratorWrapper.getPack(); + + ChunkGenerator vanilla = serverWorld.getChunkSource().getGenerator(); + NMSBiomeProvider provider = bindings.createBiomeProvider(pack.getBiomeProvider(), craftWorld.getSeed()); + ChunkMap chunkMap = serverWorld.getChunkSource().chunkMap; + WorldGenContext worldGenContext = Reflection.CHUNKMAP.getWorldGenContext(chunkMap); + Reflection.CHUNKMAP.setWorldGenContext(chunkMap, new WorldGenContext( + worldGenContext.level(), + bindings.createChunkGenerator(vanilla, pack, provider, craftWorld.getSeed()), + worldGenContext.structureManager(), + worldGenContext.lightEngine(), + worldGenContext.mainThreadExecutor(), + worldGenContext.unsavedListener() + )); + + INJECTED.add(event.getWorld()); + LOGGER.info("Successfully injected into world."); + + } finally { + INJECT_LOCK.unlock(); + } + } + } +} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSPlatform.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSPlatformBase.java similarity index 71% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSPlatform.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSPlatformBase.java index efbdde212d..150071f09d 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSPlatform.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSPlatformBase.java @@ -2,31 +2,34 @@ import com.dfsek.tectonic.api.TypeRegistry; import com.dfsek.tectonic.api.exception.LoadException; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.sounds.Music; import net.minecraft.sounds.SoundEvent; +import net.minecraft.world.attribute.AmbientAdditionsSettings; +import net.minecraft.world.attribute.AmbientMoodSettings; +import net.minecraft.world.attribute.AmbientParticle; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.entity.npc.VillagerType; -import net.minecraft.world.level.biome.AmbientAdditionsSettings; -import net.minecraft.world.level.biome.AmbientMoodSettings; -import net.minecraft.world.level.biome.AmbientParticleSettings; +import net.minecraft.world.entity.npc.villager.VillagerType; import net.minecraft.world.level.biome.Biome.Precipitation; import net.minecraft.world.level.biome.Biome.TemperatureModifier; import net.minecraft.world.level.biome.BiomeSpecialEffects.GrassColorModifier; import net.minecraft.world.level.biome.MobSpawnSettings; import org.bukkit.Bukkit; +import org.bukkit.block.CreatureSpawner; import java.util.List; import java.util.Locale; import com.dfsek.terra.addon.InternalAddon; import com.dfsek.terra.api.addon.BaseAddon; +import com.dfsek.terra.api.entity.EntityTypeExtended; import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent; import com.dfsek.terra.api.event.functional.FunctionalEventHandler; import com.dfsek.terra.api.world.biome.PlatformBiome; import com.dfsek.terra.bukkit.PlatformImpl; import com.dfsek.terra.bukkit.TerraBukkitPlugin; +import com.dfsek.terra.bukkit.util.BukkitNMSAccess; import com.dfsek.terra.bukkit.nms.config.BiomeAdditionsSoundTemplate; import com.dfsek.terra.bukkit.nms.config.BiomeMoodSoundTemplate; import com.dfsek.terra.bukkit.nms.config.BiomeParticleConfigTemplate; @@ -40,20 +43,38 @@ import com.dfsek.terra.bukkit.nms.config.VillagerTypeTemplate; -public class NMSPlatform extends PlatformImpl { +public class NMSPlatformBase extends PlatformImpl implements BukkitNMSAccess { + private final NMSVersionBindings bindings; - public NMSPlatform(TerraBukkitPlugin plugin) { + public NMSPlatformBase(TerraBukkitPlugin plugin, NMSVersionBindings bindings) { super(plugin); + this.bindings = bindings; - Bukkit.getPluginManager().registerEvents(new NMSInjectListener(), plugin); + Bukkit.getPluginManager().registerEvents(new NMSInjectListener(bindings), plugin); + } + + @Override + public com.dfsek.terra.api.entity.EntityType getEntityType(String data) { + return AwfulBukkitHacks.getEntityType(data); + } + + @Override + public org.bukkit.entity.Entity spawnEntity(Object target, org.bukkit.Location location, + com.dfsek.terra.api.entity.EntityType type) { + return AwfulBukkitHacks.spawnEntity(target, location, type); + } + + @Override + public void setSpawnerEntity(CreatureSpawner spawner, EntityTypeExtended type) { + AwfulBukkitHacks.setSpawnerEntity(spawner, type); } @Override public void register(TypeRegistry registry) { super.register(registry); registry.registerLoader(PlatformBiome.class, (type, o, loader, depthTracker) -> parseBiome((String) o, depthTracker)) - .registerLoader(ResourceLocation.class, (type, o, loader, depthTracker) -> { - ResourceLocation identifier = ResourceLocation.tryParse((String) o); + .registerLoader(Identifier.class, (type, o, loader, depthTracker) -> { + Identifier identifier = Identifier.tryParse((String) o); if(identifier == null) throw new LoadException("Invalid identifier: " + o, depthTracker); return identifier; @@ -67,7 +88,7 @@ public void register(TypeRegistry registry) { (type, o, loader, depthTracker) -> TemperatureModifier.valueOf(((String) o).toUpperCase( Locale.ROOT))) .registerLoader(MobCategory.class, (type, o, loader, depthTracker) -> MobCategory.valueOf((String) o)) - .registerLoader(AmbientParticleSettings.class, BiomeParticleConfigTemplate::new) + .registerLoader(AmbientParticle.class, BiomeParticleConfigTemplate::new) .registerLoader(SoundEvent.class, SoundEventTemplate::new) .registerLoader(AmbientMoodSettings.class, BiomeMoodSoundTemplate::new) .registerLoader(AmbientAdditionsSettings.class, BiomeAdditionsSoundTemplate::new) @@ -76,7 +97,7 @@ public void register(TypeRegistry registry) { .registerLoader(SpawnCostConfig.class, SpawnCostConfig::new) .registerLoader(SpawnEntryConfig.class, SpawnEntryConfig::new) .registerLoader(SpawnTypeConfig.class, SpawnTypeConfig::new) - .registerLoader(MobSpawnSettings.class, SpawnSettingsTemplate::new) + .registerLoader(MobSpawnSettings.class, () -> new SpawnSettingsTemplate(bindings)) .registerLoader(VillagerType.class, VillagerTypeTemplate::new); } @@ -87,7 +108,7 @@ protected InternalAddon load() { this.getEventManager().getHandler(FunctionalEventHandler.class) .register(internalAddon, PlatformInitializationEvent.class) .priority(1) - .then(event -> AwfulBukkitHacks.registerBiomes(this.getRawConfigRegistry())) + .then(event -> AwfulBukkitHacks.registerBiomes(this.getRawConfigRegistry(), bindings)) .global(); return internalAddon; diff --git a/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSStructureBeardifier.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSStructureBeardifier.java new file mode 100644 index 0000000000..a27b973375 --- /dev/null +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSStructureBeardifier.java @@ -0,0 +1,52 @@ +package com.dfsek.terra.bukkit.nms; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.chunk.ChunkAccess; +import org.bukkit.craftbukkit.block.data.CraftBlockData; + +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; +import com.dfsek.terra.api.world.info.WorldProperties; +import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState; + + +public final class NMSStructureBeardifier { + private NMSStructureBeardifier() { } + + public static void apply(ChunkGenerator delegate, ChunkAccess chunk, WorldProperties world, + BiomeProvider biomeProvider, PreLoadCompatibilityOptions compatibilityOptions, + StructureDensitySampler sampler) { + double threshold = compatibilityOptions.getBeardThreshold(); + double airThreshold = compatibilityOptions.getAirThreshold(); + int chunkX = chunk.getPos().x() << 4; + int chunkZ = 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() - 1; y >= world.getMinHeight(); y--) { + int worldX = x + chunkX; + int worldZ = z + chunkZ; + double density = sampler.sample(worldX, y, worldZ); + if(density > threshold) { + chunk.setBlockState(new BlockPos(x, y, z), ((CraftBlockData) ((BukkitBlockState) delegate + .getPalette(worldX, y, worldZ, world, biomeProvider) + .get(depth, worldX, y, worldZ, world.getSeed())).getHandle()).getState(), 0); + depth++; + } else if(density < airThreshold) { + chunk.setBlockState(new BlockPos(x, y, z), Blocks.AIR.defaultBlockState(), 0); + } else { + depth = 0; + } + } + } + } + } + + @FunctionalInterface + public interface StructureDensitySampler { + double sample(int x, int y, int z); + } +} diff --git a/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSVersionBindings.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSVersionBindings.java new file mode 100644 index 0000000000..6b590e70df --- /dev/null +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSVersionBindings.java @@ -0,0 +1,44 @@ +package com.dfsek.terra.bukkit.nms; + +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.StructureManager; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.Biome.BiomeBuilder; +import net.minecraft.world.level.biome.MobSpawnSettings; +import net.minecraft.world.level.chunk.ChunkAccess; + +import com.dfsek.terra.api.config.ConfigPack; +import com.dfsek.terra.api.world.biome.generation.BiomeProvider; +import com.dfsek.terra.api.world.chunk.generation.ChunkGenerator; +import com.dfsek.terra.api.world.info.WorldProperties; +import com.dfsek.terra.bukkit.config.PreLoadCompatibilityOptions; +import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties; + + +public interface NMSVersionBindings { + default NMSBiomeProvider createBiomeProvider(BiomeProvider delegate, long seed) { + return new NMSBiomeProvider(delegate, seed); + } + + default NMSChunkGeneratorDelegate createChunkGenerator(net.minecraft.world.level.chunk.ChunkGenerator vanilla, + ConfigPack pack, + NMSBiomeProvider biomeProvider, long seed) { + return new NMSChunkGeneratorDelegate(vanilla, pack, biomeProvider, seed, this); + } + + void addSpawn(MobSpawnSettings.Builder builder, MobCategory category, EntityType type, int weight, int minCount, int maxCount); + + void addSpawnCost(MobSpawnSettings.Builder builder, EntityType type, double mass, double gravity); + + boolean setCreatureGenerationProbability(MobSpawnSettings.Builder builder, float probability); + + void copyVanillaSpawnSettings(BiomeBuilder builder, Biome biome); + + void setSpawnSettings(BiomeBuilder builder, MobSpawnSettings settings); + + void applyBiomeColorOverrides(BiomeBuilder builder, VanillaBiomeProperties properties); + + void applyStructureBeard(ChunkGenerator generator, StructureManager structures, ChunkAccess chunk, WorldProperties world, + BiomeProvider biomeProvider, PreLoadCompatibilityOptions options); +} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSWorldProperties.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSWorldProperties.java similarity index 100% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSWorldProperties.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/NMSWorldProperties.java diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/Reflection.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/Reflection.java similarity index 98% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/Reflection.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/Reflection.java index 3c0e93e67a..32d6c91328 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/Reflection.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/Reflection.java @@ -7,7 +7,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ChunkMap; import net.minecraft.tags.TagKey; -import net.minecraft.world.entity.npc.VillagerType; +import net.minecraft.world.entity.npc.villager.VillagerType; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.biome.Biome; @@ -24,7 +24,6 @@ import java.util.List; import java.util.Map; - public class Reflection { public static final MappedRegistryProxy MAPPED_REGISTRY; public static final MappedRegistryTagSetProxy MAPPED_REGISTRY_TAG_SET; diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/RegistryFetcher.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/RegistryFetcher.java similarity index 100% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/RegistryFetcher.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/RegistryFetcher.java diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeAdditionsSoundTemplate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeAdditionsSoundTemplate.java similarity index 93% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeAdditionsSoundTemplate.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeAdditionsSoundTemplate.java index 83ac758b2d..3e3286098f 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeAdditionsSoundTemplate.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeAdditionsSoundTemplate.java @@ -5,7 +5,7 @@ import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.level.biome.AmbientAdditionsSettings; +import net.minecraft.world.attribute.AmbientAdditionsSettings; public class BiomeAdditionsSoundTemplate implements ObjectTemplate { diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeMoodSoundTemplate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeMoodSoundTemplate.java similarity index 95% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeMoodSoundTemplate.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeMoodSoundTemplate.java index 20f982920f..b7b774ae3c 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeMoodSoundTemplate.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeMoodSoundTemplate.java @@ -5,7 +5,7 @@ import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.level.biome.AmbientMoodSettings; +import net.minecraft.world.attribute.AmbientMoodSettings; public class BiomeMoodSoundTemplate implements ObjectTemplate { diff --git a/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeParticleConfigTemplate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeParticleConfigTemplate.java new file mode 100644 index 0000000000..22a7540555 --- /dev/null +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeParticleConfigTemplate.java @@ -0,0 +1,71 @@ +package com.dfsek.terra.bukkit.nms.config; + +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.commands.arguments.ParticleArgument; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.attribute.AmbientParticle; + +import java.util.stream.Stream; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class BiomeParticleConfigTemplate implements ObjectTemplate { + private static final Pattern LEGACY_BLOCK_PARTICLE = Pattern.compile( + "^((?:[a-z0-9_.-]+:)?[a-z0-9_./-]+)\\{block_state:\\{Name:((?:[a-z0-9_.-]+:)?[a-z0-9_./-]+)\\}\\}$" + ); + + @Value("particle") + @Default + private String particle = null; + + @Value("probability") + @Default + private Float probability = 0.1f; + + @Override + public AmbientParticle get() { + if(particle == null) { + return null; + } + + try { + return new AmbientParticle(parseParticle(particle), probability); + } catch(CommandSyntaxException e) { + String normalized = normalizeLegacyBlockParticle(particle); + if(normalized == null) { + throw new RuntimeException(e); + } + + try { + return new AmbientParticle(parseParticle(normalized), probability); + } catch(CommandSyntaxException fallbackException) { + fallbackException.addSuppressed(e); + throw new RuntimeException(fallbackException); + } + } + } + + private static net.minecraft.core.particles.ParticleOptions parseParticle(String value) throws CommandSyntaxException { + return ParticleArgument.readParticle(new StringReader(value), + HolderLookup.Provider.create(Stream.of(BuiltInRegistries.PARTICLE_TYPE))); + } + + private static String normalizeLegacyBlockParticle(String value) { + Matcher matcher = LEGACY_BLOCK_PARTICLE.matcher(value); + if(!matcher.matches()) { + return null; + } + + String block = matcher.group(2); + if(!block.contains(":")) { + block = "minecraft:" + block; + } + return matcher.group(1) + "{block_state:\"" + block + "\"}"; + } +} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/EntityTypeTemplate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/EntityTypeTemplate.java similarity index 86% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/EntityTypeTemplate.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/EntityTypeTemplate.java index 719791e144..2e171138d6 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/EntityTypeTemplate.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/EntityTypeTemplate.java @@ -4,14 +4,14 @@ import com.dfsek.tectonic.api.config.template.annotations.Value; import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.world.entity.EntityType; public class EntityTypeTemplate implements ObjectTemplate> { @Value("id") @Default - private ResourceLocation id = null; + private Identifier id = null; @Override public EntityType get() { diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/MusicSoundTemplate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/MusicSoundTemplate.java similarity index 100% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/MusicSoundTemplate.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/MusicSoundTemplate.java diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SoundEventTemplate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SoundEventTemplate.java similarity index 89% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SoundEventTemplate.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SoundEventTemplate.java index 2535823611..4d7b7bddd1 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SoundEventTemplate.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SoundEventTemplate.java @@ -3,14 +3,14 @@ import com.dfsek.tectonic.api.config.template.annotations.Default; import com.dfsek.tectonic.api.config.template.annotations.Value; import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; -import net.minecraft.resources.ResourceLocation; +import net.minecraft.resources.Identifier; import net.minecraft.sounds.SoundEvent; public class SoundEventTemplate implements ObjectTemplate { @Value("id") @Default - private ResourceLocation id = null; + private Identifier id = null; @Value("distance-to-travel") @Default diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnCostConfig.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnCostConfig.java similarity index 100% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnCostConfig.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnCostConfig.java diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnEntryConfig.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnEntryConfig.java similarity index 79% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnEntryConfig.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnEntryConfig.java index 60a7637693..e5dff21454 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnEntryConfig.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnEntryConfig.java @@ -4,7 +4,6 @@ import com.dfsek.tectonic.api.config.template.annotations.Value; import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; import net.minecraft.world.entity.EntityType; -import net.minecraft.world.level.biome.MobSpawnSettings.SpawnerData; import com.dfsek.terra.api.util.range.Range; @@ -26,8 +25,16 @@ public Integer getWeight() { return weight; } - public SpawnerData getSpawnEntry() { - return new SpawnerData(type, groupSize.getMin(), groupSize.getMax()); + public EntityType getType() { + return type; + } + + public int getMinCount() { + return groupSize.getMin(); + } + + public int getMaxCount() { + return groupSize.getMax(); } @Override diff --git a/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnSettingsTemplate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnSettingsTemplate.java new file mode 100644 index 0000000000..2e35dad379 --- /dev/null +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnSettingsTemplate.java @@ -0,0 +1,58 @@ +package com.dfsek.terra.bukkit.nms.config; + +import com.dfsek.tectonic.api.config.template.annotations.Default; +import com.dfsek.tectonic.api.config.template.annotations.Value; +import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.level.biome.MobSpawnSettings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.dfsek.terra.bukkit.nms.NMSVersionBindings; + + +public class SpawnSettingsTemplate implements ObjectTemplate { + private static final Logger LOGGER = LoggerFactory.getLogger(SpawnSettingsTemplate.class); + private static final AtomicBoolean PROBABILITY_WARNING_LOGGED = new AtomicBoolean(); + + private final NMSVersionBindings bindings; + + @Value("spawns") + @Default + private List spawns = List.of(); + + @Value("costs") + @Default + private List costs = List.of(); + + @Value("probability") + @Default + private Float probability = null; + + public SpawnSettingsTemplate(NMSVersionBindings bindings) { + this.bindings = bindings; + } + + @Override + public MobSpawnSettings get() { + MobSpawnSettings.Builder builder = new MobSpawnSettings.Builder(); + for(SpawnTypeConfig spawn : spawns) { + MobCategory category = spawn.getGroup(); + for(SpawnEntryConfig entry : spawn.getEntries()) { + bindings.addSpawn(builder, category, entry.getType(), entry.getWeight(), entry.getMinCount(), entry.getMaxCount()); + } + } + for(SpawnCostConfig cost : costs) { + bindings.addSpawnCost(builder, cost.getType(), cost.getMass(), cost.getGravity()); + } + if(probability != null && !bindings.setCreatureGenerationProbability(builder, probability) && + PROBABILITY_WARNING_LOGGED.compareAndSet(false, true)) { + LOGGER.warn("This server version ignores biome creature-generation probability values."); + } + + return builder.build(); + } +} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnTypeConfig.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnTypeConfig.java similarity index 100% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnTypeConfig.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnTypeConfig.java diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/VanillaBiomeProperties.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/VanillaBiomeProperties.java similarity index 91% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/VanillaBiomeProperties.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/VanillaBiomeProperties.java index 3cd1e992bb..cb74f635d9 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/VanillaBiomeProperties.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/VanillaBiomeProperties.java @@ -6,10 +6,10 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.sounds.Music; import net.minecraft.sounds.SoundEvent; -import net.minecraft.world.entity.npc.VillagerType; -import net.minecraft.world.level.biome.AmbientAdditionsSettings; -import net.minecraft.world.level.biome.AmbientMoodSettings; -import net.minecraft.world.level.biome.AmbientParticleSettings; +import net.minecraft.world.attribute.AmbientAdditionsSettings; +import net.minecraft.world.attribute.AmbientMoodSettings; +import net.minecraft.world.attribute.AmbientParticle; +import net.minecraft.world.entity.npc.villager.VillagerType; import net.minecraft.world.level.biome.Biome.TemperatureModifier; import net.minecraft.world.level.biome.BiomeSpecialEffects.GrassColorModifier; import net.minecraft.world.level.biome.MobSpawnSettings; @@ -52,7 +52,7 @@ public class VanillaBiomeProperties implements ConfigTemplate, Properties { @Value("particles") @Default - private AmbientParticleSettings particleConfig = null; + private AmbientParticle particleConfig = null; @Value("climate.precipitation") @Default @@ -130,7 +130,7 @@ public GrassColorModifier getGrassColorModifier() { return grassColorModifier; } - public AmbientParticleSettings getParticleConfig() { + public AmbientParticle getParticleConfig() { return particleConfig; } diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/VillagerTypeTemplate.java b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/VillagerTypeTemplate.java similarity index 81% rename from platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/VillagerTypeTemplate.java rename to platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/VillagerTypeTemplate.java index c254628b8f..209ce3da7a 100644 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/VillagerTypeTemplate.java +++ b/platforms/bukkit/nms/common/src/main/java/com/dfsek/terra/bukkit/nms/config/VillagerTypeTemplate.java @@ -4,15 +4,15 @@ import com.dfsek.tectonic.api.config.template.annotations.Value; import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.npc.VillagerType; +import net.minecraft.world.entity.npc.villager.VillagerType; public class VillagerTypeTemplate implements ObjectTemplate> { @Value("id") @Default - private ResourceLocation id = null; + private Identifier id = null; @Override public ResourceKey get() { diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java b/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java deleted file mode 100644 index fea134c9ef..0000000000 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSBiomeInjector.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.dfsek.terra.bukkit.nms; - -import net.minecraft.core.Holder; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeGenerationSettings; -import net.minecraft.world.level.biome.BiomeSpecialEffects; - -import java.util.Locale; -import java.util.Objects; -import java.util.Optional; - -import com.dfsek.terra.api.config.ConfigPack; -import com.dfsek.terra.bukkit.nms.config.VanillaBiomeProperties; - - -public class NMSBiomeInjector { - - public static Optional> getEntry(Registry registry, ResourceLocation identifier) { - return registry.getOptional(identifier) - .flatMap(registry::getResourceKey) - .flatMap(registry::get); - } - - public static Biome createBiome(Biome vanilla, VanillaBiomeProperties vanillaBiomeProperties) - throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { - Biome.BiomeBuilder builder = new Biome.BiomeBuilder(); - - BiomeSpecialEffects.Builder effects = new BiomeSpecialEffects.Builder(); - - effects.fogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getFogColor(), vanilla.getFogColor())) - .waterColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterColor(), vanilla.getWaterColor())) - .waterFogColor(Objects.requireNonNullElse(vanillaBiomeProperties.getWaterFogColor(), vanilla.getWaterFogColor())) - .skyColor(Objects.requireNonNullElse(vanillaBiomeProperties.getSkyColor(), vanilla.getSkyColor())) - .grassColorModifier(Objects.requireNonNullElse(vanillaBiomeProperties.getGrassColorModifier(), - vanilla.getSpecialEffects().getGrassColorModifier())) - .backgroundMusicVolume(Objects.requireNonNullElse(vanillaBiomeProperties.getMusicVolume(), vanilla.getBackgroundMusicVolume())); - - if(vanillaBiomeProperties.getGrassColor() == null) { - vanilla.getSpecialEffects().getGrassColorOverride().ifPresent(effects::grassColorOverride); - } else { - effects.grassColorOverride(vanillaBiomeProperties.getGrassColor()); - } - - if(vanillaBiomeProperties.getFoliageColor() == null) { - vanilla.getSpecialEffects().getFoliageColorOverride().ifPresent(effects::foliageColorOverride); - } else { - effects.foliageColorOverride(vanillaBiomeProperties.getFoliageColor()); - } - - if(vanillaBiomeProperties.getParticleConfig() == null) { - vanilla.getSpecialEffects().getAmbientParticleSettings().ifPresent(effects::ambientParticle); - } else { - effects.ambientParticle(vanillaBiomeProperties.getParticleConfig()); - } - - if(vanillaBiomeProperties.getLoopSound() == null) { - vanilla.getSpecialEffects().getAmbientLoopSoundEvent().ifPresent(effects::ambientLoopSound); - } else { - RegistryFetcher.soundEventRegistry().get(vanillaBiomeProperties.getLoopSound().location()).ifPresent(effects::ambientLoopSound); - } - - if(vanillaBiomeProperties.getMoodSound() == null) { - vanilla.getSpecialEffects().getAmbientMoodSettings().ifPresent(effects::ambientMoodSound); - } else { - effects.ambientMoodSound(vanillaBiomeProperties.getMoodSound()); - } - - if(vanillaBiomeProperties.getAdditionsSound() == null) { - vanilla.getSpecialEffects().getAmbientAdditionsSettings().ifPresent(effects::ambientAdditionsSound); - } else { - effects.ambientAdditionsSound(vanillaBiomeProperties.getAdditionsSound()); - } - - if(vanillaBiomeProperties.getMusic() == null) { - vanilla.getSpecialEffects().getBackgroundMusic().ifPresent(effects::backgroundMusic); - } else { - effects.backgroundMusic(vanillaBiomeProperties.getMusic()); - } - - builder.hasPrecipitation(Objects.requireNonNullElse(vanillaBiomeProperties.getPrecipitation(), vanilla.hasPrecipitation())); - - builder.temperature(Objects.requireNonNullElse(vanillaBiomeProperties.getTemperature(), vanilla.getBaseTemperature())); - - builder.downfall(Objects.requireNonNullElse(vanillaBiomeProperties.getDownfall(), vanilla.climateSettings.downfall())); - - builder.temperatureAdjustment( - Objects.requireNonNullElse(vanillaBiomeProperties.getTemperatureModifier(), vanilla.climateSettings.temperatureModifier())); - - builder.mobSpawnSettings(Objects.requireNonNullElse(vanillaBiomeProperties.getSpawnSettings(), vanilla.getMobSettings())); - - return builder - .specialEffects(effects.build()) - .generationSettings(new BiomeGenerationSettings.PlainBuilder().build()) - .build(); - } - - public static String createBiomeID(ConfigPack pack, com.dfsek.terra.api.registry.key.RegistryKey biomeID) { - return pack.getID() - .toLowerCase() + "/" + biomeID.getNamespace().toLowerCase(Locale.ROOT) + "/" + biomeID.getID().toLowerCase(Locale.ROOT); - } -} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java b/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java deleted file mode 100644 index 4a73dc77bd..0000000000 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/NMSInjectListener.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.dfsek.terra.bukkit.nms; - -import net.minecraft.server.level.ChunkMap; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.status.WorldGenContext; -import org.bukkit.World; -import org.bukkit.craftbukkit.CraftWorld; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.world.WorldInitEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.locks.ReentrantLock; - -import com.dfsek.terra.api.config.ConfigPack; -import com.dfsek.terra.bukkit.generator.BukkitChunkGeneratorWrapper; - - -public class NMSInjectListener implements Listener { - private static final Logger LOGGER = LoggerFactory.getLogger(NMSInjectListener.class); - private static final Set INJECTED = new HashSet<>(); - private static final ReentrantLock INJECT_LOCK = new ReentrantLock(); - - @EventHandler - public void onWorldInit(WorldInitEvent event) { - if(!INJECTED.contains(event.getWorld()) && - event.getWorld().getGenerator() instanceof BukkitChunkGeneratorWrapper bukkitChunkGeneratorWrapper) { - INJECT_LOCK.lock(); - INJECTED.add(event.getWorld()); - LOGGER.info("Preparing to take over the world: {}", event.getWorld().getName()); - CraftWorld craftWorld = (CraftWorld) event.getWorld(); - ServerLevel serverWorld = craftWorld.getHandle(); - - ConfigPack pack = bukkitChunkGeneratorWrapper.getPack(); - - ChunkGenerator vanilla = serverWorld.getChunkSource().getGenerator(); - NMSBiomeProvider provider = new NMSBiomeProvider(pack.getBiomeProvider(), craftWorld.getSeed()); - ChunkMap chunkMap = serverWorld.getChunkSource().chunkMap; - WorldGenContext worldGenContext = Reflection.CHUNKMAP.getWorldGenContext(chunkMap); - Reflection.CHUNKMAP.setWorldGenContext(chunkMap, new WorldGenContext( - worldGenContext.level(), - new NMSChunkGeneratorDelegate(vanilla, pack, provider, craftWorld.getSeed()), - worldGenContext.structureManager(), - worldGenContext.lightEngine(), - worldGenContext.mainThreadExecutor(), - worldGenContext.unsavedListener() - )); - - LOGGER.info("Successfully injected into world."); - - INJECT_LOCK.unlock(); - } - } -} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeParticleConfigTemplate.java b/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeParticleConfigTemplate.java deleted file mode 100644 index 99fe0cda39..0000000000 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/BiomeParticleConfigTemplate.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.dfsek.terra.bukkit.nms.config; - -import com.dfsek.tectonic.api.config.template.annotations.Default; -import com.dfsek.tectonic.api.config.template.annotations.Value; -import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; -import com.mojang.brigadier.StringReader; -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import net.minecraft.commands.arguments.ParticleArgument; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.world.level.biome.AmbientParticleSettings; - -import java.util.stream.Stream; - - -public class BiomeParticleConfigTemplate implements ObjectTemplate { - @Value("particle") - @Default - private String particle = null; - - @Value("probability") - @Default - private Float probability = 0.1f; - - @Override - public AmbientParticleSettings get() { - if(particle == null) { - return null; - } - - try { - return new AmbientParticleSettings(ParticleArgument.readParticle(new StringReader(particle), - HolderLookup.Provider.create(Stream.of(BuiltInRegistries.PARTICLE_TYPE))), probability); - } catch(CommandSyntaxException e) { - throw new RuntimeException(e); - } - } -} diff --git a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnSettingsTemplate.java b/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnSettingsTemplate.java deleted file mode 100644 index 7950f33890..0000000000 --- a/platforms/bukkit/nms/src/main/java/com/dfsek/terra/bukkit/nms/config/SpawnSettingsTemplate.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.dfsek.terra.bukkit.nms.config; - -import com.dfsek.tectonic.api.config.template.annotations.Default; -import com.dfsek.tectonic.api.config.template.annotations.Value; -import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.level.biome.MobSpawnSettings; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - - -public class SpawnSettingsTemplate implements ObjectTemplate { - - private static final Logger logger = LoggerFactory.getLogger(SpawnTypeConfig.class); - private static boolean used = false; - - @Value("spawns") - @Default - private List spawns = null; - - @Value("costs") - @Default - private List costs = null; - - @Value("probability") - @Default - private Float probability = null; - - @Override - public MobSpawnSettings get() { - MobSpawnSettings.Builder builder = new MobSpawnSettings.Builder(); - for(SpawnTypeConfig spawn : spawns) { - MobCategory group = spawn.getGroup(); - for(SpawnEntryConfig entry : spawn.getEntries()) { - builder.addSpawn(group, entry.getWeight(), entry.getSpawnEntry()); - } - } - for(SpawnCostConfig cost : costs) { - builder.addMobCharge(cost.getType(), cost.getMass(), cost.getGravity()); - } - if(probability != null) { - builder.creatureGenerationProbability(probability); - } - - return builder.build(); - } -}