Fixing a handfull of issues!

This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-21 18:07:10 -04:00
1 parent 1132242c58
commit c3359e0a16
30 files changed
+1324 -282

No files matched your search

@@ -1,17 +1,26 @@
package art.arcane.iris.generation.chunk;
import art.arcane.iris.platform.bukkit.nms.INMS;
import art.arcane.iris.platform.bukkit.nms.INMSBinding;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator;
import org.junit.Test;
import org.mockito.MockedStatic;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
public class ChunkDataHunkHolderTest {
@@ -90,6 +99,53 @@ public class ChunkDataHunkHolderTest {
}
}
@Test
public void fallbackAppliesExplicitCaveAirBetweenSolidRunsWithNegativeMinimumHeight() {
ChunkGenerator.ChunkData chunk = mock(ChunkGenerator.ChunkData.class);
when(chunk.getMinHeight()).thenReturn(-256);
when(chunk.getMaxHeight()).thenReturn(512);
ChunkDataHunkHolder holder = new ChunkDataHunkHolder(chunk);
BlockData dirt = mock(BlockData.class);
BlockData coal = mock(BlockData.class);
BlockData caveAir = mock(BlockData.class);
NativeBlockState dirtState = state(dirt);
NativeBlockState coalState = state(coal);
NativeBlockState caveAirState = state(caveAir);
BlockData[] applied = new BlockData[768];
Arrays.fill(applied, dirt);
for (int y = 0; y <= 75; y++) {
holder.setRaw(14, y + 256, 6, y < 33 ? dirtState : y <= 54 ? caveAirState : coalState);
}
doAnswer(call -> {
int y = call.getArgument(1);
applied[y + 256] = call.getArgument(3);
return null;
}).when(chunk).setBlock(anyInt(), anyInt(), anyInt(), any(BlockData.class));
doAnswer(call -> {
int from = call.getArgument(1);
int to = call.getArgument(4);
BlockData block = call.getArgument(6);
Arrays.fill(applied, from + 256, to + 256, block);
return null;
}).when(chunk).setRegion(anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), anyInt(), any(BlockData.class));
try (MockedStatic<INMS> nms = mockStatic(INMS.class)) {
nms.when(INMS::get).thenReturn(mock(INMSBinding.class));
holder.apply();
}
for (int y = -256; y < 512; y++) {
BlockData expected = y >= 33 && y <= 54 ? caveAir : y >= 55 && y <= 75 ? coal : dirt;
assertSame("column at " + y, expected, applied[y + 256]);
}
}
private static NativeBlockState state(BlockData data) {
NativeBlockState state = mock(NativeBlockState.class);
when(state.nativeHandle()).thenReturn(data);
return state;
}
private static ChunkDataHunkHolder holder(int height) {
ChunkGenerator.ChunkData chunk = mock(ChunkGenerator.ChunkData.class);
when(chunk.getMinHeight()).thenReturn(0);
@@ -231,6 +231,53 @@ public class IrisCaveCarver3DNearParityTest {
}
}
@Test
public void sparseProfilesSampleOnlyActiveColumnsAndDiscardPreviousPassColumns() {
for (int sampleStep : new int[]{1, 2, 3}) {
for (boolean adaptive : new boolean[]{false, true}) {
Engine engine = createEngine(128, 110);
AtomicInteger heightSamples = new AtomicInteger();
doAnswer(invocation -> {
heightSamples.incrementAndGet();
return 110;
}).when(engine).getHeight(anyInt(), anyInt(), anyBoolean());
IrisCaveProfile profile = createProfile(false, false)
.setSampleStep(sampleStep).setAdaptiveSampling(adaptive)
.setAllowFluid(false).setAllowSurfaceBreak(false)
.setDensityThreshold(new IrisStyledRange(2D, 2D, new IrisGeneratorStyle(NoiseStyle.FLAT)));
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile);
double[] weights = new double[256];
weights[0] = 1D;
weights[255] = 1D;
WriterCapture first = createWriterCapture(128);
carver.carve(first.writer, 0, 0, weights, 0.08D, 0.24D);
assertEquals(2, heightSamples.get());
assertFalse(first.carvedCells.isEmpty());
for (String cell : first.carvedCells) {
int x = coordinate(cell, 0);
int z = coordinate(cell, 2);
assertTrue(x == 0 && z == 0 || x == 15 && z == 15);
}
Arrays.fill(weights, 0D);
weights[(7 << 4) | 3] = 1D;
heightSamples.set(0);
WriterCapture second = createWriterCapture(128);
carver.carve(second.writer, 0, 0, weights, 0.08D, 0.24D);
assertEquals(1, heightSamples.get());
assertFalse(second.carvedCells.isEmpty());
for (String cell : second.carvedCells) {
assertEquals(7, coordinate(cell, 0));
assertEquals(3, coordinate(cell, 2));
}
}
}
}
@Test
public void verticalDensityPreservesTwoBlockModuleTunnelsWithoutBleedingAcrossBands() throws Exception {
for (int step : new int[]{2, 4, 8}) {
@@ -0,0 +1,173 @@
package art.arcane.iris.generation.mantle;
import art.arcane.iris.generation.biome.IrisBiome;
import art.arcane.iris.generation.cave.IrisCaveProfile;
import art.arcane.iris.generation.context.ChunkContext;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.EngineMetrics;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.terrain.IrisRegion;
import art.arcane.iris.world.IrisWorld;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.stream.ProceduralStream;
import org.junit.Test;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyDouble;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MantleCarvingComponentJunctionTest {
@Test
public void twoCaveProfilesBlendAcrossBothSidesOfAChunkBoundary() throws Exception {
Fixture fixture = fixture(false);
fixture.generate(false);
assertTrue(fixture.weight(-1, fixture.west(), 15, 12) > 0D);
assertTrue(fixture.weight(-1, fixture.east(), 15, 12) > 0D);
assertTrue(fixture.weight(0, fixture.west(), 0, 12) > 0D);
assertTrue(fixture.weight(0, fixture.east(), 0, 12) > 0D);
assertEquals(fixture.weight(-1, fixture.west(), 15, 12),
fixture.weight(0, fixture.east(), 0, 12), 0D);
assertEquals(fixture.weight(-1, fixture.east(), 15, 12),
fixture.weight(0, fixture.west(), 0, 12), 0D);
}
@Test
public void thirdProfileOutsideTheBlendRadiusDoesNotChangeBoundaryWeights() throws Exception {
Fixture control = fixture(false);
Fixture junction = fixture(true);
control.generate(false);
junction.generate(false);
assertEquals("A third biome outside the local blend radius must not remove the east cave from the west chunk",
control.weight(-1, control.east(), 15, 12),
junction.weight(-1, junction.east(), 15, 12), 0D);
assertEquals("A third biome outside the local blend radius must not remove the west cave from the east chunk",
control.weight(0, control.west(), 0, 12),
junction.weight(0, junction.west(), 0, 12), 0D);
}
@Test
public void threeWayJunctionRetainsEachProfileOnlyInsideItsLocalBlend() throws Exception {
Fixture fixture = fixture(true);
fixture.generate(false);
for (int chunkX : new int[]{-1, 0}) {
int boundaryX = chunkX == -1 ? 15 : 0;
assertEquals(3, fixture.captured().get(chunkX).size());
assertTrue(fixture.weight(chunkX, fixture.west(), boundaryX, 6) > 0D);
assertTrue(fixture.weight(chunkX, fixture.east(), boundaryX, 6) > 0D);
assertTrue(fixture.weight(chunkX, fixture.north(), boundaryX, 6) > 0D);
assertEquals(0D, fixture.weight(chunkX, fixture.north(), boundaryX, 12), 0D);
assertEquals(0D, fixture.weight(chunkX, fixture.west(), boundaryX, 0), 0D);
assertEquals(0D, fixture.weight(chunkX, fixture.east(), boundaryX, 0), 0D);
assertEquals(1D, fixture.weight(chunkX, fixture.north(), boundaryX, 0), 0D);
}
}
@Test
public void junctionWeightsRemainStableAcrossGenerationOrder() throws Exception {
Fixture forward = fixture(true);
Fixture reverse = fixture(true);
forward.generate(false);
reverse.generate(true);
for (int chunkX : new int[]{-1, 0}) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
assertEquals(forward.weight(chunkX, forward.west(), x, z),
reverse.weight(chunkX, reverse.west(), x, z), 0D);
assertEquals(forward.weight(chunkX, forward.east(), x, z),
reverse.weight(chunkX, reverse.east(), x, z), 0D);
assertEquals(forward.weight(chunkX, forward.north(), x, z),
reverse.weight(chunkX, reverse.north(), x, z), 0D);
}
}
}
}
@SuppressWarnings("unchecked")
private static Fixture fixture(boolean thirdProfile) throws ReflectiveOperationException {
Engine engine = mock(Engine.class);
EngineMantle engineMantle = mock(EngineMantle.class);
when(engineMantle.getEngine()).thenReturn(engine);
MantleCarvingComponent component = new MantleCarvingComponent(engineMantle);
when(engine.getDimension()).thenReturn(new IrisDimension());
when(engine.getWorld()).thenReturn(IrisWorld.builder().minHeight(0).maxHeight(128).build());
when(engine.getMetrics()).thenReturn(new EngineMetrics(16));
IrisComplex complex = mock(IrisComplex.class);
when(complex.allowsMantleChunkWrite(anyInt(), anyInt())).thenReturn(true);
ProceduralStream<Double> heights = mock(ProceduralStream.class);
when(heights.getDouble(anyDouble(), anyDouble())).thenReturn(100D);
when(complex.getNaturalHeightStream()).thenReturn(heights);
ProceduralStream<IrisRegion> regions = mock(ProceduralStream.class);
ProceduralStream<IrisBiome> surfaces = mock(ProceduralStream.class);
ProceduralStream<IrisBiome> caves = mock(ProceduralStream.class);
ProceduralStream<NativeBlockState> fluids = mock(ProceduralStream.class);
when(complex.getRegionStream()).thenReturn(regions);
when(complex.getNaturalTrueBiomeStream()).thenReturn(surfaces);
when(complex.getCaveBiomeStream()).thenReturn(caves);
when(complex.getFluidStream()).thenReturn(fluids);
IrisCaveProfile west = new IrisCaveProfile().setEnabled(true);
IrisCaveProfile east = new IrisCaveProfile().setEnabled(true);
IrisCaveProfile north = new IrisCaveProfile().setEnabled(true);
IrisBiome westBiome = new IrisBiome().setCaveProfile(west);
IrisBiome eastBiome = new IrisBiome().setCaveProfile(east);
IrisBiome northBiome = new IrisBiome().setCaveProfile(north);
when(caves.get(anyDouble(), anyDouble())).thenAnswer(call -> {
double x = call.getArgument(0);
double z = call.getArgument(1);
return thirdProfile && z < 7D ? northBiome : x < 0D ? westBiome : eastBiome;
});
ChunkContext context = mock(ChunkContext.class);
when(context.getComplex()).thenReturn(complex);
MantleWriter writer = mock(MantleWriter.class);
Mantle<Matter> storage = mock(Mantle.class);
when(storage.getWorldHeight()).thenReturn(128);
when(writer.getMantle()).thenReturn(storage);
Map<Integer, Map<IrisCaveProfile, double[]>> captured = new HashMap<>();
Map<IrisCaveProfile, IrisCaveCarver3D> carvers = new IdentityHashMap<>();
for (IrisCaveProfile profile : new IrisCaveProfile[]{west, east, north}) {
IrisCaveCarver3D carver = mock(IrisCaveCarver3D.class);
doAnswer(call -> {
int chunkX = call.getArgument(1);
double[] weights = call.getArgument(3);
captured.computeIfAbsent(chunkX, ignored -> new IdentityHashMap<>()).put(profile, weights.clone());
return 0;
}).when(carver).carve(any(), anyInt(), anyInt(), any(), anyDouble(), anyDouble(),
any(), any(), any(), any(), any());
carvers.put(profile, carver);
}
Field profileCarvers = MantleCarvingComponent.class.getDeclaredField("profileCarvers");
profileCarvers.setAccessible(true);
profileCarvers.set(component, carvers);
return new Fixture(component, context, writer, west, east, north, captured);
}
private record Fixture(MantleCarvingComponent component, ChunkContext context, MantleWriter writer,
IrisCaveProfile west, IrisCaveProfile east, IrisCaveProfile north,
Map<Integer, Map<IrisCaveProfile, double[]>> captured) {
private void generate(boolean reverse) {
component.generateLayer(writer, reverse ? 0 : -1, 0, context);
component.generateLayer(writer, reverse ? -1 : 0, 0, context);
}
private double weight(int chunkX, IrisCaveProfile profile, int x, int z) {
double[] weights = captured.get(chunkX).get(profile);
return weights == null ? 0D : weights[(x << 4) | z];
}
}
}
@@ -1,162 +0,0 @@
package art.arcane.iris.generation.mantle;
import art.arcane.iris.generation.cave.IrisCaveProfile;
import org.junit.BeforeClass;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class MantleCarvingComponentTop2BlendTest {
private static Constructor<?> weightedProfileConstructor;
private static Method limitMethod;
private static Field profileField;
private static Field columnWeightsField;
@BeforeClass
public static void setup() throws Exception {
Class<?> weightedProfileClass = Class.forName("art.arcane.iris.generation.mantle.MantleCarvingComponent$WeightedProfile");
weightedProfileConstructor = weightedProfileClass.getDeclaredConstructor(IrisCaveProfile.class, double[].class, double.class, Class.forName("art.arcane.iris.pack.value.IrisRange"), int.class);
weightedProfileConstructor.setAccessible(true);
limitMethod = MantleCarvingComponent.class.getDeclaredMethod("limitAndMergeBlendedProfiles", List.class, int.class, int.class);
limitMethod.setAccessible(true);
profileField = weightedProfileClass.getDeclaredField("profile");
profileField.setAccessible(true);
columnWeightsField = weightedProfileClass.getDeclaredField("columnWeights");
columnWeightsField.setAccessible(true);
}
@Test
public void topTwoProfilesAreKeptAndDroppedWeightsAreMergedIntoDominantColumns() throws Exception {
WeightedInput input = createWeightedProfiles();
List<?> limited = invokeLimit(input.weightedProfiles(), 2);
assertEquals(2, limited.size());
Map<IrisCaveProfile, double[]> byProfile = extractWeightsByProfile(limited);
IrisCaveProfile first = input.profiles().first();
IrisCaveProfile second = input.profiles().second();
assertEquals(1.0D, byProfile.get(first)[1], 0D);
assertEquals(1.0D, byProfile.get(second)[0], 0D);
}
@Test
public void topTwoMergeIsDeterministicAcrossRuns() throws Exception {
WeightedInput firstInput = createWeightedProfiles();
WeightedInput secondInput = createWeightedProfiles();
List<?> first = invokeLimit(firstInput.weightedProfiles(), 2);
List<?> second = invokeLimit(secondInput.weightedProfiles(), 2);
Map<IrisCaveProfile, double[]> firstByProfile = extractWeightsByProfile(first);
Map<IrisCaveProfile, double[]> secondByProfile = extractWeightsByProfile(second);
assertEquals(firstByProfile.get(firstInput.profiles().first())[0], secondByProfile.get(secondInput.profiles().first())[0], 0D);
assertEquals(firstByProfile.get(firstInput.profiles().first())[1], secondByProfile.get(secondInput.profiles().first())[1], 0D);
assertEquals(firstByProfile.get(firstInput.profiles().second())[0], secondByProfile.get(secondInput.profiles().second())[0], 0D);
assertEquals(firstByProfile.get(firstInput.profiles().second())[1], secondByProfile.get(secondInput.profiles().second())[1], 0D);
}
@Test
public void topTwoMergePreservesIndependentWeightsAtHighColumnIndexes() throws Exception {
WeightedInput input = createWeightedProfiles();
List<?> limited = invokeLimit(input.weightedProfiles(), 2);
Map<IrisCaveProfile, double[]> byProfile = extractWeightsByProfile(limited);
IrisCaveProfile first = input.profiles().first();
IrisCaveProfile second = input.profiles().second();
assertEquals(1.0D, byProfile.get(first)[255], 0D);
assertEquals(1.0D, byProfile.get(second)[254], 0D);
}
@Test
public void topTwoTieBreaksBySequenceNotIdentity() throws Exception {
IrisCaveProfile first = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.0D);
IrisCaveProfile second = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.0D);
IrisCaveProfile third = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.0D);
double[] weightsA = new double[256];
double[] weightsB = new double[256];
double[] weightsC = new double[256];
weightsA[0] = 0.5D;
weightsB[0] = 0.5D;
weightsC[0] = 0.5D;
List<Object> weighted = new ArrayList<>();
weighted.add(weightedProfileConstructor.newInstance(first, weightsA, average(weightsA), null, 0));
weighted.add(weightedProfileConstructor.newInstance(second, weightsB, average(weightsB), null, 1));
weighted.add(weightedProfileConstructor.newInstance(third, weightsC, average(weightsC), null, 2));
List<?> limited = invokeLimit(weighted, 2);
assertEquals(2, limited.size());
Map<IrisCaveProfile, double[]> byProfile = extractWeightsByProfile(limited);
assertEquals(true, byProfile.containsKey(first));
assertEquals(true, byProfile.containsKey(second));
assertEquals(false, byProfile.containsKey(third));
}
private WeightedInput createWeightedProfiles() throws Exception {
IrisCaveProfile first = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.31D);
IrisCaveProfile second = new IrisCaveProfile().setEnabled(true).setBaseWeight(1.17D);
IrisCaveProfile third = new IrisCaveProfile().setEnabled(true).setBaseWeight(0.93D);
Profiles profiles = new Profiles(first, second, third);
double[] firstWeights = new double[256];
firstWeights[0] = 0.2D;
firstWeights[1] = 0.8D;
firstWeights[255] = 0.6D;
double[] secondWeights = new double[256];
secondWeights[0] = 0.7D;
secondWeights[1] = 0.1D;
secondWeights[254] = 0.7D;
double[] thirdWeights = new double[256];
thirdWeights[0] = 0.3D;
thirdWeights[1] = 0.4D;
thirdWeights[254] = 0.3D;
thirdWeights[255] = 0.4D;
List<Object> weighted = new ArrayList<>();
weighted.add(weightedProfileConstructor.newInstance(first, firstWeights, average(firstWeights), null, 0));
weighted.add(weightedProfileConstructor.newInstance(second, secondWeights, average(secondWeights), null, 1));
weighted.add(weightedProfileConstructor.newInstance(third, thirdWeights, average(thirdWeights), null, 2));
return new WeightedInput(weighted, profiles);
}
private List<?> invokeLimit(List<Object> weightedProfiles, int limit) throws Exception {
return (List<?>) limitMethod.invoke(null, weightedProfiles, limit, 256);
}
private Map<IrisCaveProfile, double[]> extractWeightsByProfile(List<?> weightedProfiles) throws Exception {
Map<IrisCaveProfile, double[]> byProfile = new IdentityHashMap<>();
for (Object weightedProfile : weightedProfiles) {
IrisCaveProfile profile = (IrisCaveProfile) profileField.get(weightedProfile);
double[] weights = (double[]) columnWeightsField.get(weightedProfile);
byProfile.put(profile, weights);
}
return byProfile;
}
private double average(double[] weights) {
double total = 0D;
for (double weight : weights) {
total += weight;
}
return total / weights.length;
}
private record Profiles(IrisCaveProfile first, IrisCaveProfile second, IrisCaveProfile third) {
}
private record WeightedInput(List<Object> weightedProfiles, Profiles profiles) {
}
}
@@ -13,6 +13,7 @@ import art.arcane.iris.structure.object.ObjectPlaceMode;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.mantle.EngineMantle;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.iris.testsupport.PlatformBinding;
import art.arcane.volmlib.util.math.Vector3i;
@@ -30,7 +31,10 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class NativeStructureObjectVetoTest {
@@ -52,6 +56,9 @@ public class NativeStructureObjectVetoTest {
when(complex.getHeightStream()).thenReturn(heightStream);
engine = mock(Engine.class);
when(engine.getHeight()).thenReturn(256);
when(engine.getHeight(anyInt(), anyInt())).thenReturn(SURFACE_Y);
when(engine.getHeight(anyInt(), anyInt(), eq(true))).thenAnswer(invocation ->
engine.getHeight(invocation.getArgument(0), invocation.getArgument(1)));
when(engine.getComplex()).thenReturn(complex);
when(engine.getDimension()).thenReturn(mock(IrisDimension.class));
volumes();
@@ -67,6 +74,79 @@ public class NativeStructureObjectVetoTest {
assertFalse(placer.written().isEmpty());
}
@Test
public void flattenSkirtRejectsATreeWhoseGroundWillBeLoweredOutsideThePiece() {
RecordingPlacer placer = new RecordingPlacer(engine);
volumes(new NativeStructureVolume("minecraft:village_plains", 1, 64, -1, 11, 65, 7,
new NativeStructureVolume.SurfaceAdjustment(List.of(
new NativeStructureVolume.SurfaceAnchor(1, 11, -1, 7, 64, 2)), 24, 64)));
assertEquals(-1, place(placer));
assertTrue(placer.written().isEmpty());
}
@Test
public void flattenSkirtKeepsATreeWhereTheGradeDoesNotLowerItsGround() {
RecordingPlacer placer = new RecordingPlacer(engine);
volumes(new NativeStructureVolume("minecraft:village_plains", 1, 96, -1, 11, 97, 7,
new NativeStructureVolume.SurfaceAdjustment(List.of(
new NativeStructureVolume.SurfaceAnchor(1, 11, -1, 7, 96, 2)), 24, 64)));
assertTrue(place(placer) >= 0);
assertFalse(placer.written().isEmpty());
}
@Test
public void flattenCutKeepsObjectsBelowTheRemainingCaveRoof() {
when(engine.getHeight(anyInt(), anyInt())).thenReturn(140);
RecordingPlacer placer = new RecordingPlacer(engine);
volumes(new NativeStructureVolume("minecraft:village_plains", 1, 120, -1, 11, 121, 7,
new NativeStructureVolume.SurfaceAdjustment(List.of(
new NativeStructureVolume.SurfaceAnchor(1, 11, -1, 7, 120, 2)), 24, 64)));
assertTrue(place(placer) >= 0);
assertFalse(placer.written().isEmpty());
verify(engine, times(1)).getHeight(0, 0);
}
@Test
public void flattenCutKeepsObjectsAboveTheRemovedSurfaceBand() {
when(engine.getHeight(anyInt(), anyInt())).thenReturn(40);
RecordingPlacer placer = new RecordingPlacer(engine);
volumes(new NativeStructureVolume("minecraft:village_plains", 1, 20, -1, 11, 21, 7,
new NativeStructureVolume.SurfaceAdjustment(List.of(
new NativeStructureVolume.SurfaceAnchor(1, 11, -1, 7, 20, 2)), 24, 64)));
assertTrue(place(placer) >= 0);
assertFalse(placer.written().isEmpty());
}
@Test
public void flattenCutUsesHostGroundBelowAnUpperTerrainLayer() {
EngineMantle mantle = mock(EngineMantle.class);
when(engine.getMantle()).thenReturn(mantle);
when(mantle.trueHeight(anyInt(), anyInt())).thenReturn(40);
RecordingPlacer placer = new RecordingPlacer(engine);
volumes(new NativeStructureVolume("minecraft:village_plains", 1, 20, -1, 11, 21, 7,
new NativeStructureVolume.SurfaceAdjustment(List.of(
new NativeStructureVolume.SurfaceAnchor(1, 11, -1, 7, 20, 2)), 24, 64)));
assertTrue(place(placer) >= 0);
assertFalse(placer.written().isEmpty());
}
@Test
public void flattenCutConvertsTheHostGroundToNegativeWorldCoordinates() {
when(engine.getMinHeight()).thenReturn(-256);
RecordingPlacer placer = new RecordingPlacer(engine);
volumes(new NativeStructureVolume("minecraft:village_plains", 1, -190, -1, 11, -189, 7,
new NativeStructureVolume.SurfaceAdjustment(List.of(
new NativeStructureVolume.SurfaceAnchor(1, 11, -1, 7, -190, 2)), 24, 64)));
assertEquals(-1, place(placer));
assertTrue(placer.written().isEmpty());
}
@Test
public void canopyBlockInsideAPieceRejectsTheWholeObject() {
int canopyY = plantedTopY();
@@ -250,7 +330,7 @@ public class NativeStructureObjectVetoTest {
}
private NativeStructureVolume volume(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
return new NativeStructureVolume("minecraft:village_plains", minX, minY, minZ, maxX, maxY, maxZ);
return new NativeStructureVolume("minecraft:village_plains", minX, minY, minZ, maxX, maxY, maxZ, null);
}
private static NativeBlockState state(String key, boolean solid) {
@@ -105,7 +105,7 @@ public class NativeStructureVolumeMemoTest {
}
private static NativeStructureVolume volume(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
return new NativeStructureVolume("minecraft:village_swamp", minX, minY, minZ, maxX, maxY, maxZ);
return new NativeStructureVolume("minecraft:village_swamp", minX, minY, minZ, maxX, maxY, maxZ, null);
}
private static final class RecordingHooks implements EnginePlatformHooks {
@@ -4,13 +4,16 @@ import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume;
import org.junit.Test;
import java.util.List;
import java.util.ArrayList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class NativeStructureVolumeTest {
private static final NativeStructureVolume PIECE =
new NativeStructureVolume("minecraft:village_plains", 10, 64, 20, 25, 78, 40);
new NativeStructureVolume("minecraft:village_plains", 10, 64, 20, 25, 78, 40, null);
@Test
public void rectQueriesIgnoreTheVerticalAxis() {
@@ -53,4 +56,46 @@ public class NativeStructureVolumeTest {
assertEquals(PIECE.maxY(), normalized.maxY());
assertEquals(PIECE.maxZ(), normalized.maxZ());
}
@Test
public void terrainInfluenceDoesNotChangePhysicalIntersection() {
NativeStructureVolume volume = adjustedPoint(0, 90, 0, 24);
assertTrue(volume.intersectsInfluenceRect(-1, 0, -1, 0));
assertFalse(volume.intersects(-1, 40, 0, -1, 160, 0));
assertFalse(volume.intersectsRect(-1, 0, -1, 0));
}
@Test
public void oppositeGradesBlendWithoutCuttingTheSurface() {
List<NativeStructureVolume> volumes = List.of(adjustedPoint(0, 90, 0, 20), adjustedPoint(20, 110, 0, 20));
assertEquals(100, NativeStructureVolume.surfaceCutY(volumes, 10, 0, 100));
}
@Test
public void halfBlockCutsRoundTheirSignedDepthLikeTheFitter() {
assertEquals(99, NativeStructureVolume.surfaceCutY(List.of(adjustedPoint(0, 99, 0, 24)), 12, 0, 100));
}
@Test
public void realVillageJunctionsLowerTheReportedPineGround() {
int[][] junctions = {{-2591,144,-7062},{-2591,144,-7061},{-2593,141,-7055},{-2592,141,-7055},
{-2591,163,-7070},{-2591,163,-7071},{-2608,149,-7075},{-2609,149,-7075},
{-2591,136,-7051},{-2591,137,-7051},{-2602,126,-7047},{-2591,136,-7050},
{-2591,137,-7050},{-2602,126,-7046},{-2602,122,-7042},{-2602,122,-7041},
{-2624,124,-7067},{-2624,124,-7068}};
List<NativeStructureVolume.SurfaceAnchor> anchors = new ArrayList<>();
for (int[] junction : junctions) {
anchors.add(new NativeStructureVolume.SurfaceAnchor(junction[0], junction[0], junction[2], junction[2], junction[1], 1));
}
NativeStructureVolume volume = new NativeStructureVolume("minecraft:village_plains", -2600, 144, -7070, -2590, 145, -7062,
new NativeStructureVolume.SurfaceAdjustment(anchors, 24, 64));
assertEquals(151, NativeStructureVolume.surfaceCutY(List.of(volume), -2601, -7063, 162));
assertEquals(162, NativeStructureVolume.surfaceCutY(List.of(volume), -2700, -7063, 162));
}
private static NativeStructureVolume adjustedPoint(int x, int groundY, int z, int padding) {
return new NativeStructureVolume("minecraft:village_plains", x, groundY, z, x, groundY, z,
new NativeStructureVolume.SurfaceAdjustment(List.of(
new NativeStructureVolume.SurfaceAnchor(x, x, z, z, groundY, 1)), padding, 64));
}
}
@@ -0,0 +1,184 @@
package art.arcane.iris.structure.object;
import art.arcane.iris.generation.block.TileData;
import art.arcane.iris.generation.decoration.tree.TreeBlockCanvas;
import art.arcane.iris.generation.decoration.tree.TreeGrounding;
import art.arcane.iris.generation.geometry.IrisBlockVector;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.math.Vector3i;
import org.junit.Test;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class IrisObjectFractionalScalingTest {
@Test
public void fractionalReductionRetainsBothLayersWhenTwoOutputVoxelsRemain() {
NativeBlockState stem = state("minecraft:mushroom_stem");
NativeBlockState cap = state("minecraft:red_mushroom_block");
IrisObject source = layers(stem, cap);
for (boolean savedOrigin : new boolean[]{false, true}) {
for (double factor : new double[]{0.6D, 0.75D, 0.9D}) {
IrisObject scaled = scale(source, factor, savedOrigin);
assertEquals(2, scaled.getH());
assertEquals(2, scaled.getBlocks().size());
assertSame(stem, scaled.getBlocks().get(scaled.getSigned(0, 0, 0)));
assertSame(cap, scaled.getBlocks().get(scaled.getSigned(0, 1, 0)));
}
}
assertSame(stem, source.getBlocks().get(source.getSigned(0, 0, 0)));
assertSame(cap, source.getBlocks().get(source.getSigned(0, 1, 0)));
}
@Test
public void fractionalEnlargementUsesTheDominantMaterialInSharedOutputVoxels() {
NativeBlockState lower = state("minecraft:stone");
NativeBlockState upper = state("minecraft:dirt");
IrisObject source = layers(lower, upper);
for (boolean savedOrigin : new boolean[]{false, true}) {
IrisObject scaled = scale(source, 1.75D, savedOrigin);
assertEquals(4, scaled.getH());
for (int y = 0; y < 4; y++) {
assertSame(y < 2 ? lower : upper, scaled.getBlocks().get(scaled.getSigned(0, y, 0)));
}
}
}
@Test
public void fractionalReductionResolvesMaterialCoverageAcrossAllThreeAxes() {
IrisObject source = new IrisObject(2, 2, 2);
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
for (int z = 0; z < 2; z++) {
source.setUnsigned(x, y, z, state("test:voxel_" + x + y + z));
}
}
}
IrisObject scaled = source.scaled(0.75D, IrisObjectPlacementScaleInterpolator.NONE);
assertEquals(8, scaled.getBlocks().size());
for (int x = 0; x < 2; x++) {
for (int y = 0; y < 2; y++) {
for (int z = 0; z < 2; z++) {
assertSame(source.getBlocks().get(source.getSigned(x, y, z)),
scaled.getBlocks().get(scaled.getSigned(x, y, z)));
}
}
}
}
@Test
public void fractionalSavedOriginPreservesNegativeCoordinatesAndTheirTiles() {
NativeBlockState lower = state("minecraft:chest");
NativeBlockState upper = state("minecraft:barrel");
IrisObject source = new IrisObject(1, 4, 1);
source.setCenter(new Vector3i(0, 3, 0));
source.setUnsigned(0, 1, 0, lower);
source.setUnsignedTile(0, 1, 0, tile("minecraft:chest", "lower"));
source.setUnsigned(0, 2, 0, upper);
source.setUnsignedTile(0, 2, 0, tile("minecraft:barrel", "upper"));
IrisObject scaled = source.scaledAroundOrigin(0.75D, IrisObjectPlacementScaleInterpolator.NONE);
assertEquals(new Vector3i(0, 3, 0), scaled.getCenter());
assertEquals(-3, scaled.getAABB().min().getY());
assertEquals(0, scaled.getAABB().max().getY());
assertSame(lower, scaled.getBlocks().get(new IrisBlockVector(0, -2, 0)));
assertSame(upper, scaled.getBlocks().get(new IrisBlockVector(0, -1, 0)));
assertEquals("lower", scaled.getStates().get(new IrisBlockVector(0, -2, 0)).getProperties().get("name"));
assertEquals("upper", scaled.getStates().get(new IrisBlockVector(0, -1, 0)).getProperties().get("name"));
}
@Test
public void sameMaterialTilesFollowTheirDominantSourceVoxel() {
NativeBlockState chest = state("minecraft:chest");
IrisObject source = layers(chest, chest);
TileData lower = tile("minecraft:chest", "lower");
TileData upper = tile("minecraft:chest", "upper");
source.setUnsignedTile(0, 0, 0, lower);
source.setUnsignedTile(0, 1, 0, upper);
IrisObject scaled = source.scaled(0.75D, IrisObjectPlacementScaleInterpolator.NONE);
TileData scaledLower = scaled.getStates().get(scaled.getSigned(0, 0, 0));
TileData scaledUpper = scaled.getStates().get(scaled.getSigned(0, 1, 0));
assertEquals(lower, scaledLower);
assertEquals(upper, scaledUpper);
assertNotSame(lower, scaledLower);
assertNotSame(lower.getProperties(), scaledLower.getProperties());
assertNotSame(upper, scaledUpper);
assertNotSame(scaledLower.getProperties(), scaledUpper.getProperties());
}
@Test
public void sameMaterialCanopyRolesAndRootContactsFollowTheDominantSourceVoxel() {
NativeBlockState wood = state("minecraft:spruce_wood");
IrisObject source = layers(wood, wood);
TreeGrounding grounding = TreeGrounding.create(List.of(new TreeGrounding.Root(0, 0, 0, wood)),
8, Set.of(new TreeBlockCanvas.Vec(0, 1, 0)));
source.setTreeGrounding(grounding);
for (boolean savedOrigin : new boolean[]{false, true}) {
IrisObject scaled = scale(source, 0.75D, savedOrigin);
int bottom = -scaled.getCenter().getY();
TreeGrounding result = scaled.getTreeGrounding();
assertEquals(List.of(new TreeGrounding.Root(0, bottom, 0, wood)), result.roots());
assertFalse(result.isCanopy(0, bottom, 0));
assertTrue(result.isCanopy(0, bottom + 1, 0));
assertEquals(6, result.maxExtension());
}
assertSame(grounding, source.getTreeGrounding());
}
@Test
public void canopyWinningAnUnresolvableRootCollisionDoesNotBecomeAGroundContact() {
NativeBlockState wood = state("minecraft:spruce_wood");
IrisObject source = layers(wood, wood);
source.setTreeGrounding(TreeGrounding.create(List.of(new TreeGrounding.Root(0, 0, 0, wood)),
8, Set.of(new TreeBlockCanvas.Vec(0, 1, 0))));
IrisObject scaled = source.scaled(0.5D, IrisObjectPlacementScaleInterpolator.NONE);
assertEquals(1, scaled.getBlocks().size());
assertTrue(scaled.getTreeGrounding().roots().isEmpty());
assertTrue(scaled.getTreeGrounding().isCanopy(0, 0, 0));
}
private static IrisObject layers(NativeBlockState lower, NativeBlockState upper) {
IrisObject source = new IrisObject(1, 2, 1);
source.setCenter(new Vector3i(0, 0, 0));
source.setUnsigned(0, 0, 0, lower);
source.setUnsigned(0, 1, 0, upper);
return source;
}
private static IrisObject scale(IrisObject source, double scale, boolean savedOrigin) {
return savedOrigin ? source.scaledAroundOrigin(scale, IrisObjectPlacementScaleInterpolator.NONE)
: source.scaled(scale, IrisObjectPlacementScaleInterpolator.NONE);
}
private static TileData tile(String material, String name) {
KMap<String, Object> properties = new KMap<>();
properties.put("name", name);
return new TileData(material, properties);
}
private static NativeBlockState state(String key) {
NativeBlockState state = mock(NativeBlockState.class);
when(state.key()).thenReturn(key);
when(state.materialKey()).thenReturn(key);
return state;
}
}
@@ -620,6 +620,91 @@ public class IrisObjectPlacementRunnerRegressionTest {
assertSame(machine, placer.get(0, ANCHOR_Y + 9, 0));
}
@Test
public void organicMushroomStiltsUseTheStemAndStopAtTheCaveFloor() {
NativeBlockState stem = state("minecraft:mushroom_stem", true);
NativeBlockState cap = state("minecraft:red_mushroom_block", true);
IrisObject mushroom = new IrisObject(1, 2, 1);
mushroom.setUnsigned(0, 0, 0, stem);
mushroom.setUnsigned(0, 1, 0, cap);
RecordingPlacer placer = new RecordingPlacer(null);
placer.terrain(0, 0, 0, 30, 120, 140);
placer.set(0, 30, 0, solid);
placer.writes().clear();
IrisObjectPlacement placement = placement().setMode(ObjectPlaceMode.ORGANIC_STILT)
.setStiltSettings(new IrisStiltSettings().setOrganicJitter(0).setOrganicScratch(0));
mushroom.place(0, 60, 0, placer, placement, new RNG(2L), data);
assertEquals(1, placer.writesOf(cap).size());
for (int y = 31; y < 60; y++) {
assertSame(stem, placer.get(0, y, 0));
}
assertSame(solid, placer.get(0, 30, 0));
assertTrue(placer.writes().stream().allMatch(write -> write.y() >= 31));
}
@Test
public void defaultOrganicStiltsDoNotReachAFloorMoreThanOneHundredBlocksBelow() {
NativeBlockState stem = state("minecraft:mushroom_stem", true);
IrisObject mushroom = new IrisObject(1, 1, 1);
mushroom.setUnsigned(0, 0, 0, stem);
RecordingPlacer placer = new RecordingPlacer(null);
placer.set(0, 30, 0, solid);
placer.writes().clear();
IrisObjectPlacement placement = placement().setMode(ObjectPlaceMode.ORGANIC_STILT)
.setStiltSettings(new IrisStiltSettings().setOrganicJitter(0).setOrganicScratch(0));
mushroom.place(0, 160, 0, placer, placement, new RNG(2L), data);
assertEquals(9, placer.world.values().stream().filter(block -> block == stem).count());
assertTrue(placer.writesOf(stem).stream().allMatch(write -> write.y() >= 152));
assertNull(placer.get(0, 151, 0));
assertSame(solid, placer.get(0, 30, 0));
}
@Test
public void shrinkingTwoBlockMushroomsPreservesTheStemUsedByOrganicStilts() {
NativeBlockState stem = state("minecraft:mushroom_stem", true);
NativeBlockState cap = state("minecraft:red_mushroom_block", true);
IrisObject mushroom = new IrisObject(1, 2, 1);
mushroom.setUnsigned(0, 0, 0, stem);
mushroom.setUnsigned(0, 1, 0, cap);
IrisObject scaled = mushroom.scaled(0.75, IrisObjectPlacementScaleInterpolator.NONE);
RecordingPlacer placer = new RecordingPlacer(null);
placer.set(0, ANCHOR_Y, 0, solid);
placer.writes().clear();
IrisObjectPlacement placement = placement().setMode(ObjectPlaceMode.ORGANIC_STILT)
.setStiltSettings(new IrisStiltSettings().setOrganicJitter(0).setOrganicScratch(0));
scaled.place(0, ANCHOR_Y + 10, 0, placer, placement, new RNG(2L), data);
assertSame(stem, placer.get(0, ANCHOR_Y + 1, 0));
assertEquals(1, placer.writesOf(cap).size());
}
@Test
public void centerHeightSurfaceSupportAllowsAnImportedRootAboveALowerAdjacentColumn() {
NativeBlockState wood = state("minecraft:spruce_wood", true);
IrisObject tree = new IrisObject(7, 27, 7);
tree.getBlocks().put(new IrisBlockVector(0, -13, 0), wood);
tree.getBlocks().put(new IrisBlockVector(1, -13, 0), wood);
tree.getBlocks().put(new IrisBlockVector(1, -13, -1), wood);
RecordingPlacer placer = new RecordingPlacer(null);
placer.terrain(0, 0, 0, 167);
placer.terrain(1, 0, 0, 163);
placer.terrain(1, -1, 0, 163);
IrisObjectPlacement placement = new IrisObjectPlacement()
.setRotation(IrisObjectRotation.of(0, 0, 0));
assertTrue(tree.place(0, -1, 0, placer, placement, new RNG(2L), data) >= 0);
assertSame(wood, placer.get(1, 167, 0));
assertNull(placer.get(1, 166, 0));
assertFalse(placer.isSurfaceSolid(1, 166, 0));
assertEquals(163, placer.highest(1, 0));
}
@Test
public void stiltPaletteCannotIntroduceExcludedMaterialsOrContainers() {
NativeBlockState calcite = state("minecraft:calcite", true);
@@ -10,6 +10,8 @@ import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.testsupport.PlatformBinding;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.math.RNG;
import org.junit.Before;
import org.junit.Rule;
@@ -84,6 +86,30 @@ public class TreeGroundingPlacementTest {
assertFalse(terrain.written.containsKey(new Position(2, 79, 0)));
}
@Test
public void nativeFlattenInfluenceDoesNotBlockRootExtensionsBelowItsPiece() {
Terrain terrain = new Terrain();
terrain.heights.put(new Column(1, 0), 77);
Engine engine = data.getEngine();
when(terrain.placer.getEngine()).thenReturn(engine);
when(engine.getDimension()).thenReturn(new IrisDimension());
when(engine.getHeight(anyInt(), anyInt(), anyBoolean())).thenAnswer(invocation ->
terrain.surface(invocation.getArgument(0), invocation.getArgument(1)));
NativeStructureVolume volume = new NativeStructureVolume("minecraft:village_plains",
10, 100, 0, 12, 110, 2,
new NativeStructureVolume.SurfaceAdjustment(List.of(
new NativeStructureVolume.SurfaceAnchor(10, 12, 0, 2, 100, 4)), 16, 16));
when(engine.getNativeStructureVolumes(anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(new KList<>(List.of(volume)));
assertTrue(place(tree(true), terrain, placement()) >= 0);
for (int y = 77; y <= 81; y++) {
assertSame(wood, terrain.written.get(new Position(1, y, 0)));
}
assertFalse(terrain.written.containsKey(new Position(1, 76, 0)));
}
@Test
public void cliffRejectionWritesNoPartOfTheTreeOrListener() {
Terrain terrain = new Terrain();
@@ -0,0 +1,65 @@
package art.arcane.iris.world;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.platform.generation.BukkitChunkGenerator;
import art.arcane.iris.world.lifecycle.WorldLifecycleCaller;
import art.arcane.iris.world.lifecycle.WorldLifecycleRequest;
import org.bukkit.NamespacedKey;
import org.bukkit.WorldCreator;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
public class IrisWorldCreatorSeedTest {
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Test
public void explicitSeedsReachTheGeneratorAndLifecycleRequestWithoutDefaultSubstitution() throws Exception {
for (long seed : new long[]{0L, 78264193L, -734829104958217364L, Long.MIN_VALUE, Long.MAX_VALUE}) {
assertCreatedSeed(new IrisWorldCreator().seed(seed), seed);
}
}
@Test
public void omittedSeedUsesTheCreationDefault() throws Exception {
assertCreatedSeed(new IrisWorldCreator(), 1337L);
}
private void assertCreatedSeed(IrisWorldCreator creator, long expectedSeed) throws Exception {
NamespacedKey key = new NamespacedKey("iris", "seed-selection");
File root = temporaryFolder.newFolder();
IrisDimension dimension = new IrisDimension();
dimension.setLoadKey("cohesion");
WorldCreator bukkitCreator = WorldCreatorCompat.ofKey(key);
List<IrisWorld> worlds = new ArrayList<>();
try (MockedStatic<IrisWorldStorage> storage = mockStatic(IrisWorldStorage.class);
MockedStatic<WorldCreatorCompat> compatibility = mockStatic(WorldCreatorCompat.class);
MockedConstruction<BukkitChunkGenerator> generators = mockConstruction(BukkitChunkGenerator.class,
(generator, context) -> worlds.add((IrisWorld) context.arguments().getFirst()))) {
storage.when(() -> IrisWorldStorage.keyFromName("seed-selection")).thenReturn(key);
storage.when(() -> IrisWorldStorage.dimensionRoot(key)).thenReturn(root);
compatibility.when(() -> WorldCreatorCompat.ofKey(key)).thenReturn(bukkitCreator);
compatibility.when(() -> WorldCreatorCompat.keyOf(bukkitCreator)).thenReturn(key);
WorldCreator created = creator.name("seed-selection").dimension(dimension).create();
WorldLifecycleRequest request = WorldLifecycleRequest.fromCreator(
created, false, false, WorldLifecycleCaller.CREATE);
assertEquals(1, generators.constructed().size());
assertEquals(expectedSeed, worlds.getFirst().getRawWorldSeed());
assertEquals(expectedSeed, created.seed());
assertEquals(expectedSeed, request.seed());
}
}
}