From b61d2f305b977ad4d10d037b4e539c794d1e517f Mon Sep 17 00:00:00 2001 From: Brian Neumann-Fopiano Date: Fri, 30 Oct 2026 22:50:49 -0400 Subject: [PATCH] Modded structure state key - Modded biome policy supplies the structure state key from the generator - History worlds key structure state by the active epoch - Studio worlds get a fresh transient key - Tests cover the key binding and the structure state codec --- .../iris/modded/IrisModdedChunkGenerator.java | 11 +++- .../arcane/iris/modded/ModdedBiomePolicy.java | 8 ++- .../iris/modded/ModdedBiomePolicyTest.java | 17 +++-- .../NativeModdedBiomeSourceCodecTest.java | 64 +++++++++++++++++++ 4 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeModdedBiomeSourceCodecTest.java 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 c91340570..4ba5c56d7 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 @@ -105,6 +105,7 @@ import java.util.Objects; import java.util.OptionalLong; import java.util.Set; import java.util.TreeSet; +import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.IntConsumer; @@ -946,6 +947,14 @@ public final class IrisModdedChunkGenerator implements NativeGeneratorOwner, Nat + " requires an attached generation-history runtime router.")); } + private String structureStateKey() { + Engine current = boundEngineOrNull(); + if (current == null || allowsGenerationHistoryBypass(current)) { + return "transient:" + UUID.randomUUID(); + } + return "epoch:" + requireHistoryRouter(current, "structure state key").history().activeEpoch().epochId(); + } + boolean allowsGenerationHistoryBypass(Engine current) { if (generationMode != ModdedGenerationMode.TRANSIENT_STUDIO) { return false; @@ -985,7 +994,7 @@ public final class IrisModdedChunkGenerator implements NativeGeneratorOwner, Nat ModdedBiomePolicy policy = new ModdedBiomePolicy<>(source); policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(this::boundEngineOrNull, this::awaitStructureEngine, this::allowsGenerationHistoryBypass, this::configuredStructureBiomeKeys, - IrisModdedChunkGenerator::retainedBiomeKeys, this::visibleBiomeSeed)); + IrisModdedChunkGenerator::retainedBiomeKeys, this::visibleBiomeSeed, this::structureStateKey)); return policy; } 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 8cd709adf..ad978d418 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 @@ -1395,6 +1395,11 @@ final class ModdedBiomePolicy implements NativeModdedBiomePolicy { return requireConfiguredStructureBiomeKeys(exactStructureBiomeKeys()); } + @Override + public String structureStateKey() { + return Objects.requireNonNull(runtime, "runtime").structureStateKey().get(); + } + @Override public int horizontalSearchStep(int blockY, int searchRadius) { return horizontalBiomeSearchQuartStep(blockY, searchRadius); @@ -1402,6 +1407,7 @@ final class ModdedBiomePolicy implements NativeModdedBiomePolicy { record RuntimeCallbacks(Supplier currentEngine, Supplier awaitEngine, Predicate historyBypass, Supplier> configuredStructureKeys, - Function> retainedBiomeKeys, LongSupplier visibleBiomeSeed) { + Function> retainedBiomeKeys, LongSupplier visibleBiomeSeed, + Supplier structureStateKey) { } } 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 f2aff0f79..a7cf721ab 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 @@ -102,6 +102,15 @@ public class ModdedBiomePolicyTest { assertSame(desert, policy.orderedPossibleBiomes().getFirst()); } + @Test + public void structureStateKeyComesFromTheBoundRuntime() { + ModdedBiomePolicy policy = new ModdedBiomePolicy<>(new Access(new ArrayList<>())); + policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> null, () -> null, engine -> false, + Set::of, engine -> Set.of(), () -> 0L, () -> "epoch:example")); + + assertEquals("epoch:example", policy.structureStateKey()); + } + @Test public void missingRequiredStructureBiomesFailBeforePublishingAPartialSet() { AtomicReference> configured = new AtomicReference<>(Set.of("example:missing")); @@ -130,7 +139,7 @@ public class ModdedBiomePolicyTest { 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)); + () -> Set.of(forest.key()), bound -> Set.of(), () -> 0L, () -> "transient:test")); assertSame(forest, policy.requiredStructureBiome(6000, 0, -6000, null)); assertSame(forest, policy.requiredStructureBiome(6000, -30, -6000, null)); @@ -212,7 +221,7 @@ public class ModdedBiomePolicyTest { ModdedBiomePolicy policy = new ModdedBiomePolicy<>( new Access(new ArrayList<>(List.of(plains, mirage)))); policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> engine, () -> engine, bound -> false, - () -> Set.of(plains.key(), mirage.key()), bound -> Set.of(), () -> 0L)); + () -> Set.of(plains.key(), mirage.key()), bound -> Set.of(), () -> 0L, () -> "transient:test")); int[] columns = {0, 1024, 2048, 3072, 4096, 5120}; NativeBiomeSourcePolicy.BiomeLocation found = policy.findClosestBiome3d( @@ -241,7 +250,7 @@ public class ModdedBiomePolicyTest { private static ModdedBiomePolicy boundTo(Engine engine) { ModdedBiomePolicy policy = new ModdedBiomePolicy<>(new Access(new ArrayList<>())); policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> engine, () -> engine, bound -> true, - Set::of, bound -> Set.of(), () -> 0L)); + Set::of, bound -> Set.of(), () -> 0L, () -> "transient:test")); return policy; } @@ -249,7 +258,7 @@ public class ModdedBiomePolicyTest { AtomicReference> configured) { ModdedBiomePolicy policy = new ModdedBiomePolicy<>(new Access(entries)); policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> null, () -> null, engine -> false, - configured::get, engine -> Set.of(), () -> 0L)); + configured::get, engine -> Set.of(), () -> 0L, () -> "transient:test")); return policy; } diff --git a/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeModdedBiomeSourceCodecTest.java b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeModdedBiomeSourceCodecTest.java new file mode 100644 index 000000000..07c1d8dde --- /dev/null +++ b/adapters/modded-common/src/test/java/art/arcane/volmlib/nativelib/minecraft26_2/modded/NativeModdedBiomeSourceCodecTest.java @@ -0,0 +1,64 @@ +package art.arcane.volmlib.nativelib.minecraft26_2.modded; + +import art.arcane.volmlib.nativelib.terrain.NativeModdedBiomePolicy; +import com.google.gson.JsonElement; +import com.mojang.serialization.DataResult; +import com.mojang.serialization.JsonOps; +import com.mojang.serialization.MapCodec; +import net.minecraft.SharedConstants; +import net.minecraft.core.Holder; +import net.minecraft.server.Bootstrap; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.Climate; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.lang.reflect.Constructor; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class NativeModdedBiomeSourceCodecTest { + @BeforeClass + public static void bootstrap() { + SharedConstants.tryDetectVersion(); + Bootstrap.bootStrap(); + } + + @Test + public void runtimeSourceEncodeReportsErrorInsteadOfThrowing() { + DataResult result = BiomeSource.CODEC.encodeStart(JsonOps.INSTANCE, runtimeSource("epoch:unused")); + assertTrue(result.isError()); + } + + @Test + @SuppressWarnings("unchecked") + public void structureStateSourceEncodesItsGenerationKey() throws ReflectiveOperationException { + BiomeSource structureState = structureState(runtimeSource("epoch:example")); + MapCodec codec = (MapCodec) NativeModdedBiomeSource.STRUCTURE_STATE_CODEC; + + JsonElement encoded = codec.codec().encodeStart(JsonOps.INSTANCE, structureState).getOrThrow(); + + assertEquals("epoch:example", encoded.getAsJsonObject().get("generation").getAsString()); + assertTrue(codec.codec().parse(JsonOps.INSTANCE, encoded).isError()); + } + + private static BiomeSource structureState(NativeModdedBiomeSource source) throws ReflectiveOperationException { + Class type = Class.forName(NativeModdedBiomeSource.class.getName() + "$StructureStateBiomeSource"); + Constructor constructor = type.getDeclaredConstructor(NativeModdedBiomeSource.class, Set.class); + constructor.setAccessible(true); + return (BiomeSource) constructor.newInstance(source, Set.>of()); + } + + @SuppressWarnings("unchecked") + private static NativeModdedBiomeSource, Climate.Sampler>> runtimeSource(String key) { + NativeModdedBiomePolicy, Climate.Sampler> policy = mock(NativeModdedBiomePolicy.class); + when(policy.structureStateKey()).thenReturn(key); + return new NativeModdedBiomeSource<>(new NativeModdedBiomeSource.Options<>( + mock(BiomeSource.class), () -> null, access -> policy)); + } +}