Remove Hydrology

This commit is contained in:
Brian Neumann-Fopiano
2026-08-27 00:45:15 -04:00
parent bdaa84975a
commit f398c55091
188 changed files with 1291 additions and 21010 deletions
@@ -62,7 +62,6 @@ public class CustomBiomeSource extends BiomeSource {
private final ConcurrentHashMap<Long, Holder<Biome>> noiseBiomeCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, Holder<Biome>> structureBiomeCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, Holder<Biome>> surfaceStructureBiomeCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Long, Holder<Biome>> naturalSurfaceStructureBiomeCache = new ConcurrentHashMap<>();
private volatile KMap<String, Holder<Biome>> customBiomes;
private volatile Map<Biome, Holder<Biome>> vanillaSpawnBiomes;
private volatile IrisDimension cacheDimension;
@@ -332,18 +331,8 @@ public class CustomBiomeSource extends BiomeSource {
if (quartStep == 1) {
return super.findBiomeHorizontal(x, y, z, searchRadius, allowed, random, sampler);
}
GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_structure_ring_biome");
if (lease == null) {
throw new IllegalStateException("Iris structure ring biome lookup was rejected during an engine transition");
}
try (lease; IrisContext.Scope ignored = IrisContext.open(engine, lease.sessionId(), null)) {
if (!isRuntimeAvailable()) {
throw new IllegalStateException("Iris structure ring biome lookup has no active engine runtime");
}
ensureCachesCurrent();
return findNaturalSurfaceBiomeHorizontal(
x, y, z, searchRadius, quartStep, allowed, random);
}
return super.findBiomeHorizontal(
x, y, z, searchRadius, quartStep, allowed, random, false, sampler);
}
static int horizontalBiomeSearchQuartStep(int blockY, int searchRadius) {
@@ -401,60 +390,6 @@ public class CustomBiomeSource extends BiomeSource {
return resolvedSurfaceHolder;
}
private Pair<BlockPos, Holder<Biome>> findNaturalSurfaceBiomeHorizontal(
int x,
int y,
int z,
int searchRadius,
int quartStep,
Predicate<Holder<Biome>> allowed,
RandomSource random
) {
int centerQuartX = QuartPos.fromBlock(x);
int centerQuartZ = QuartPos.fromBlock(z);
int quartRadius = QuartPos.fromBlock(searchRadius);
Pair<BlockPos, Holder<Biome>> selected = null;
int matches = 0;
for (int radius = 0; radius <= quartRadius; radius += quartStep) {
for (int offsetZ = -radius; offsetZ <= radius; offsetZ += quartStep) {
for (int offsetX = -radius; offsetX <= radius; offsetX += quartStep) {
int quartX = centerQuartX + offsetX;
int quartZ = centerQuartZ + offsetZ;
Holder<Biome> holder = getNaturalSurfaceStructureBiomeHolder(quartX, quartZ);
if (!allowed.test(holder)) {
continue;
}
if (selected == null || random.nextInt(matches + 1) == 0) {
selected = Pair.of(new BlockPos(
QuartPos.toBlock(quartX),
y,
QuartPos.toBlock(quartZ)), holder);
}
matches++;
}
}
}
return selected;
}
private Holder<Biome> getNaturalSurfaceStructureBiomeHolder(int x, int z) {
long columnKey = packColumnKey(x, z);
Holder<Biome> cachedHolder = naturalSurfaceStructureBiomeCache.get(columnKey);
if (cachedHolder != null) {
return cachedHolder;
}
Holder<Biome> resolvedHolder = resolveNaturalSurfaceStructureBiomeHolder(x, z);
Holder<Biome> existingHolder = naturalSurfaceStructureBiomeCache.putIfAbsent(
columnKey, resolvedHolder);
if (existingHolder != null) {
return existingHolder;
}
if (naturalSurfaceStructureBiomeCache.size() > NOISE_BIOME_CACHE_MAX) {
naturalSurfaceStructureBiomeCache.clear();
}
return resolvedHolder;
}
private boolean isGuaranteedSurfaceBiome(int quartY) {
if (engine == null || engine.isClosed() || engine.getComplex() == null) {
return false;
@@ -482,23 +417,6 @@ public class CustomBiomeSource extends BiomeSource {
return holder;
}
private Holder<Biome> resolveNaturalSurfaceStructureBiomeHolder(int x, int z) {
int blockX = x << 2;
int blockZ = z << 2;
IrisBiome irisBiome = engine.getComplex().getNaturalTrueBiomeStream().get(blockX, blockZ);
if (irisBiome == null) {
throw new IllegalStateException("Iris returned no natural structure biome at block "
+ blockX + "," + blockZ);
}
Holder<Biome> holder = resolveBiomeHolder(biomeRegistry, irisBiome.getStructureDerivativeKey());
if (holder == null) {
throw new IllegalStateException("Iris natural structure biome derivative '"
+ irisBiome.getStructureDerivativeKey() + "' is not registered at block "
+ blockX + "," + blockZ);
}
return holder;
}
public Holder<Biome> getVisibleNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
GenerationSessionLease lease = tryAcquireGenerationLease("bukkit_visible_biome");
if (lease == null) {
@@ -588,7 +506,6 @@ public class CustomBiomeSource extends BiomeSource {
noiseBiomeCache.clear();
structureBiomeCache.clear();
surfaceStructureBiomeCache.clear();
naturalSurfaceStructureBiomeCache.clear();
customBiomes = refreshedCustomBiomes;
vanillaSpawnBiomes = refreshedSpawnBiomes;
cacheDimension = dimension;
@@ -45,36 +45,9 @@ public class CustomBiomeSourceStructureContractTest {
assertTrue(source.contains("private static final int STRONGHOLD_RING_SEARCH_Y = 0"));
assertTrue(source.contains("private static final int STRONGHOLD_RING_SEARCH_RADIUS = 112"));
assertTrue(source.contains("private static final int STRONGHOLD_RING_SEARCH_QUART_STEP = 4"));
assertTrue(source.contains("x, y, z, searchRadius, quartStep, allowed, random, false, sampler"));
assertTrue(source.contains(
"return super.findBiomeHorizontal(x, y, z, searchRadius, allowed, random, sampler)"));
assertTrue(source.contains("tryAcquireGenerationLease(\"bukkit_structure_ring_biome\")"));
assertTrue(source.contains("findNaturalSurfaceBiomeHorizontal("));
assertTrue(source.contains("radius += quartStep"));
assertTrue(source.contains("offsetZ += quartStep"));
assertTrue(source.contains("offsetX += quartStep"));
assertTrue(source.contains("random.nextInt(matches + 1) == 0"));
}
@Test
public void onlyConcentricRingSuitabilityUsesTheNaturalTerrainStream() throws IOException {
String source = Files.readString(Path.of(System.getProperty("iris.customBiomeSource")));
int realResolutionStart = source.indexOf("private Holder<Biome> resolveSurfaceStructureBiomeHolder(");
int naturalResolutionStart = source.indexOf(
"private Holder<Biome> resolveNaturalSurfaceStructureBiomeHolder(");
int resolutionEnd = source.indexOf("public Holder<Biome> getVisibleNoiseBiome(", naturalResolutionStart);
assertTrue(realResolutionStart >= 0);
assertTrue(naturalResolutionStart > realResolutionStart);
assertTrue(resolutionEnd > naturalResolutionStart);
String realResolution = source.substring(realResolutionStart, naturalResolutionStart);
String naturalResolution = source.substring(naturalResolutionStart, resolutionEnd);
assertTrue(realResolution.contains("engine.getComplex().getTrueBiomeStream().get(blockX, blockZ)"));
assertFalse(realResolution.contains("getNaturalTrueBiomeStream()"));
assertTrue(naturalResolution.contains(
"engine.getComplex().getNaturalTrueBiomeStream().get(blockX, blockZ)"));
assertFalse(naturalResolution.contains("getTrueBiomeStream()"));
assertFalse(source.contains("studioBootstrapSurfaceStructureBiomeCache"));
}
@Test
@@ -2,11 +2,6 @@ package art.arcane.iris.api.terrain;
public enum IrisColumnField {
SURFACE_HEIGHT,
NATURAL_HEIGHT,
SURFACE_KIND,
BIOME_KEY,
RIVER_STATE,
RIVER_DISTANCE,
RIVER_FLOW,
RIVER_WATER_SURFACE_Y
BIOME_KEY
}
@@ -1,64 +0,0 @@
package art.arcane.iris.api.terrain;
import java.util.Objects;
public record IrisColumnSample(
int blockX,
int blockZ,
int surfaceHeight,
int naturalHeight,
IrisSurfaceKind surfaceKind,
String biomeKey,
IrisRiverState riverState,
double riverDistance,
int riverFlow,
int riverWaterSurfaceY
) {
public static final int UNAVAILABLE_HEIGHT = Integer.MIN_VALUE;
public static final double UNAVAILABLE_RIVER_DISTANCE = Double.NaN;
public static final int UNAVAILABLE_RIVER_FLOW = -1;
public IrisColumnSample {
Objects.requireNonNull(surfaceKind, "surfaceKind");
Objects.requireNonNull(riverState, "riverState");
biomeKey = biomeKey == null || biomeKey.isBlank() ? null : biomeKey;
if (!Double.isNaN(riverDistance) && (!Double.isFinite(riverDistance) || riverDistance < 0D)) {
throw new IllegalArgumentException("riverDistance must be non-negative, finite, or unavailable");
}
if (riverFlow < UNAVAILABLE_RIVER_FLOW) {
throw new IllegalArgumentException("riverFlow must be non-negative or unavailable");
}
}
public boolean hasSurfaceHeight() {
return surfaceHeight != UNAVAILABLE_HEIGHT;
}
public boolean hasNaturalHeight() {
return naturalHeight != UNAVAILABLE_HEIGHT;
}
public boolean hasSurfaceKind() {
return surfaceKind != IrisSurfaceKind.UNKNOWN;
}
public boolean hasBiomeKey() {
return biomeKey != null;
}
public boolean hasRiverState() {
return riverState != IrisRiverState.NONE;
}
public boolean hasRiverDistance() {
return !Double.isNaN(riverDistance);
}
public boolean hasRiverFlow() {
return riverFlow != UNAVAILABLE_RIVER_FLOW;
}
public boolean hasRiverWaterSurfaceY() {
return riverWaterSurfaceY != UNAVAILABLE_HEIGHT;
}
}
@@ -2,5 +2,5 @@ package art.arcane.iris.api.terrain;
@FunctionalInterface
public interface IrisColumnSink {
void accept(IrisColumnSample sample);
void accept(int blockX, int blockZ, int surfaceHeight, IrisSurfaceKind kind, String biomeKey);
}
@@ -1,7 +0,0 @@
package art.arcane.iris.api.terrain;
public enum IrisRiverState {
NONE,
WET,
DRY
}
@@ -4,9 +4,6 @@ public enum IrisSurfaceKind {
UNKNOWN,
LAND,
SHORE,
RIVER,
RIVER_SHORE,
DRY_CHANNEL,
OCEAN,
VOID
}
@@ -318,7 +318,7 @@ public final class BukkitVisionOverlay implements GuiOverlay {
public String openInEditor(double worldX, double worldZ, RenderType type) {
IrisComplex complex = engine.getComplex();
File file = switch (type) {
case BIOME, LAYER_LOAD, DECORATOR_LOAD, OBJECT_LOAD, HEIGHT, RIVER ->
case BIOME, LAYER_LOAD, DECORATOR_LOAD, OBJECT_LOAD, HEIGHT ->
complex.getTrueBiomeStream().get(worldX, worldZ).openInVSCode();
case BIOME_LAND -> complex.getLandBiomeStream().get(worldX, worldZ).openInVSCode();
case BIOME_SEA -> complex.getSeaBiomeStream().get(worldX, worldZ).openInVSCode();
@@ -2,9 +2,7 @@ package art.arcane.iris.core.service;
import art.arcane.iris.api.terrain.IrisColumnField;
import art.arcane.iris.api.terrain.IrisColumnQuery;
import art.arcane.iris.api.terrain.IrisColumnSample;
import art.arcane.iris.api.terrain.IrisColumnSink;
import art.arcane.iris.api.terrain.IrisRiverState;
import art.arcane.iris.api.terrain.IrisSurfaceKind;
import art.arcane.iris.api.terrain.IrisTerrainService;
import art.arcane.iris.api.terrain.IrisWorldInfo;
@@ -19,8 +17,6 @@ import art.arcane.iris.engine.object.InferredType;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
import art.arcane.iris.engine.river.RiverRouteState;
import art.arcane.iris.engine.river.runtime.IrisRiverSurfaceSample;
import art.arcane.iris.platform.bukkit.BukkitPlatform;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.spi.IrisServices;
@@ -105,14 +101,13 @@ public class IrisTerrainSVC implements IrisService, IrisTerrainService {
try {
int surface = engine.getHeight(blockX, blockZ);
IrisRiverSurfaceSample riverSurface = engine.getComplex().getRiverSurfaceStream().get(blockX, blockZ);
int fluid = (int) Math.round(riverSurface.waterSurfaceY());
int fluid = engine.getDimension().getFluidHeight();
InferredType inferredType = null;
if (IrisSurfaceClassifier.requiresSurfaceBiome(surface, fluid)) {
IrisBiome biome = engine.getSurfaceBiome(blockX, blockZ);
inferredType = biome == null ? null : biome.getInferredType();
}
return IrisSurfaceClassifier.classify(surface, fluid, inferredType, riverSurface);
return IrisSurfaceClassifier.classify(surface, fluid, inferredType);
} catch (Throwable error) {
reportQueryFault("surfaceKind", world, error);
return IrisSurfaceKind.UNKNOWN;
@@ -229,72 +224,26 @@ public class IrisTerrainSVC implements IrisService, IrisTerrainService {
EnumSet<IrisColumnField> fields = query.fields();
boolean wantHeight = fields.contains(IrisColumnField.SURFACE_HEIGHT);
boolean wantNaturalHeight = fields.contains(IrisColumnField.NATURAL_HEIGHT);
boolean wantKind = fields.contains(IrisColumnField.SURFACE_KIND);
boolean wantBiome = fields.contains(IrisColumnField.BIOME_KEY);
boolean wantRiverState = fields.contains(IrisColumnField.RIVER_STATE);
boolean wantRiverDistance = fields.contains(IrisColumnField.RIVER_DISTANCE);
boolean wantRiverFlow = fields.contains(IrisColumnField.RIVER_FLOW);
boolean wantRiverWaterSurface = fields.contains(IrisColumnField.RIVER_WATER_SURFACE_Y);
boolean wantRiver = wantKind || wantRiverState || wantRiverDistance || wantRiverFlow
|| wantRiverWaterSurface;
try {
int minHeight = engine.getMinHeight();
int fluid = engine.getDimension().getFluidHeight();
long visited = IrisColumnWalk.walk(query, (int blockX, int blockZ) -> {
if (engine.isClosed()) {
return false;
}
IrisRiverSurfaceSample riverSurface = wantRiver
? engine.getComplex().getRiverSurfaceStream().get(blockX, blockZ)
: null;
int surface = wantHeight || wantKind ? engine.getHeight(blockX, blockZ) : 0;
int fluid = riverSurface == null
? engine.getDimension().getFluidHeight()
: (int) Math.round(riverSurface.waterSurfaceY());
boolean needsBiome = wantBiome
|| (wantKind && IrisSurfaceClassifier.requiresSurfaceBiome(surface, fluid));
IrisBiome biome = needsBiome ? engine.getSurfaceBiome(blockX, blockZ) : null;
IrisSurfaceKind kind = wantKind
? IrisSurfaceClassifier.classify(
surface,
fluid,
biome == null ? null : biome.getInferredType(),
riverSurface
)
? IrisSurfaceClassifier.classify(surface, fluid, biome == null ? null : biome.getInferredType())
: IrisSurfaceKind.UNKNOWN;
String biomeKey = wantBiome && biome != null ? biome.getLoadKey() : null;
int natural = wantNaturalHeight
? (int) Math.round(engine.getComplex().getNaturalHeightStream().get(blockX, blockZ)) + minHeight
: IrisColumnSample.UNAVAILABLE_HEIGHT;
boolean riverPresent = riverSurface != null && riverSurface.river().present();
IrisRiverState riverState = wantRiverState && riverSurface != null
? riverState(riverSurface)
: IrisRiverState.NONE;
double riverDistance = wantRiverDistance && riverPresent
? riverSurface.river().distance()
: IrisColumnSample.UNAVAILABLE_RIVER_DISTANCE;
int riverFlow = wantRiverFlow && riverPresent
? riverSurface.river().flow()
: IrisColumnSample.UNAVAILABLE_RIVER_FLOW;
int riverWaterSurfaceY = wantRiverWaterSurface
&& riverPresent
&& riverSurface.river().state() == RiverRouteState.WET
? fluid + minHeight
: IrisColumnSample.UNAVAILABLE_HEIGHT;
sink.accept(new IrisColumnSample(
blockX,
blockZ,
wantHeight ? surface + minHeight : IrisColumnSample.UNAVAILABLE_HEIGHT,
natural,
kind,
biomeKey,
riverState,
riverDistance,
riverFlow,
riverWaterSurfaceY
));
sink.accept(blockX, blockZ, wantHeight ? surface + minHeight : -1, kind, biomeKey);
return true;
});
return visited == query.columnCount();
@@ -304,17 +253,6 @@ public class IrisTerrainSVC implements IrisService, IrisTerrainService {
}
}
static IrisRiverState riverState(IrisRiverSurfaceSample surface) {
if (!surface.river().present()) {
return IrisRiverState.NONE;
}
return switch (surface.river().state()) {
case WET -> IrisRiverState.WET;
case DRY -> IrisRiverState.DRY;
case SUPPRESSED -> IrisRiverState.NONE;
};
}
private static Optional<String> key(IrisBiome biome) {
String loadKey = biome == null ? null : biome.getLoadKey();
return loadKey == null || loadKey.isEmpty() ? Optional.empty() : Optional.of(loadKey);
@@ -2,9 +2,6 @@ package art.arcane.iris.core.service.terrain;
import art.arcane.iris.api.terrain.IrisSurfaceKind;
import art.arcane.iris.engine.object.InferredType;
import art.arcane.iris.engine.river.RiverRouteState;
import art.arcane.iris.engine.river.RiverSection;
import art.arcane.iris.engine.river.runtime.IrisRiverSurfaceSample;
public final class IrisSurfaceClassifier {
private IrisSurfaceClassifier() {
@@ -25,32 +22,4 @@ public final class IrisSurfaceClassifier {
return inferredType == InferredType.SHORE ? IrisSurfaceKind.SHORE : IrisSurfaceKind.LAND;
}
public static IrisSurfaceKind classify(
int engineSurfaceHeight,
int engineFluidHeight,
InferredType inferredType,
IrisRiverSurfaceSample riverSurface
) {
if (engineSurfaceHeight <= 0) {
return IrisSurfaceKind.VOID;
}
if (riverSurface != null && riverSurface.river().present() && !riverSurface.subterranean()) {
if (riverSurface.river().state() == RiverRouteState.DRY) {
return riverSurface.river().section() == RiverSection.DRY_CHANNEL
? IrisSurfaceKind.DRY_CHANNEL
: IrisSurfaceKind.LAND;
}
if (riverSurface.river().state() == RiverRouteState.WET) {
RiverSection section = riverSurface.river().section();
if (section == RiverSection.BANK) {
return IrisSurfaceKind.RIVER_SHORE;
}
if (section == RiverSection.CHANNEL || section == RiverSection.MOUTH) {
return IrisSurfaceKind.RIVER;
}
}
}
return classify(engineSurfaceHeight, engineFluidHeight, inferredType);
}
}
@@ -1,145 +0,0 @@
package art.arcane.iris.api.terrain;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class IrisColumnSampleTest {
@Test
public void unavailableFieldsHaveUnambiguousSentinels() {
IrisColumnSample sample = sample(
IrisColumnSample.UNAVAILABLE_HEIGHT,
IrisColumnSample.UNAVAILABLE_HEIGHT,
IrisSurfaceKind.UNKNOWN,
null,
IrisRiverState.NONE,
IrisColumnSample.UNAVAILABLE_RIVER_DISTANCE,
IrisColumnSample.UNAVAILABLE_RIVER_FLOW,
IrisColumnSample.UNAVAILABLE_HEIGHT
);
assertFalse(sample.hasSurfaceHeight());
assertFalse(sample.hasNaturalHeight());
assertFalse(sample.hasSurfaceKind());
assertFalse(sample.hasBiomeKey());
assertFalse(sample.hasRiverState());
assertFalse(sample.hasRiverDistance());
assertFalse(sample.hasRiverFlow());
assertFalse(sample.hasRiverWaterSurfaceY());
}
@Test
public void negativeWorldHeightsRemainAvailableValues() {
IrisColumnSample sample = sample(
-1,
-64,
IrisSurfaceKind.DRY_CHANNEL,
"test:river",
IrisRiverState.DRY,
0D,
0,
-1
);
assertTrue(sample.hasSurfaceHeight());
assertTrue(sample.hasNaturalHeight());
assertTrue(sample.hasSurfaceKind());
assertTrue(sample.hasBiomeKey());
assertTrue(sample.hasRiverState());
assertTrue(sample.hasRiverDistance());
assertTrue(sample.hasRiverFlow());
assertTrue(sample.hasRiverWaterSurfaceY());
}
@Test
public void blankBiomeKeysNormalizeToUnavailable() {
IrisColumnSample sample = sample(
64,
65,
IrisSurfaceKind.LAND,
" ",
IrisRiverState.NONE,
IrisColumnSample.UNAVAILABLE_RIVER_DISTANCE,
IrisColumnSample.UNAVAILABLE_RIVER_FLOW,
IrisColumnSample.UNAVAILABLE_HEIGHT
);
assertNull(sample.biomeKey());
assertFalse(sample.hasBiomeKey());
}
@Test
public void theSinkReceivesTheTypedSample() {
IrisColumnSample sample = sample(
64,
65,
IrisSurfaceKind.RIVER,
"test:river",
IrisRiverState.WET,
0.5D,
3,
67
);
AtomicReference<IrisColumnSample> received = new AtomicReference<>();
IrisColumnSink sink = received::set;
sink.accept(sample);
assertSame(sample, received.get());
}
@Test
public void invalidHydrologyValuesAreRejected() {
assertInvalid(Double.POSITIVE_INFINITY, 1);
assertInvalid(-0.1D, 1);
assertInvalid(0D, -2);
}
private static void assertInvalid(double distance, int flow) {
try {
sample(
64,
65,
IrisSurfaceKind.RIVER,
"test:river",
IrisRiverState.WET,
distance,
flow,
67
);
fail("Expected invalid hydrology values to be rejected");
} catch (IllegalArgumentException expected) {
assertTrue(expected.getMessage().startsWith("river"));
}
}
private static IrisColumnSample sample(
int surfaceHeight,
int naturalHeight,
IrisSurfaceKind surfaceKind,
String biomeKey,
IrisRiverState riverState,
double riverDistance,
int riverFlow,
int riverWaterSurfaceY
) {
return new IrisColumnSample(
12,
-7,
surfaceHeight,
naturalHeight,
surfaceKind,
biomeKey,
riverState,
riverDistance,
riverFlow,
riverWaterSurfaceY
);
}
}
@@ -1,10 +1,8 @@
package art.arcane.iris.core.commands;
import art.arcane.iris.engine.IrisComplex;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.IObjectPlacer;
import art.arcane.iris.engine.object.IrisDimension;
import art.arcane.iris.util.project.stream.ProceduralStream;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
@@ -15,31 +13,22 @@ import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class CommandObjectFluidHeightTest {
@Test
public void absoluteObjectPlacementShiftsTheColumnRiverHeadFromNegativeMinY() {
public void absoluteObjectPlacementShiftsFluidHeightFromNegativeMinY() {
World world = mock(World.class);
Engine engine = mock(Engine.class);
IrisDimension dimension = mock(IrisDimension.class);
IrisComplex complex = mock(IrisComplex.class);
@SuppressWarnings("unchecked")
ProceduralStream<Double> riverHead = mock(ProceduralStream.class);
Map<Block, BlockData> future = new HashMap<>();
when(engine.getMinHeight()).thenReturn(-64);
when(engine.getDimension()).thenReturn(dimension);
when(engine.getComplex()).thenReturn(complex);
when(dimension.getFluidHeight()).thenReturn(127);
when(complex.getRiverWaterSurfaceStream()).thenReturn(riverHead);
when(riverHead.get(12, -7)).thenReturn(131D);
IObjectPlacer placer = CommandObject.createPlacer(world, future, engine);
assertEquals(63, placer.getFluidHeight());
assertEquals(67, placer.getFluidHeight(12, -7));
verify(riverHead).get(12, -7);
}
}
@@ -2,15 +2,8 @@ package art.arcane.iris.core.service;
import art.arcane.iris.api.terrain.IrisColumnField;
import art.arcane.iris.api.terrain.IrisColumnQuery;
import art.arcane.iris.api.terrain.IrisRiverState;
import art.arcane.iris.api.terrain.IrisSurfaceKind;
import art.arcane.iris.api.terrain.IrisTerrainService;
import art.arcane.iris.engine.river.RiverEdgeId;
import art.arcane.iris.engine.river.RiverNodeId;
import art.arcane.iris.engine.river.RiverRouteState;
import art.arcane.iris.engine.river.RiverSample;
import art.arcane.iris.engine.river.RiverSection;
import art.arcane.iris.engine.river.runtime.IrisRiverSurfaceSample;
import art.arcane.iris.util.common.plugin.IrisService;
import org.bukkit.World;
import org.junit.Test;
@@ -89,7 +82,9 @@ public class IrisTerrainSVCTest {
IrisTerrainSVC service = new IrisTerrainSVC();
AtomicInteger sinkCalls = new AtomicInteger();
boolean answered = service.sampleColumns(null, SMALL, sample -> sinkCalls.incrementAndGet());
boolean answered = service.sampleColumns(null, SMALL,
(int blockX, int blockZ, int surfaceHeight, IrisSurfaceKind kind, String biomeKey)
-> sinkCalls.incrementAndGet());
assertFalse(answered);
assertEquals(0, sinkCalls.get());
@@ -102,38 +97,4 @@ public class IrisTerrainSVCTest {
assertFalse(service.sampleColumns(null, null, null));
assertFalse(service.sampleColumns(null, SMALL, null));
}
@Test
public void riverRouteStatesMapToThePublicDiagnosticStates() {
assertEquals(IrisRiverState.NONE, IrisTerrainSVC.riverState(
new IrisRiverSurfaceSample(RiverSample.none(), 70D, 70D, 70D, false, false)
));
assertEquals(IrisRiverState.WET, IrisTerrainSVC.riverState(river(RiverRouteState.WET)));
assertEquals(IrisRiverState.DRY, IrisTerrainSVC.riverState(river(RiverRouteState.DRY)));
assertEquals(IrisRiverState.NONE, IrisTerrainSVC.riverState(river(RiverRouteState.SUPPRESSED)));
}
private static IrisRiverSurfaceSample river(RiverRouteState state) {
RiverSection section = switch (state) {
case WET -> RiverSection.CHANNEL;
case DRY -> RiverSection.DRY_CHANNEL;
case SUPPRESSED -> RiverSection.NONE;
};
RiverSample sample = new RiverSample(
true,
state,
section,
0D,
0.5D,
1D,
1,
1,
8D,
4D,
3D,
false,
RiverEdgeId.of(new RiverNodeId(0, 0), new RiverNodeId(1, 0))
);
return new IrisRiverSurfaceSample(sample, 70D, 60D, 63D, false, state == RiverRouteState.WET);
}
}
@@ -2,12 +2,6 @@ package art.arcane.iris.core.service.terrain;
import art.arcane.iris.api.terrain.IrisSurfaceKind;
import art.arcane.iris.engine.object.InferredType;
import art.arcane.iris.engine.river.RiverEdgeId;
import art.arcane.iris.engine.river.RiverNodeId;
import art.arcane.iris.engine.river.RiverRouteState;
import art.arcane.iris.engine.river.RiverSample;
import art.arcane.iris.engine.river.RiverSection;
import art.arcane.iris.engine.river.runtime.IrisRiverSurfaceSample;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@@ -64,86 +58,4 @@ public class IrisSurfaceClassifierTest {
}
}
}
@Test
public void activeRiverGeometryOverridesGenericOceanAndLandKinds() {
assertEquals(IrisSurfaceKind.RIVER, IrisSurfaceClassifier.classify(
60,
63,
InferredType.SEA,
river(RiverRouteState.WET, RiverSection.CHANNEL)
));
assertEquals(IrisSurfaceKind.RIVER, IrisSurfaceClassifier.classify(
60,
63,
InferredType.SEA,
river(RiverRouteState.WET, RiverSection.MOUTH)
));
assertEquals(IrisSurfaceKind.RIVER_SHORE, IrisSurfaceClassifier.classify(
64,
63,
InferredType.SHORE,
river(RiverRouteState.WET, RiverSection.BANK)
));
assertEquals(IrisSurfaceKind.DRY_CHANNEL, IrisSurfaceClassifier.classify(
60,
60,
InferredType.LAND,
river(RiverRouteState.DRY, RiverSection.DRY_CHANNEL)
));
assertEquals(IrisSurfaceKind.LAND, IrisSurfaceClassifier.classify(
60,
60,
InferredType.LAND,
river(RiverRouteState.DRY, RiverSection.DRY_BANK)
));
}
@Test
public void voidClassificationWinsOverRiverGeometry() {
for (RiverSection section : RiverSection.values()) {
if (section == RiverSection.NONE) {
continue;
}
RiverRouteState state = section == RiverSection.DRY_CHANNEL || section == RiverSection.DRY_BANK
? RiverRouteState.DRY
: RiverRouteState.WET;
assertEquals(IrisSurfaceKind.VOID, IrisSurfaceClassifier.classify(
0,
63,
InferredType.LAND,
river(state, section)
));
}
}
@Test
public void suppressedRoutesDoNotCreatePublicRiverSurfaceKinds() {
assertEquals(IrisSurfaceKind.LAND, IrisSurfaceClassifier.classify(
64,
63,
InferredType.LAND,
river(RiverRouteState.SUPPRESSED, RiverSection.CHANNEL)
));
}
private static IrisRiverSurfaceSample river(RiverRouteState state, RiverSection section) {
RiverSample sample = new RiverSample(
true,
state,
section,
0D,
0.5D,
1D,
1,
1,
8D,
4D,
3D,
false,
RiverEdgeId.of(new RiverNodeId(0, 0), new RiverNodeId(1, 0))
);
double waterSurface = state == RiverRouteState.WET ? 63D : 60D;
return new IrisRiverSurfaceSample(sample, 70D, 60D, waterSurface, false, state == RiverRouteState.WET);
}
}
@@ -126,7 +126,7 @@ public final class ModdedVisionOverlay implements GuiOverlay {
}
IrisComplex complex = engine.getComplex();
File file = switch (type) {
case BIOME, LAYER_LOAD, DECORATOR_LOAD, OBJECT_LOAD, HEIGHT, RIVER ->
case BIOME, LAYER_LOAD, DECORATOR_LOAD, OBJECT_LOAD, HEIGHT ->
complex.getTrueBiomeStream().get(worldX, worldZ).openInVSCode();
case BIOME_LAND -> complex.getLandBiomeStream().get(worldX, worldZ).openInVSCode();
case BIOME_SEA -> complex.getSeaBiomeStream().get(worldX, worldZ).openInVSCode();