diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 9052869c2..74d4d4953 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -49,9 +49,11 @@ body: id: mcversion attributes: label: Minecraft Version - description: What version of Minecraft is the server on? Iris 4.x targets 26.2 only; older versions are not supported. + description: What version of Minecraft is the server on? Iris 4.x supports 26.1.2 to 26.3 on Bukkit-family servers and 26.3 on Fabric, Forge and NeoForge; older versions are not supported. options: + - '26.3' - '26.2' + - '26.1.2' - Other (unsupported) validations: required: true @@ -59,7 +61,7 @@ body: id: loaderversion attributes: label: Platform / Loader Version - description: Exact server or loader build (see console). For example "Paper 26.2-60", "Fabric Loader 0.19.3", "NeoForge 26.2.0.12-beta". + description: Exact server or loader build (see console). For example "Paper 26.3-133", "Fabric Loader 0.19.5", "NeoForge 26.3.0.33-beta". placeholder: DO NOT SAY "LATEST" validations: required: true diff --git a/README.md b/README.md index 77959c775..96180d806 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Iris is a world generation engine for Minecraft servers and mod loaders. It gene biomes, caves, structures, objects, and entities from editable JSON packs, with a full in-game studio authoring workflow. The same engine runs as a Bukkit-family plugin and as a Fabric, Forge, or NeoForge server mod. Cross-platform generation targets deterministic parity for identical -artifacts, pack bytes, seeds, and test areas. The master branch targets Minecraft 26.2. +artifacts, pack bytes, seeds, and test areas. The master branch targets Minecraft 26.3. # [Support](https://discord.gg/3xxPTpT) **|** [Documentation](https://github.com/VolmitSoftware/docs/blob/master/iris/00-overview.md) **|** [Git](https://github.com/IrisDimensions) @@ -20,12 +20,12 @@ Canonical English is defined in the typed Java catalogs under `core/src/main/jav | Platform | Artifact | Minecraft | Notes | |---|---|---|---| -| Paper / Purpur / Leaf / Canvas | plugin jar | 26.1.2 - 26.2 | Full feature set | -| Folia | plugin jar | 26.1.2 - 26.2 | Region-safe scheduling throughout | -| Spigot / CraftBukkit | plugin jar | 26.1.2 - 26.2 | Managed `iris:*` creation and generation; exact vanilla-slot `/iris replace` is unavailable | -| Fabric | mod jar | 26.2 | Server worldgen + client HUD; requires Fabric Loader 0.19.3+ | -| Forge | mod jar | 26.2 | Server worldgen + client HUD; current target is Forge 26.2-65.1.1 | -| NeoForge | mod jar | 26.2 | Server worldgen + client HUD; current target is NeoForge 26.2.0.59 | +| Paper / Purpur / Leaf / Canvas | plugin jar | 26.1.2 - 26.3 | Full feature set | +| Folia | plugin jar | 26.1.2 - 26.3 | Region-safe scheduling throughout | +| Spigot / CraftBukkit | plugin jar | 26.1.2 - 26.3 | Managed `iris:*` creation and generation; exact vanilla-slot `/iris replace` is unavailable | +| Fabric | mod jar | 26.3 | Server worldgen + client HUD; requires Fabric Loader 0.19.5+ | +| Forge | mod jar | 26.3 | Server worldgen + client HUD; current target is Forge 26.3-66.0.8 | +| NeoForge | mod jar | 26.3 | Server worldgen + client HUD; current target is NeoForge 26.3.0.33-beta | Java 25 is required on every platform. diff --git a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeCodecTest.java b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeCodecTest.java index a30e593d8..637cc6abc 100644 --- a/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeCodecTest.java +++ b/adapters/bukkit/nms/v26_3_R1/src/test/java/art/arcane/iris/platform/bukkit/nms/v26_3_R1/CustomBiomeCodecTest.java @@ -13,6 +13,7 @@ import net.minecraft.data.registries.VanillaRegistries; import net.minecraft.resources.RegistryOps; import net.minecraft.server.Bootstrap; import net.minecraft.world.attribute.EnvironmentAttributes; +import net.minecraft.world.attribute.modifier.MobSpawnSettingsModifier; import net.minecraft.world.entity.MobCategory; import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.MobSpawnSettings; @@ -53,5 +54,7 @@ public class CustomBiomeCodecTest { assertEquals(2, decoded.count().minInclusive()); assertEquals(4, decoded.count().maxInclusive()); assertEquals(0x102030, biome.getWaterColor() & 0xFFFFFF); + assertEquals(MobSpawnSettingsModifier.overlay(), + biome.getAttributes().get(EnvironmentAttributes.NATURAL_MOB_SPAWNS).modifier()); } } diff --git a/adapters/fabric/build.gradle b/adapters/fabric/build.gradle index 2a7d4c17d..3936168bd 100644 --- a/adapters/fabric/build.gradle +++ b/adapters/fabric/build.gradle @@ -60,8 +60,8 @@ apply from: file('../../gradle/modded-generation-build-revision.gradle') Properties rootProperties = new Properties() file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) } String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2')) -String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.2')) -String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse(rootProperties.getProperty('fabricLoaderVersion', '0.19.3')) +String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3')) +String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse(rootProperties.getProperty('fabricLoaderVersion', '0.19.5')) String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')) Closure irisArtifactName = { String platform, String targetVersion -> return "Iris v${project.version} [${platform}] ${targetVersion}.jar" diff --git a/adapters/fabric/src/main/resources/fabric.mod.json b/adapters/fabric/src/main/resources/fabric.mod.json index 37f76f980..05bafaebb 100644 --- a/adapters/fabric/src/main/resources/fabric.mod.json +++ b/adapters/fabric/src/main/resources/fabric.mod.json @@ -70,7 +70,7 @@ } ], "depends": { - "fabricloader": ">=0.19.3", + "fabricloader": ">=0.19.5", "minecraft": "~${minecraftVersion}", "java": ">=25" } diff --git a/adapters/fabric/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/fabric/NativeFabricSpawnPositionTest.java b/adapters/fabric/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/fabric/NativeFabricSpawnPositionTest.java new file mode 100644 index 000000000..482f0354a --- /dev/null +++ b/adapters/fabric/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/fabric/NativeFabricSpawnPositionTest.java @@ -0,0 +1,43 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.fabric; + +import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.ServerLevelAccessor; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.function.BooleanSupplier; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NativeFabricSpawnPositionTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void chunkGenerationSpawnsUseTheVanillaRulesAndObstructionChecks() { + NativeFabricLoader loader = new NativeFabricLoader( + new NativeLoaderOptions("irisworldgen", "treefeller", BooleanSupplier::getAsBoolean)); + ServerLevelAccessor level = mock(ServerLevelAccessor.class); + Mob mob = mock(Mob.class); + when(mob.checkSpawnRules(level, EntitySpawnReason.CHUNK_GENERATION)).thenReturn(true); + when(mob.checkSpawnObstruction(level)).thenReturn(true); + assertTrue(loader.checkSpawnPosition(mob, level, EntitySpawnReason.CHUNK_GENERATION)); + + when(mob.checkSpawnObstruction(level)).thenReturn(false); + assertFalse(loader.checkSpawnPosition(mob, level, EntitySpawnReason.CHUNK_GENERATION)); + + when(mob.checkSpawnObstruction(level)).thenReturn(true); + when(mob.checkSpawnRules(level, EntitySpawnReason.CHUNK_GENERATION)).thenReturn(false); + assertFalse(loader.checkSpawnPosition(mob, level, EntitySpawnReason.CHUNK_GENERATION)); + } +} diff --git a/adapters/forge/build.gradle b/adapters/forge/build.gradle index 5028ad734..8a61c81b3 100644 --- a/adapters/forge/build.gradle +++ b/adapters/forge/build.gradle @@ -42,7 +42,7 @@ buildscript { plugins { id 'java' - id 'net.minecraftforge.gradle' version '7.0.31' + id 'net.minecraftforge.gradle' version '7.0.40' alias(libs.plugins.shadow) } @@ -60,8 +60,8 @@ apply from: file('../../gradle/modded-generation-build-revision.gradle') Properties rootProperties = new Properties() file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) } String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2')) -String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.2')) -String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse(rootProperties.getProperty('forgeVersion', '26.2-65.1.1')) +String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3')) +String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse(rootProperties.getProperty('forgeVersion', '26.3-66.0.8')) String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')) Closure loaderDisplayVersion = { String loaderVersion -> String coordinatePrefix = "${minecraftVersion}-" @@ -272,7 +272,7 @@ tasks.named('shadowJar', ShadowJar).configure { } } manifest { - // FML 26.2 has no TOML mixin handling at all (ModFileParser reads neither [[mixins]] nor + // FML 26.3 has no TOML mixin handling at all (ModFileParser reads neither [[mixins]] nor // accessTransformers on Forge). This manifest attribute is the only registration mechanism; // do not move it into mods.toml. attributes('MixinConfigs': 'volmlib.entity.mixins.json,volmlib.client.mixins.json') diff --git a/adapters/forge/src/main/resources/META-INF/mods.toml b/adapters/forge/src/main/resources/META-INF/mods.toml index a55737f00..0c267e564 100644 --- a/adapters/forge/src/main/resources/META-INF/mods.toml +++ b/adapters/forge/src/main/resources/META-INF/mods.toml @@ -1,5 +1,5 @@ modLoader = "javafml" -loaderVersion = "[65,66)" +loaderVersion = "[66,67)" license = "GPL-3.0" issueTrackerURL = "https://github.com/VolmitSoftware/Iris/issues" @@ -16,7 +16,7 @@ logoFile = "assets/irisworldgen/icon.png" [[dependencies.irisworldgen]] modId = "forge" mandatory = true -versionRange = "[65,66)" +versionRange = "[66,67)" ordering = "NONE" side = "BOTH" diff --git a/adapters/forge/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/forge/NativeForgeSpawnPositionTest.java b/adapters/forge/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/forge/NativeForgeSpawnPositionTest.java new file mode 100644 index 000000000..13ccce2db --- /dev/null +++ b/adapters/forge/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/forge/NativeForgeSpawnPositionTest.java @@ -0,0 +1,52 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.forge; + +import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.ServerLevelAccessor; +import net.minecraftforge.common.util.Result; +import net.minecraftforge.event.entity.living.MobSpawnEvent; +import net.minecraftforge.eventbus.api.listener.EventListener; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BooleanSupplier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NativeForgeSpawnPositionTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void chunkGenerationSpawnsFireThePositionCheckEventAndHonorItsDenial() { + NativeForgeLoader loader = new NativeForgeLoader( + new NativeLoaderOptions("irisworldgen", "treefeller", BooleanSupplier::getAsBoolean)); + ServerLevelAccessor level = mock(ServerLevelAccessor.class); + Mob mob = mock(Mob.class); + when(mob.checkSpawnRules(level, EntitySpawnReason.CHUNK_GENERATION)).thenReturn(true); + when(mob.checkSpawnObstruction(level)).thenReturn(true); + AtomicReference observed = new AtomicReference<>(); + EventListener listener = MobSpawnEvent.PositionCheck.BUS.addListener((MobSpawnEvent.PositionCheck event) -> { + observed.set(event.getSpawnReason()); + event.setResult(Result.DENY); + }); + try { + assertFalse(loader.checkSpawnPosition(mob, level, EntitySpawnReason.CHUNK_GENERATION)); + } finally { + MobSpawnEvent.PositionCheck.BUS.removeListener(listener); + } + assertEquals(EntitySpawnReason.CHUNK_GENERATION, observed.get()); + assertTrue(loader.checkSpawnPosition(mob, level, EntitySpawnReason.CHUNK_GENERATION)); + } +} diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java index 1bc6e2250..33d77d171 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/IrisModdedChunkGenerator.java @@ -44,6 +44,7 @@ import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeFeatureBiomeSourc import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeDimensionRuntime; import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer; +import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedLoader; import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeGeneratorOwner; import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeGeneratorHandle; @@ -1011,6 +1012,11 @@ public final class IrisModdedChunkGenerator implements NativeGeneratorOwner, Nat return ModdedEngineBootstrap.currentServer(); } + @Override + public NativeModdedLoader loader() { + return ModdedEngineBootstrap.loader(); + } + @Override public Engine current() { return engine(); diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomePolicy.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomePolicy.java index cf6638152..83d69e3c3 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomePolicy.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedBiomePolicy.java @@ -72,6 +72,7 @@ final class ModdedBiomePolicy implements NativeModdedBiomePolicy { private volatile BiomeHolderTable visibleBiomeCache = new BiomeHolderTable<>(); private volatile BiomeHolderTable structureBiomeCache = new BiomeHolderTable<>(); private volatile BiomeHolderTable surfaceStructureBiomeCache = new BiomeHolderTable<>(); + private volatile BiomeHolderTable naturalStructureBiomeCache = new BiomeHolderTable<>(); private volatile RuntimeCallbacks runtime; private volatile Set possibleStructureBiomeKeys; private volatile BiomeKeySets biomeKeySets; @@ -94,6 +95,7 @@ final class ModdedBiomePolicy implements NativeModdedBiomePolicy { visibleBiomeCache = new BiomeHolderTable<>(); structureBiomeCache = new BiomeHolderTable<>(); surfaceStructureBiomeCache = new BiomeHolderTable<>(); + naturalStructureBiomeCache = new BiomeHolderTable<>(); warnedUnresolvedBiomeKeys.clear(); possibleStructureBiomeKeys = null; biomeKeySets = null; @@ -104,6 +106,7 @@ final class ModdedBiomePolicy implements NativeModdedBiomePolicy { visibleBiomeCache.evictRuntime(runtimeIdentity); structureBiomeCache.evictRuntime(runtimeIdentity); surfaceStructureBiomeCache.evictRuntime(runtimeIdentity); + naturalStructureBiomeCache.evictRuntime(runtimeIdentity); } /** @@ -791,11 +794,40 @@ final class ModdedBiomePolicy implements NativeModdedBiomePolicy { if (!isReady(engine)) { throw new IllegalStateException("Iris structure biome lookup has no active engine runtime"); } - return getNoiseBiome(engine, quartX, quartY, quartZ, sampler); + return getNaturalStructureBiome(engine, quartX, quartZ); } } } + // The structure state only samples biomes for the concentric-ring search, which reaches ~24k blocks out for + // strongholds. The natural surface answers there without planning hydrology tiles, the same as the Bukkit ring search. + private H getNaturalStructureBiome(Engine engine, int quartX, int quartZ) { + long key = packColumnKey(quartX, quartZ); + int runtimeIdentity = engine.getCacheID(); + BiomeHolderTable cache = naturalStructureBiomeCache; + H cached = cache.get(runtimeIdentity, key); + if (cached != null) { + return cached; + } + NativeBiomeSourceAccess.RegistryView registry = biomeRegistry(); + if (registry == null) { + throw new IllegalStateException("Iris structure biome lookup has no biome registry"); + } + int blockX = quartX << 2; + int blockZ = quartZ << 2; + IrisBiome irisBiome = engine.getComplex().getNaturalTrueBiomeStream().get(blockX, blockZ); + if (irisBiome == null) { + throw new IllegalStateException("Iris returned no natural structure biome at block " + blockX + "," + blockZ); + } + H resolved = resolveHolder(registry, irisBiome.getStructureDerivativeKey()); + if (resolved == null) { + throw new IllegalStateException("Iris natural structure biome derivative '" + + irisBiome.getStructureDerivativeKey() + "' is not registered at block " + blockX + "," + blockZ); + } + cache.put(runtimeIdentity, key, resolved); + return resolved; + } + static long biomeResolutionSeed(long worldSeed, int blockX, int blockY, int blockZ) { return worldSeed ^ ((long) blockX * 341873128712L) diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedForcedDatapack.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedForcedDatapack.java index e5555f39b..ff4d78ce0 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedForcedDatapack.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/ModdedForcedDatapack.java @@ -315,11 +315,11 @@ public final class ModdedForcedDatapack { throw new IllegalStateException("SHA-256 is unavailable", missing); } digest.update((HASH_SALT + '|' + ModdedEngineBootstrap.loader().platformName() - + '|' + DataVersion.V26_2.getPackFormat() + '\n').getBytes(StandardCharsets.UTF_8)); + + '|' + DataVersion.V26_3.getPackFormat() + '\n').getBytes(StandardCharsets.UTF_8)); String inputFingerprint = IrisDatapackCompiler.computeInputFingerprint( compilationPackRoots(), List.of(), - DataVersion.V26_2.get(), + DataVersion.V26_3.get(), false ); digest.update(inputFingerprint.getBytes(StandardCharsets.UTF_8)); @@ -328,7 +328,7 @@ public final class ModdedForcedDatapack { private static void writeStagedPack(Path stagingDirectory) throws IOException { Map> seenBiomes = new LinkedHashMap<>(); - IDataFixer fixer = DataVersion.V26_2.get(); + IDataFixer fixer = DataVersion.V26_3.get(); int packCount = 0; KList presetIds = new KList<>(); @@ -679,7 +679,7 @@ public final class ModdedForcedDatapack { } static void writePackMeta(Path packDirectory) throws IOException { - int packFormat = DataVersion.V26_2.getPackFormat(); + int packFormat = DataVersion.V26_3.getPackFormat(); String json = "{\n" + " \"pack\": {\n" + " \"description\": \"Iris world generation biomes and dimension types for installed packs.\",\n" diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckDimensionContract.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckDimensionContract.java index 3fcf5e4c0..1ee890883 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckDimensionContract.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/WorldCheckDimensionContract.java @@ -55,7 +55,7 @@ final class WorldCheckDimensionContract { } static Dimension expectedDimensionContract(IrisDimension dimension) { - JSONObject json = new JSONObject(dimension.getDimensionType().toJson(DataVersion.V26_2.get())); + JSONObject json = new JSONObject(dimension.getDimensionType().toJson(DataVersion.V26_3.get())); return new Dimension( json.getInt("min_y"), json.getInt("height"), diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDatapackCommands.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDatapackCommands.java index ddb84d22f..f58722f51 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDatapackCommands.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedDatapackCommands.java @@ -165,7 +165,7 @@ public final class ModdedDatapackCommands { IrisDimension dimension = engine.getDimension(); String json; try { - json = dimension.getDimensionType().toJson(DataVersion.V26_2.get()); + json = dimension.getDimensionType().toJson(DataVersion.V26_3.get()); } catch (Throwable e) { ModdedIrisLog.error("Iris dimension type generation failed for {}", dimension.getLoadKey(), e); IrisModdedCommands.fail(source, IrisLanguage.plain(ModdedCommandMessages.MODDED_DATAPACK_COMMANDS_DIMENSION_TYPE_GENERATION_FAILED, MessageArgument.untrusted("value", level.name()), MessageArgument.untrusted("value2", String.valueOf(e.getMessage())))); @@ -187,7 +187,7 @@ public final class ModdedDatapackCommands { } File mcmeta = new File(worldDatapacksFolder(server), WORLD_PACK_NAME + "/pack.mcmeta"); - int packFormat = DataVersion.V26_2.getPackFormat(); + int packFormat = DataVersion.V26_3.getPackFormat(); String meta = "{\n" + " \"pack\": {\n" + " \"description\": \"Iris dimension types derived from the installed Iris packs.\",\n" diff --git a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenMethod.java b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenMethod.java index 058066888..f61043f5a 100644 --- a/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenMethod.java +++ b/adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedPregenMethod.java @@ -503,7 +503,7 @@ public final class ModdedPregenMethod implements PregeneratorMethod { /** * A dedicated server with {@code pause-when-empty-seconds > 0} returns from * {@code MinecraftServer#tickServer} before {@code tickChildren} once it has been empty for that - * long (26.2 only keeps {@code tickConnection} plus the task/chunk-poll window alive). That + * long (26.3 only keeps {@code tickConnection} plus the task/chunk-poll window alive). That * freezes every per-tick Iris service - world manager, scheduler, protocol sync, pregen HUD - and * the loader's own generation hooks for the whole job, and console pregen on a default * server.properties is always empty. The guard zeroes the setting for the duration of the job diff --git a/adapters/modded-common/src/main/resources/pack.mcmeta b/adapters/modded-common/src/main/resources/pack.mcmeta index 1189047ea..b4f9d7069 100644 --- a/adapters/modded-common/src/main/resources/pack.mcmeta +++ b/adapters/modded-common/src/main/resources/pack.mcmeta @@ -1,7 +1,7 @@ { "pack": { "description": "Iris client resources and mod data", - "min_format": [88, 0], - "max_format": [107, 1] + "min_format": [97, 1], + "max_format": [121, 0] } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedChunkGeneratorSpawnTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedChunkGeneratorSpawnTest.java index a908898f3..43aa6b0a0 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedChunkGeneratorSpawnTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/IrisModdedChunkGeneratorSpawnTest.java @@ -6,6 +6,7 @@ import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; import net.minecraft.util.random.Weighted; import net.minecraft.util.random.WeightedList; +import net.minecraft.util.valueproviders.UniformInt; import net.minecraft.world.entity.EntityTypes; import net.minecraft.world.level.biome.MobSpawnSettings; import org.junit.Test; @@ -24,10 +25,10 @@ public class IrisModdedChunkGeneratorSpawnTest { @Test public void explicitSpawnsReplaceMatchingVanillaTypesAndPreserveOthers() { - MobSpawnSettings.SpawnerData zombie = new MobSpawnSettings.SpawnerData(EntityTypes.ZOMBIE, 1, 4); - MobSpawnSettings.SpawnerData vanillaSlime = new MobSpawnSettings.SpawnerData(EntityTypes.SLIME, 1, 1); - MobSpawnSettings.SpawnerData explicitSlime = new MobSpawnSettings.SpawnerData(EntityTypes.SLIME, 2, 5); - MobSpawnSettings.SpawnerData explicitCow = new MobSpawnSettings.SpawnerData(EntityTypes.COW, 2, 4); + MobSpawnSettings.SpawnerData zombie = new MobSpawnSettings.SpawnerData(EntityTypes.ZOMBIE, UniformInt.of(1, 4)); + MobSpawnSettings.SpawnerData vanillaSlime = new MobSpawnSettings.SpawnerData(EntityTypes.SLIME, UniformInt.of(1, 1)); + MobSpawnSettings.SpawnerData explicitSlime = new MobSpawnSettings.SpawnerData(EntityTypes.SLIME, UniformInt.of(2, 5)); + MobSpawnSettings.SpawnerData explicitCow = new MobSpawnSettings.SpawnerData(EntityTypes.COW, UniformInt.of(2, 4)); WeightedList vanilla = WeightedList.of(List.of( new Weighted<>(zombie, 100), new Weighted<>(vanillaSlime, 1))); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomePolicyTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomePolicyTest.java index d590d36ff..644e24040 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomePolicyTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedBiomePolicyTest.java @@ -1,6 +1,12 @@ package art.arcane.iris.modded; +import art.arcane.iris.generation.biome.IrisBiome; +import art.arcane.iris.generation.runtime.Engine; +import art.arcane.iris.generation.runtime.GenerationSessionLease; +import art.arcane.iris.generation.runtime.IrisComplex; import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourceAccess; +import art.arcane.volmlib.util.stream.ProceduralStream; +import art.arcane.volmlib.util.stream.interpolation.Interpolated; import org.junit.Test; import java.util.ArrayList; @@ -13,6 +19,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ModdedBiomePolicyTest { @Test @@ -52,6 +61,29 @@ public class ModdedBiomePolicyTest { assertTrue(failure.getMessage().contains("example:missing")); } + @Test + public void structureRingBiomesResolveFromTheNaturalSurfaceWithoutPlanningHydrology() throws Exception { + BiomeToken forest = new BiomeToken("minecraft:forest"); + IrisBiome natural = mock(IrisBiome.class); + when(natural.getStructureDerivativeKey()).thenReturn(forest.key()); + IrisComplex complex = mock(IrisComplex.class); + when(complex.getNaturalTrueBiomeStream()).thenReturn( + ProceduralStream.of((x, z) -> natural, Interpolated.of(value -> 0D, value -> null))); + when(complex.getTrueBiomeStream()).thenThrow( + new AssertionError("Stronghold ring lookups must not plan hydrology")); + Engine engine = mock(Engine.class); + when(engine.getComplex()).thenReturn(complex); + when(engine.getMinHeight()).thenReturn(-64); + when(engine.acquireGenerationLease(anyString())).thenReturn(GenerationSessionLease.noop()); + ModdedBiomePolicy policy = new ModdedBiomePolicy<>( + new Access(new ArrayList<>(List.of(forest)))); + policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> engine, () -> engine, bound -> true, + () -> Set.of(forest.key()), bound -> Set.of(), () -> 0L)); + + assertSame(forest, policy.requiredStructureBiome(6000, 0, -6000, null)); + assertSame(forest, policy.requiredStructureBiome(6000, -30, -6000, null)); + } + private static ModdedBiomePolicy policy(List entries, AtomicReference> configured) { ModdedBiomePolicy policy = new ModdedBiomePolicy<>(new Access(entries)); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionTypeParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionTypeParityTest.java index 1adc60d53..0f522dc2f 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionTypeParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedDimensionTypeParityTest.java @@ -79,7 +79,7 @@ public class ModdedDimensionTypeParityTest { IrisDimension end = dimension("the_end", IrisEnvironment.THE_END, 0, 256, 256, new IrisDimensionTypeOptions()); IrisDimension custom = dimension("custom_contract", IrisEnvironment.CUSTOM, -128, 384, 384, customOptions); List dimensions = List.of(overworld, nether, end, custom); - IDataFixer fixer = DataVersion.V26_2.get(); + IDataFixer fixer = DataVersion.V26_3.get(); Path packDirectory = Files.createTempDirectory("iris-dimension-contracts"); KList roots = new KList<>(); roots.add(packDirectory.toFile()); diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedForcedDatapackTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedForcedDatapackTest.java index beccd3958..ad1af8e24 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedForcedDatapackTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedForcedDatapackTest.java @@ -51,15 +51,15 @@ import static org.junit.Assert.assertTrue; public class ModdedForcedDatapackTest { @Test - public void metadataRetains262FormatWhenBukkitAlsoSupports263() throws IOException { + public void metadataUses263DataFormat() throws IOException { Path packDirectory = Files.createTempDirectory("iris-modded-pack-format"); try { ModdedForcedDatapack.writePackMeta(packDirectory); JSONObject pack = new JSONObject(Files.readString(packDirectory.resolve("pack.mcmeta"))) .getJSONObject("pack"); - assertEquals(107, pack.getInt("pack_format")); - assertEquals(107, pack.getInt("min_format")); - assertEquals(107, pack.getInt("max_format")); + assertEquals(121, pack.getInt("pack_format")); + assertEquals(121, pack.getInt("min_format")); + assertEquals(121, pack.getInt("max_format")); } finally { deleteTree(packDirectory); } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedPlatformPathsTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedPlatformPathsTest.java index facc55cf2..c46775ab3 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedPlatformPathsTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedPlatformPathsTest.java @@ -9,6 +9,9 @@ import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerAccess; import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedServerLevels; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.ServerLevelAccessor; import net.minecraft.world.level.block.state.BlockState; import org.junit.Rule; import org.junit.Test; @@ -33,7 +36,7 @@ public class ModdedPlatformPathsTest { @Override public String minecraftVersion() { - return "26.2"; + return "26.3"; } @Override @@ -83,6 +86,11 @@ public class ModdedPlatformPathsTest { public boolean canBreakBlock(ServerLevel level, ServerPlayer player, BlockPos position, BlockState state) { return false; } + + @Override + public boolean checkSpawnPosition(Mob mob, ServerLevelAccessor level, EntitySpawnReason reason) { + return false; + } }); } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverridesTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverridesTest.java index 2b8d9a68e..2c2e5c2ce 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverridesTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedStructureSetFrequencyOverridesTest.java @@ -14,16 +14,18 @@ import net.minecraft.core.registries.Registries; import net.minecraft.resources.Identifier; import net.minecraft.resources.ResourceKey; import net.minecraft.server.Bootstrap; +import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeResolver; import net.minecraft.world.level.biome.BiomeSource; import net.minecraft.world.level.biome.Climate; import net.minecraft.world.level.chunk.ChunkGeneratorStructureState; import net.minecraft.world.level.levelgen.RandomState; import net.minecraft.world.level.levelgen.structure.StructureSet; +import net.minecraft.world.level.levelgen.structure.placement.AbstractSpreadingStructurePlacement; import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement; import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadType; import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement; -import net.minecraft.world.level.levelgen.structure.placement.StructurePlacementType; import org.junit.BeforeClass; import org.junit.Test; @@ -166,10 +168,10 @@ public class ModdedStructureSetFrequencyOverridesTest { ) { return new RandomSpreadStructurePlacement( Vec3i.ZERO, - StructurePlacement.FrequencyReductionMethod.DEFAULT, + AbstractSpreadingStructurePlacement.FrequencyReductionMethod.DEFAULT, 1F, salt, - Optional.of(new StructurePlacement.ExclusionZone(exclusionTarget, 1)), + Optional.of(new AbstractSpreadingStructurePlacement.ExclusionZone(exclusionTarget, 1)), spacing, separation, RandomSpreadType.LINEAR); @@ -183,6 +185,7 @@ public class ModdedStructureSetFrequencyOverridesTest { RandomState.class, BiomeSource.class, long.class, + ChunkPos.class, long.class, List.class); constructor.setAccessible(true); @@ -190,6 +193,7 @@ public class ModdedStructureSetFrequencyOverridesTest { null, new EmptyBiomeSource(), 1L, + ChunkPos.ZERO, 1L, sets); } @@ -211,8 +215,8 @@ public class ModdedStructureSetFrequencyOverridesTest { private static final class EmptyBiomeSource extends BiomeSource { @Override - public Holder getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) { - return null; + public BiomeResolver createResolver(Climate.Sampler sampler) { + return (x, y, z) -> null; } @Override @@ -226,18 +230,14 @@ public class ModdedStructureSetFrequencyOverridesTest { } } - private static final class UnsupportedPlacement extends StructurePlacement { - private UnsupportedPlacement() { - super(Vec3i.ZERO, FrequencyReductionMethod.DEFAULT, 1F, 1, Optional.empty()); - } - + private static final class UnsupportedPlacement implements StructurePlacement { @Override - protected boolean isPlacementChunk(ChunkGeneratorStructureState state, int x, int z) { + public boolean isStructureChunk(ChunkGeneratorStructureState state, int x, int z) { return false; } @Override - public StructurePlacementType type() { + public com.mojang.serialization.MapCodec codec() { return null; } } diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileParityTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileParityTest.java index d87c20d51..e19701f43 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileParityTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedTileParityTest.java @@ -19,6 +19,7 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BarrelBlockEntity; import net.minecraft.world.level.block.entity.ChestBlockEntity; import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.level.block.entity.SignTextSlot; import net.minecraft.world.level.block.entity.SpawnerBlockEntity; import net.minecraft.world.level.storage.TagValueInput; import org.junit.BeforeClass; @@ -61,9 +62,9 @@ public class ModdedTileParityTest { sign.loadWithComponents(TagValueInput.create(ProblemReporter.DISCARDING, registries, tile.nativeData().payload())); - assertEquals("Iris", sign.getFrontText().getMessage(0, false).getString()); - assertEquals("parity", sign.getBackText().getMessage(3, false).getString()); - assertEquals(DyeColor.BLUE, sign.getFrontText().getColor()); + assertEquals("Iris", sign.getText(SignTextSlot.FRONT).getMessages(false).get(0).getString()); + assertEquals("parity", sign.getText(SignTextSlot.BACK).getMessages(false).get(3).getString()); + assertEquals(DyeColor.BLUE, sign.getText(SignTextSlot.FRONT).getColor()); } @Test diff --git a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java index 52ca89a7a..8578a381c 100644 --- a/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/iris/modded/ModdedWaterEntitySpawnTest.java @@ -12,8 +12,11 @@ import art.arcane.volmlib.util.math.RNG; import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; import net.minecraft.server.Bootstrap; import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.BlockTags; +import net.minecraft.tags.TagLoader; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; @@ -22,6 +25,8 @@ import org.junit.BeforeClass; import org.junit.Test; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import static org.junit.Assert.assertEquals; @@ -42,6 +47,8 @@ public class ModdedWaterEntitySpawnTest { state.initCache(); } } + BuiltInRegistries.BLOCK.prepareTagReload(new TagLoader.LoadResult<>(Registries.BLOCK, Map.of( + BlockTags.BLOCKS_MOTION, List.of(Blocks.OAK_FENCE.builtInRegistryHolder())))).apply(); } @Test diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java index 00c4436c6..43a5f6712 100644 --- a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeStructureOperationsTest.java @@ -6,7 +6,11 @@ import art.arcane.volmlib.nativelib.minecraft26_2.modded.ModdedPlatformWorld; import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; -import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.levelgen.feature.FallenTreeFeature; +import net.minecraft.world.level.levelgen.feature.HugeBrownMushroomFeature; +import net.minecraft.world.level.levelgen.feature.HugeRedMushroomFeature; +import net.minecraft.world.level.levelgen.feature.OreFeature; +import net.minecraft.world.level.levelgen.feature.TreeFeature; import net.minecraft.world.level.levelgen.structure.BoundingBox; import org.junit.BeforeClass; import org.junit.Test; @@ -32,11 +36,11 @@ public class NativeStructureOperationsTest { @Test public void objectFeaturesMatchPaperGroups() { - assertEquals("trees", NativeStructureOperations.classifyFeature(Feature.TREE)); - assertEquals("fallen_trees", NativeStructureOperations.classifyFeature(Feature.FALLEN_TREE)); - assertEquals("mushrooms", NativeStructureOperations.classifyFeature(Feature.HUGE_BROWN_MUSHROOM)); - assertEquals("mushrooms", NativeStructureOperations.classifyFeature(Feature.HUGE_RED_MUSHROOM)); - assertNull(NativeStructureOperations.classifyFeature(Feature.ORE)); + assertEquals("trees", NativeStructureOperations.classifyFeature(mock(TreeFeature.class))); + assertEquals("fallen_trees", NativeStructureOperations.classifyFeature(mock(FallenTreeFeature.class))); + assertEquals("mushrooms", NativeStructureOperations.classifyFeature(mock(HugeBrownMushroomFeature.class))); + assertEquals("mushrooms", NativeStructureOperations.classifyFeature(mock(HugeRedMushroomFeature.class))); + assertNull(NativeStructureOperations.classifyFeature(mock(OreFeature.class))); } @Test diff --git a/adapters/neoforge/build.gradle b/adapters/neoforge/build.gradle index 557e7bdd7..5b8f1eb86 100644 --- a/adapters/neoforge/build.gradle +++ b/adapters/neoforge/build.gradle @@ -42,7 +42,7 @@ buildscript { plugins { id 'java' - id 'net.neoforged.moddev' version '2.0.141' + id 'net.neoforged.moddev' version '2.0.147' alias(libs.plugins.shadow) } @@ -60,8 +60,8 @@ apply from: file('../../gradle/modded-generation-build-revision.gradle') Properties rootProperties = new Properties() file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) } String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.1.2-26.2')) -String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.2')) -String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse(rootProperties.getProperty('neoForgeVersion', '26.2.0.59')) +String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3')) +String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse(rootProperties.getProperty('neoForgeVersion', '26.3.0.33-beta')) String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate').getOrElse(rootProperties.getProperty('volmLibCoordinate', 'com.github.VolmitSoftware:VolmLib:master-SNAPSHOT')) Closure irisArtifactName = { String platform, String targetVersion -> return "Iris v${project.version} [${platform}] ${targetVersion}.jar" diff --git a/adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml index 2c254d9ea..3d7ea0fb6 100644 --- a/adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml +++ b/adapters/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -25,7 +25,7 @@ logoFile = "assets/irisworldgen/icon.png" [[dependencies.irisworldgen]] modId = "neoforge" type = "required" -versionRange = "[26.2,26.3)" +versionRange = "[26.3,26.4)" ordering = "NONE" side = "BOTH" diff --git a/adapters/neoforge/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/neoforge/NativeNeoForgeSpawnPositionTest.java b/adapters/neoforge/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/neoforge/NativeNeoForgeSpawnPositionTest.java new file mode 100644 index 000000000..25667b89c --- /dev/null +++ b/adapters/neoforge/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/neoforge/NativeNeoForgeSpawnPositionTest.java @@ -0,0 +1,58 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.neoforge; + +import art.arcane.volmlib.nativelib.modded.NativeLoaderOptions; +import net.minecraft.SharedConstants; +import net.minecraft.server.Bootstrap; +import net.minecraft.world.entity.EntitySpawnReason; +import net.minecraft.world.entity.Mob; +import net.minecraft.world.level.ServerLevelAccessor; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.entity.living.MobSpawnEvent; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NativeNeoForgeSpawnPositionTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void chunkGenerationSpawnsFireThePositionCheckEventAndHonorItsDenial() { + NativeNeoForgeLoader loader = new NativeNeoForgeLoader( + new NativeLoaderOptions("irisworldgen", "treefeller", BooleanSupplier::getAsBoolean)); + ServerLevelAccessor level = mock(ServerLevelAccessor.class); + Mob mob = mock(Mob.class); + when(mob.checkSpawnRules(level, EntitySpawnReason.CHUNK_GENERATION)).thenReturn(true); + when(mob.checkSpawnObstruction(level)).thenReturn(true); + AtomicBoolean deny = new AtomicBoolean(true); + AtomicReference observed = new AtomicReference<>(); + Consumer listener = (MobSpawnEvent.PositionCheck event) -> { + if (event.getEntity() == mob && deny.get()) { + observed.set(event.getSpawnType()); + event.setResult(MobSpawnEvent.PositionCheck.Result.FAIL); + } + }; + NeoForge.EVENT_BUS.addListener(MobSpawnEvent.PositionCheck.class, listener); + try { + assertFalse(loader.checkSpawnPosition(mob, level, EntitySpawnReason.CHUNK_GENERATION)); + assertEquals(EntitySpawnReason.CHUNK_GENERATION, observed.get()); + deny.set(false); + assertTrue(loader.checkSpawnPosition(mob, level, EntitySpawnReason.CHUNK_GENERATION)); + } finally { + NeoForge.EVENT_BUS.unregister(listener); + } + } +} diff --git a/build.gradle b/build.gradle index a4a6eb464..b4269ebad 100644 --- a/build.gradle +++ b/build.gradle @@ -70,12 +70,12 @@ project(':core') { group = 'art.arcane' version = rootProject.version } -String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.2') -// The Bukkit jar retains 26.1.2 and 26.2 alongside 26.3; loader jars stay 26.2-only. +String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.3') +// The Bukkit jar retains 26.1.2 and 26.2 alongside 26.3; loader jars are 26.3-only. String bukkitMinecraftRange = providers.gradleProperty('bukkitMinecraftRange').getOrElse('26.1.2-26.3') -String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse('0.19.3') -String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse('26.2-65.1.1') -String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse('26.2.0.59') +String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse('0.19.5') +String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse('26.3-66.0.8') +String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse('26.3.0.33-beta') String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') .orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT') .get() @@ -997,15 +997,8 @@ project(':adapters:bukkit:nms:v26_3_R1') { TaskProvider unpackHeadlessNativeSources = tasks.register('unpackHeadlessNativeSources', Sync) { dependsOn(headlessNativeSources) inputs.property('nativeTypes', headlessNativeTypes) - inputs.property('nativeVersion', '26.3') from(provider { headlessNativeSources.collect { File artifact -> zipTree(artifact) } }) { headlessNativeTypes.each { String type -> include("art/arcane/volmlib/nativelib/minecraft26_2/modded/${type}.java") } - include('versions/26.3/**') - eachFile { FileCopyDetails source -> - if (source.path.startsWith('versions/26.3/')) { - source.path = source.path.substring('versions/26.3/'.length()) - } - } } from(rootProject.file('adapters/modded-common/src/main/java')) { include('art/arcane/iris/modded/ModdedDecoratorHooks.java', diff --git a/core/src/main/java/art/arcane/iris/pack/datapack/v263/DataFixerV263.java b/core/src/main/java/art/arcane/iris/pack/datapack/v263/DataFixerV263.java index 8c3228f12..a17f1e67d 100644 --- a/core/src/main/java/art/arcane/iris/pack/datapack/v263/DataFixerV263.java +++ b/core/src/main/java/art/arcane/iris/pack/datapack/v263/DataFixerV263.java @@ -34,9 +34,12 @@ public final class DataFixerV263 extends DataFixerV1217 { .put("max_inclusive", maximum)); } } + // Vanilla writes spawns as an overlay. A bare value decodes as an override, which NeoForge biome modifiers refuse to edit. attributes.put("minecraft:gameplay/natural_mob_spawns", new JSONObject() - .put("spawns_by_category", spawners) - .put("spawn_costs", fixed.getJSONObject("spawn_costs"))); + .put("argument", new JSONObject() + .put("spawns_by_category", spawners) + .put("spawn_costs", fixed.getJSONObject("spawn_costs"))) + .put("modifier", "overlay")); Object probability = fixed.remove("creature_spawn_probability"); if (probability != null) { attributes.put("minecraft:gameplay/creature_world_gen_spawn_probability", probability); diff --git a/gradle.properties b/gradle.properties index 15d565080..0e8b47eb7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,10 +22,10 @@ org.gradle.caching=true org.gradle.configureondemand=false irisVersion=4.1.2-26.2 -minecraftVersion=26.2 -# Bukkit plugin api-version: lowest supported Minecraft release line so one artifact loads on 26.1.2 and 26.2 +minecraftVersion=26.3 +# Bukkit plugin api-version: lowest supported Minecraft release line so one artifact loads on 26.1.2 through 26.3 apiVersion=26.1 -fabricLoaderVersion=0.19.3 -forgeVersion=26.2-65.1.1 -neoForgeVersion=26.2.0.59 +fabricLoaderVersion=0.19.5 +forgeVersion=26.3-66.0.8 +neoForgeVersion=26.3.0.33-beta volmLibCoordinate=com.github.VolmitSoftware.VolmLib:volmlib-shared:d24d28acddbf3555c0c30ac99569ebacd476ce27 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 79cfae060..0579cc126 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,16 +49,16 @@ multiverseCore = "5.8.0" craftengine = "26.8.2" # https://github.com/Xiao-MoMi/craft-engine/releases # Fabric API (net.fabricmc.fabric-api) - each module is independently versioned -fabricApi-base = "2.0.4+ece063239e" -fabricApi-registrySync = "7.1.0+c7bd5b8e9e" -fabricApi-resourceLoader = "2.0.13+9edec1269e" -fabricApi-lifecycleEvents = "4.1.3+4575b05f9e" -fabricApi-commandApi = "3.1.0+00cb03469e" -fabricApi-eventsInteraction = "5.2.6+8f57f7ee9e" -fabricApi-networking = "6.3.3+72073ef09e" -fabricApi-rendering = "25.3.1+6988455e9e" -fabricApi-keyMapping = "2.0.5+e2bdee789e" -fabricApi-permission = "1.0.3+b7020ca29e" +fabricApi-base = "2.0.6+fcdff87f5d" +fabricApi-registrySync = "8.0.1+fcdff87f5d" +fabricApi-resourceLoader = "3.0.4+fcdff87f5d" +fabricApi-lifecycleEvents = "4.1.9+ffef5f675d" +fabricApi-commandApi = "3.1.2+fcdff87f5d" +fabricApi-eventsInteraction = "5.3.6+3434d6d95d" +fabricApi-networking = "6.3.8+fcdff87f5d" +fabricApi-rendering = "27.0.14+901a437c5d" +fabricApi-keyMapping = "2.0.8+3434d6d95d" +fabricApi-permission = "1.0.8+fcdff87f5d" [libraries] # Core Libraries diff --git a/gradle/native-minecraft-sources.gradle b/gradle/native-minecraft-sources.gradle index 4f21047ea..4cc8201af 100644 --- a/gradle/native-minecraft-sources.gradle +++ b/gradle/native-minecraft-sources.gradle @@ -9,7 +9,7 @@ String nativeLibraryCoordinate = providers.gradleProperty('volmLibCoordinate') .getOrElse(nativeLibraryProperties.getProperty('volmLibCoordinate')) String nativeSourceLoader = extensions.extraProperties.get('nativeSourceLoader') String nativeSourceImplementation = 'minecraft26_2' -String nativeSourceMinecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.2') +String nativeSourceMinecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.3') extensions.extraProperties.set('nativeSourceImplementation', nativeSourceImplementation) dependencies.add(nativeMinecraftSources.name, "com.github.VolmitSoftware.VolmLib:native-${nativeSourceImplementation}-sources:${nativeLibraryCoordinate.tokenize(':').last()}")