Integrate optional terrain transforms

- Add versioned terrain providers that run before content placement.
- Resolve transformed terrain heights, occupancy, and cave metadata through bounded caches.
- Initialize native structures after binding the modded generation runtime.
- Cover transform behavior, provider resolution, and structure binding with regression tests.
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-02 05:39:20 -04:00
1 parent 74df63f717
commit 25c04c0157
35 files changed
+2160 -21

No files matched your search

@@ -42,6 +42,7 @@ public class ModdedMixinAuditTest {
"\\[\\[mixins]]\\s*config\\s*=\\s*\"([^\"]+)\"");
// One applied mixin per config proves the config was registered, which is all the audit detects.
private static final Set<String> UNAUDITED = Set.of(
"volmlib.entity.mixins.json/ServerLevelStructureBootstrapMixin",
"volmlib.entity.mixins.json/WorldGenerationWriteGuardMixin",
"volmlib.entity.mixins.json/ChunkMapTerrainReceiptAccess",
"volmlib.entity.mixins.json/ChunkTerrainReceiptMixin",
@@ -0,0 +1,145 @@
package art.arcane.iris.modded;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.EnginePlatformHooks;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedChunkGenerator;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedServer;
import art.arcane.volmlib.nativelib.terrain.NativeWorld;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.MockedStatic;
import java.io.File;
import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class ModdedStructureBindingOrderTest {
@BeforeClass
public static void bootstrap() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void initialBindingPublishesReadyEngineBeforeStartingStructureSearches() throws Exception {
NativeModdedChunkGenerator<?, ?, ?> nativeGenerator = mock(NativeModdedChunkGenerator.class);
ModdedEngineBinding<Engine> binding = new ModdedEngineBinding<>(1L, TimeUnit.MILLISECONDS);
IrisModdedChunkGenerator generator = generator(nativeGenerator, binding);
NativeWorld world = mock(NativeWorld.class);
NativeModdedServer server = mock(NativeModdedServer.class);
Engine engine = readyEngine();
when(nativeGenerator.represents(world)).thenReturn(true);
when(server.generateStructures()).thenReturn(true);
doAnswer(invocation -> {
assertSame(engine, generator.awaitStructureEngine());
assertSame(engine, binding.await("overworld"));
return null;
}).when(nativeGenerator).initializeStructureState(world);
try (MockedStatic<NativeModdedServer> servers = mockStatic(NativeModdedServer.class);
MockedStatic<ModdedWorldEngines> engines = mockStatic(ModdedWorldEngines.class);
MockedStatic<ModdedIrisLog> log = mockStatic(ModdedIrisLog.class)) {
servers.when(() -> NativeModdedServer.forWorld(world)).thenReturn(server);
engines.when(() -> ModdedWorldEngines.get(eq(world), any(), any(), anyLong(), any())).thenReturn(engine);
generator.bindLevel(world);
verify(nativeGenerator).initializeStructureState(world);
}
}
@Test
public void levelBindingStartsDeferredStructuresAfterAnEarlierDataQueryBoundTheEngine() throws Exception {
NativeModdedChunkGenerator<?, ?, ?> nativeGenerator = mock(NativeModdedChunkGenerator.class);
ModdedEngineBinding<Engine> binding = new ModdedEngineBinding<>(1L, TimeUnit.MILLISECONDS);
IrisModdedChunkGenerator generator = generator(nativeGenerator, binding);
NativeWorld world = mock(NativeWorld.class);
Engine engine = readyEngine();
when(nativeGenerator.represents(world)).thenReturn(true);
when(world.nativeHandle()).thenReturn(new Object());
field("engine").set(generator, engine);
field("boundLevel").set(generator, world);
binding.complete(engine);
doAnswer(invocation -> {
assertSame(engine, generator.awaitStructureEngine());
assertSame(engine, binding.await("overworld"));
return null;
}).when(nativeGenerator).initializeStructureState(world);
generator.bindLevel(world);
verify(nativeGenerator).initializeStructureState(world);
}
@Test
public void repointPublishesReplacementBindingBeforeStartingStructureSearches() throws Exception {
NativeModdedChunkGenerator<?, ?, ?> nativeGenerator = mock(NativeModdedChunkGenerator.class);
ModdedEngineBinding<Engine> binding = new ModdedEngineBinding<>(1L, TimeUnit.MILLISECONDS);
IrisModdedChunkGenerator generator = generator(nativeGenerator, binding);
NativeWorld world = mock(NativeWorld.class);
NativeModdedServer server = mock(NativeModdedServer.class);
Engine replacement = readyEngine();
when(nativeGenerator.represents(world)).thenReturn(true);
when(server.generateStructures()).thenReturn(true);
doAnswer(invocation -> {
assertSame(replacement, generator.awaitStructureEngine());
assertSame(replacement, binding.await("overworld"));
return null;
}).when(nativeGenerator).initializeStructureState(world);
try (MockedStatic<NativeModdedServer> servers = mockStatic(NativeModdedServer.class);
MockedStatic<ModdedWorldEngines> engines = mockStatic(ModdedWorldEngines.class)) {
servers.when(() -> NativeModdedServer.forWorld(world)).thenReturn(server);
engines.when(() -> ModdedWorldEngines.prepareReplacement(eq(world), eq("overworld"),
eq("overworld"), eq(42L), any())).thenReturn(replacement);
generator.repointAndBind(world, "overworld", "overworld", 42L);
verify(nativeGenerator).initializeStructureState(world);
}
}
private static IrisModdedChunkGenerator generator(NativeModdedChunkGenerator<?, ?, ?> nativeGenerator,
ModdedEngineBinding<Engine> binding) throws Exception {
IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class, CALLS_REAL_METHODS);
field("nativeGenerator").set(generator, nativeGenerator);
field("engineBinding").set(generator, binding);
field("dimensionKey").set(generator, "overworld");
field("generationMode").set(generator, ModdedGenerationMode.PERSISTENT_RESTORE);
field("announced").set(generator, new AtomicBoolean());
field("importedFeatures").set(generator, mock(ModdedImportedFeatureStage.class));
return generator;
}
private static Engine readyEngine() {
Engine engine = mock(Engine.class);
IrisData data = mock(IrisData.class);
when(engine.getComplex()).thenReturn(mock(IrisComplex.class));
when(engine.getData()).thenReturn(data);
when(data.getDataFolder()).thenReturn(new File("build/structure-binding-test/pack"));
when(engine.getPlatformHooks()).thenReturn(mock(EnginePlatformHooks.class));
return engine;
}
private static Field field(String name) throws NoSuchFieldException {
Field field = IrisModdedChunkGenerator.class.getDeclaredField(name);
field.setAccessible(true);
return field;
}
}
@@ -0,0 +1,70 @@
package art.arcane.volmlib.nativelib.minecraft26_2.modded.mixin;
import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeModdedChunkGenerator;
import art.arcane.volmlib.nativelib.terrain.NativeWorld;
import net.minecraft.SharedConstants;
import net.minecraft.server.Bootstrap;
import net.minecraft.server.level.ServerChunkCache;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class NativeStructureBootstrapTest {
@BeforeClass
public static void bootstrap() {
SharedConstants.tryDetectVersion();
Bootstrap.bootStrap();
}
@Test
public void levelConstructionDefersNativeStructureInitialization() {
ServerLevelStructureBootstrapMixin mixin = mock(ServerLevelStructureBootstrapMixin.class, CALLS_REAL_METHODS);
ServerChunkCache chunks = mock(ServerChunkCache.class);
NativeModdedChunkGenerator<?, ?, ?> generator = mock(NativeModdedChunkGenerator.class);
ChunkGeneratorStructureState state = mock(ChunkGeneratorStructureState.class);
when(mixin.getChunkSource()).thenReturn(chunks);
when(chunks.getGenerator()).thenReturn(generator);
mixin.iris$initializeStructureState(state);
verify(state, never()).ensureStructuresGenerated();
}
@Test
public void levelConstructionStillInitializesVanillaStructures() {
ServerLevelStructureBootstrapMixin mixin = mock(ServerLevelStructureBootstrapMixin.class, CALLS_REAL_METHODS);
ServerChunkCache chunks = mock(ServerChunkCache.class);
ChunkGenerator generator = mock(ChunkGenerator.class);
ChunkGeneratorStructureState state = mock(ChunkGeneratorStructureState.class);
when(mixin.getChunkSource()).thenReturn(chunks);
when(chunks.getGenerator()).thenReturn(generator);
mixin.iris$initializeStructureState(state);
verify(state).ensureStructuresGenerated();
}
@Test
public void boundNativeWorldInitializesItsDeferredStructures() {
NativeModdedChunkGenerator<?, ?, ?> generator = mock(NativeModdedChunkGenerator.class, CALLS_REAL_METHODS);
NativeWorld world = mock(NativeWorld.class);
ServerLevel level = mock(ServerLevel.class);
ServerChunkCache chunks = mock(ServerChunkCache.class);
ChunkGeneratorStructureState state = mock(ChunkGeneratorStructureState.class);
when(world.nativeHandle()).thenReturn(level);
when(level.getChunkSource()).thenReturn(chunks);
when(chunks.getGeneratorState()).thenReturn(state);
generator.initializeStructureState(world);
verify(state).ensureStructuresGenerated();
}
}