From 5a5e8a8dc85d092198491570e907af8e6a2e1e62 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 5 Sep 2026 12:31:36 -0400 Subject: [PATCH] Validated terrain samples - Terrain sampling rejects non-finite active bounds and noise before caching heights - Validation tests cover invalid samples and unchanged finite arithmetic - Bukkit packaging uses the existing 8 MB artifact limit --- build.gradle | 2 +- .../art/arcane/iris/engine/IrisComplex.java | 14 +++++- ...risComplexConstantGeneratorBoundsTest.java | 45 +++++++++++++++++-- 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/build.gradle b/build.gradle index 7b8a375be..90886034b 100644 --- a/build.gradle +++ b/build.gradle @@ -72,7 +72,7 @@ String fabricArtifactName = irisArtifactName('Fabric', "${minecraftVersion}+${lo String forgeArtifactName = irisArtifactName('Forge', "${minecraftVersion}+${loaderDisplayVersion(forgeVersion)}") String neoForgeArtifactName = irisArtifactName('NeoForge', "${minecraftVersion}+${loaderDisplayVersion(neoForgeVersion)}") File pluginOutDirectory = layout.projectDirectory.dir('../PluginOuts').asFile -long maximumBukkitArtifactBytes = 7_500_000L +long maximumBukkitArtifactBytes = 8_000_000L apply plugin: ApiGenerator // Where `buildAll` drops the per-platform jars for a local test server. Use the approved sibling diff --git a/core/src/main/java/art/arcane/iris/engine/IrisComplex.java b/core/src/main/java/art/arcane/iris/engine/IrisComplex.java index 451c85555..3e5565b6c 100644 --- a/core/src/main/java/art/arcane/iris/engine/IrisComplex.java +++ b/core/src/main/java/art/arcane/iris/engine/IrisComplex.java @@ -1401,15 +1401,25 @@ public class IrisComplex implements DataProvider { if (generators.length == 0) { return 0D; } + if (!Double.isFinite(low) || !Double.isFinite(high)) { + throw new IllegalStateException("Terrain generator bounds were not finite at " + x + "," + z + + " (generator=" + generators[0].getLoadKey() + ", bounds=" + low + ".." + high + ")."); + } double height = 0D; - if (low == high && Double.isFinite(low)) { + if (low == high) { for (int i = 0; i < generators.length; i++) { height += low; } return height / generators.length; } for (IrisGenerator generator : generators) { - height += M.lerp(low, high, generator.getHeight(x, z, seed)); + double noise = generator.getHeight(x, z, seed); + if (!Double.isFinite(noise)) { + throw new IllegalStateException("Terrain generator noise was not finite at " + x + "," + z + + " (generator=" + generator.getLoadKey() + ", noise=" + noise + + ", bounds=" + low + ".." + high + ")."); + } + height += M.lerp(low, high, noise); } return height / generators.length; } diff --git a/core/src/test/java/art/arcane/iris/engine/IrisComplexConstantGeneratorBoundsTest.java b/core/src/test/java/art/arcane/iris/engine/IrisComplexConstantGeneratorBoundsTest.java index d83c9b381..3fea48f2c 100644 --- a/core/src/test/java/art/arcane/iris/engine/IrisComplexConstantGeneratorBoundsTest.java +++ b/core/src/test/java/art/arcane/iris/engine/IrisComplexConstantGeneratorBoundsTest.java @@ -7,6 +7,7 @@ import org.junit.Test; import java.util.Arrays; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; public class IrisComplexConstantGeneratorBoundsTest { @@ -51,11 +52,10 @@ public class IrisComplexConstantGeneratorBoundsTest { } @Test - public void nonconstantAndNonfiniteBoundsRetainNoiseEvaluation() { - double[][] bounds = {{0D, 1D}, {-9D, 12D}, {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY}, - {Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY}, {Double.NaN, Double.NaN}}; + public void nonconstantFiniteBoundsRetainNoiseEvaluation() { + double[][] bounds = {{0D, 1D}, {-9D, 12D}}; for (double[] pair : bounds) { - for (double noise : new double[]{0.3D, Double.NaN, Double.POSITIVE_INFINITY}) { + for (double noise : new double[]{0.3D, -2D, 3D}) { CountingGenerator generator = new CountingGenerator(noise); double expected = (0D + M.lerp(pair[0], pair[1], noise)) / 1; assertBits(expected, IrisComplex.averageGeneratorHeights(new IrisGenerator[]{generator}, @@ -65,6 +65,43 @@ public class IrisComplexConstantGeneratorBoundsTest { } } + @Test + public void nonfiniteBoundsFailBeforeSamplingWithColumnAndGeneratorDetails() { + for (double invalid : new double[]{Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}) { + for (double[] bounds : new double[][]{{invalid, 1D}, {0D, invalid}, {invalid, invalid}}) { + CountingGenerator generator = new CountingGenerator(0.3D); + generator.setLoadKey("diagnostic/terrain"); + IllegalStateException failure = assertThrows(IllegalStateException.class, + () -> IrisComplex.averageGeneratorHeights(new IrisGenerator[]{generator}, + bounds[0], bounds[1], 671D, 2437D, 7331L)); + assertTrue(failure.getMessage().contains("671.0,2437.0")); + assertTrue(failure.getMessage().contains("generator=diagnostic/terrain")); + assertTrue(failure.getMessage().contains("bounds=" + bounds[0] + ".." + bounds[1])); + assertEquals(0, generator.calls); + } + } + } + + @Test + public void nonfiniteActiveNoiseFailsAtItsFirstSampleWithObservedValue() { + for (double invalid : new double[]{Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY}) { + CountingGenerator valid = new CountingGenerator(0.3D); + CountingGenerator invalidGenerator = new CountingGenerator(invalid); + invalidGenerator.setLoadKey("diagnostic/cliffs"); + CountingGenerator unsampled = new CountingGenerator(0.5D); + IllegalStateException failure = assertThrows(IllegalStateException.class, + () -> IrisComplex.averageGeneratorHeights(new IrisGenerator[]{valid, invalidGenerator, unsampled}, + 0D, 44D, 671D, 2437D, 7331L)); + assertTrue(failure.getMessage().contains("671.0,2437.0")); + assertTrue(failure.getMessage().contains("generator=diagnostic/cliffs")); + assertTrue(failure.getMessage().contains("noise=" + invalid)); + assertTrue(failure.getMessage().contains("bounds=0.0..44.0")); + assertEquals(1, valid.calls); + assertEquals(1, invalidGenerator.calls); + assertEquals(0, unsampled.calls); + } + } + private static void assertBits(double expected, double actual) { assertEquals(Double.doubleToLongBits(expected), Double.doubleToLongBits(actual)); }