From 3a0e4557b7e030710b7c6104af1b2f209afff140 Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Sat, 5 Sep 2026 21:18:05 -0400 Subject: [PATCH] Fixed cave biome identity - Missing cave biome identities use the column surface biome - Capture tests cover omitted caves, configured caves, and invalid surface keys - Runtime scope tests retain keyed cave biome fixtures --- .../arcane/iris/engine/framework/Engine.java | 3 +- .../IrisEngineGenerationRuntimeScopeTest.java | 5 ++ .../engine/history/SavedBiomeCaptureTest.java | 86 +++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/art/arcane/iris/engine/framework/Engine.java b/core/src/main/java/art/arcane/iris/engine/framework/Engine.java index a5694466e..ceb6d4ca4 100644 --- a/core/src/main/java/art/arcane/iris/engine/framework/Engine.java +++ b/core/src/main/java/art/arcane/iris/engine/framework/Engine.java @@ -331,7 +331,8 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer, @BlockCoordinates default IrisBiome getCaveBiome(int x, int z) { - return getComplex().getCaveBiomeStream().get(x, z); + IrisBiome biome = getComplex().getCaveBiomeStream().get(x, z); + return biome == null || biome.getLoadKey() == null ? getSurfaceBiome(x, z) : biome; } @BlockCoordinates diff --git a/core/src/test/java/art/arcane/iris/engine/IrisEngineGenerationRuntimeScopeTest.java b/core/src/test/java/art/arcane/iris/engine/IrisEngineGenerationRuntimeScopeTest.java index fa3143068..6f185ee00 100644 --- a/core/src/test/java/art/arcane/iris/engine/IrisEngineGenerationRuntimeScopeTest.java +++ b/core/src/test/java/art/arcane/iris/engine/IrisEngineGenerationRuntimeScopeTest.java @@ -102,6 +102,7 @@ public class IrisEngineGenerationRuntimeScopeTest { when(context.getComplex()).thenReturn(selected.complex); when(context.isNaturalTerrain()).thenReturn(true); IrisBiome cave = mock(IrisBiome.class); + when(cave.getLoadKey()).thenReturn("cave"); IrisBiome surface = mock(IrisBiome.class); ProceduralStream caves = mock(ProceduralStream.class); ProceduralStream surfaces = mock(ProceduralStream.class); @@ -139,6 +140,9 @@ public class IrisEngineGenerationRuntimeScopeTest { when(router.biomes()).thenReturn(mock(SavedBiomeRuntime.class)); setField(engine, "generationHistoryRuntimeRouter", router); ProceduralStream caves = mock(ProceduralStream.class); + IrisBiome cave = mock(IrisBiome.class); + when(cave.getLoadKey()).thenReturn("cave"); + when(caves.get(19D, -3D)).thenReturn(cave); when(active.complex.getCaveBiomeStream()).thenReturn(caves); when(selected.complex.getCaveBiomeStream()).thenReturn(caves); doCallRealMethod().when(active.complex).isNaturalTerrainContext(); @@ -175,6 +179,7 @@ public class IrisEngineGenerationRuntimeScopeTest { when(context.getComplex()).thenReturn(active.complex); when(context.isNaturalTerrain()).thenReturn(false); IrisBiome cave = mock(IrisBiome.class); + when(cave.getLoadKey()).thenReturn("cave"); IrisBiome surface = mock(IrisBiome.class); IrisRegion region = mock(IrisRegion.class); ProceduralStream caves = mock(ProceduralStream.class); diff --git a/core/src/test/java/art/arcane/iris/engine/history/SavedBiomeCaptureTest.java b/core/src/test/java/art/arcane/iris/engine/history/SavedBiomeCaptureTest.java index 73f2cb9bd..aabd5cc93 100644 --- a/core/src/test/java/art/arcane/iris/engine/history/SavedBiomeCaptureTest.java +++ b/core/src/test/java/art/arcane/iris/engine/history/SavedBiomeCaptureTest.java @@ -1,18 +1,77 @@ package art.arcane.iris.engine.history; import art.arcane.iris.engine.framework.Engine; +import art.arcane.iris.engine.object.InferredType; import art.arcane.iris.engine.object.IrisBiome; import art.arcane.iris.engine.object.IrisRegion; import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyDouble; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; public class SavedBiomeCaptureTest { + @Test + public void capturesSurfaceIdentityAtEveryHeightWhenCaveBiomesAreOmitted() throws Exception { + IrisBiome surface = biome("flat"); + IrisBiome emptyCave = new IrisBiome().setInferredType(InferredType.CAVE); + Engine engine = caveCaptureEngine(surface, emptyCave); + SavedBiomeRuntime historical = mock(SavedBiomeRuntime.class); + + SavedBiomeChunk chunk = SavedBiomeCapture.capture(engine, stage(), historical, null); + + SavedBiomeChunk.Cell expected = new SavedBiomeChunk.Cell(1L, "flat", "flat-region"); + assertEquals(new SavedBiomeChunk.Header(-1, 2, 1L, -64, 16), chunk.header()); + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + assertEquals(expected, chunk.surfaceAt(x, z)); + assertEquals(expected, chunk.caveBaseAt(x, z)); + assertEquals(1, chunk.column(x, z).vertical().size()); + for (int y = -64; y < -48; y++) { + assertEquals(expected, chunk.biomeAt(x, y, z)); + } + } + } + assertSame(emptyCave, engine.getComplex().getCaveBiomeStream().get(-16D, 32D)); + assertNull(emptyCave.getLoadKey()); + assertTrue(emptyCave.getDecorators().isEmpty()); + assertTrue(emptyCave.getStructures().isEmpty()); + verifyNoInteractions(historical); + } + + @Test + public void retainsConfiguredCaveIdentityThroughTheEngineLookup() throws Exception { + IrisBiome surface = biome("flat"); + IrisBiome cave = biome("configured-cave").setInferredType(InferredType.CAVE); + Engine engine = caveCaptureEngine(surface, cave); + + SavedBiomeChunk chunk = SavedBiomeCapture.capture(engine, stage(), mock(SavedBiomeRuntime.class), null); + + assertSame(cave, engine.getCaveBiome(-16, 32)); + assertEquals("configured-cave", chunk.caveBaseAt(0, 0).biomeKey()); + assertEquals("configured-cave", chunk.biomeAt(0, -64, 0).biomeKey()); + assertEquals("configured-cave", chunk.biomeAt(0, -57, 0).biomeKey()); + assertEquals("flat", chunk.biomeAt(0, -56, 0).biomeKey()); + assertEquals("flat", chunk.surfaceAt(0, 0).biomeKey()); + } + + @Test + public void stillRejectsAnUnkeyedSurfaceBiome() { + Engine engine = caveCaptureEngine(new IrisBiome(), new IrisBiome().setInferredType(InferredType.CAVE)); + + assertThrows(IllegalArgumentException.class, + () -> SavedBiomeCapture.capture(engine, stage(), mock(SavedBiomeRuntime.class), null)); + } + @Test public void retainsFloatingChildIdentityWhenItSharesTheHostDerivative() throws Exception { Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS); @@ -92,6 +151,33 @@ public class SavedBiomeCaptureTest { verifyNoInteractions(historical); } + private static Engine caveCaptureEngine(IrisBiome surface, IrisBiome cave) { + Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS); + IrisRegion region = region("flat-region"); + when(engine.getMinHeight()).thenReturn(-64); + when(engine.getHeight()).thenReturn(16); + when(engine.getHeight(anyInt(), anyInt())).thenReturn(8); + when(engine.getDimensionStackContext()).thenReturn(null); + when(engine.getComplex().getTransitionGenerationPlan()).thenReturn(null); + when(engine.getComplex().getCaveBiomeStream().get(anyDouble(), anyDouble())).thenReturn(cave); + when(engine.getRegion(anyInt(), anyInt())).thenReturn(region); + when(engine.getRegion(anyInt(), anyInt(), anyInt())).thenReturn(region); + when(engine.getSurfaceBiome(anyInt(), anyInt())).thenReturn(surface); + doCallRealMethod().when(engine).getCaveBiome(anyInt(), anyInt()); + doCallRealMethod().when(engine).getBiomeOrMantle(anyInt(), anyInt(), anyInt()); + when(engine.getCaveOrMantleBiome(anyInt(), anyInt(), anyInt())) + .thenAnswer(call -> engine.getCaveBiome(call.getArgument(0), call.getArgument(2))); + return engine; + } + + private static GenerationHistory.GenerationStage stage() { + GenerationHistory.GenerationStage stage = mock(GenerationHistory.GenerationStage.class); + when(stage.chunkX()).thenReturn(-1); + when(stage.chunkZ()).thenReturn(2); + when(stage.activation()).thenReturn(GenerationActivation.initial("a".repeat(64), 1L)); + return stage; + } + private static IrisBiome biome(String key) { IrisBiome biome = new IrisBiome(); biome.setLoadKey(key);