mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-22 16:17:44 +00:00
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:
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",
|
||||
|
||||
+145
@@ -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;
|
||||
}
|
||||
}
|
||||
+70
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user