Filtered visible biome locate

- /locate biome on non-stacked Iris dimensions searches the visible biomes chunks are filled with instead of falling back to the vanilla structure-biome search
- BiomeSearchFilter rejects columns whose region, land, sea, shore, cave, carving, subterrain or image-map biomes cannot hold a target, so terrain height is computed only for columns that can match
- Hydrology planning state is consulted only when a target comes from a river policy, so a locate no longer queues hydrology tile plans across its whole radius
- Admitted samples resolve inside their generation-history coordinate scope, so saved-biome reads for generated chunks no longer fail the command
- HintedLocator surface hints and IrisBiome visible key enumeration are shared with the locate filter
- Bukkit and modded biome policies share the same filtered search
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-15 19:04:45 -04:00
1 parent e1049949c6
commit 257c7d4f87
10 files changed
+960 -85

No files matched your search

@@ -42,6 +42,7 @@ import art.arcane.iris.generation.biome.IrisBiome;
import art.arcane.iris.generation.biome.IrisBiomeCustom;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.context.IrisContext;
import art.arcane.iris.generation.locator.BiomeSearchFilter;
import art.arcane.volmlib.util.math.RNG;
import java.io.IOException;
@@ -300,10 +301,6 @@ final class ModdedBiomePolicy<H, S> implements NativeModdedBiomePolicy<H, S> {
if (!isReady(engine)) {
throw new IllegalStateException("Iris visible biome search has no active engine runtime");
}
DimensionStackContext stackContext = engine.getDimensionStackContext();
if (stackContext == null) {
return query.defaultFallback().get();
}
Set<H> candidates = new HashSet<>();
for (H biome : collectVisibleBiomeHolders()) {
if (query.allowed().test(biome)) {
@@ -313,16 +310,86 @@ final class ModdedBiomePolicy<H, S> implements NativeModdedBiomePolicy<H, S> {
if (candidates.isEmpty()) {
return null;
}
DimensionStackContext stackContext = engine.getDimensionStackContext();
if (stackContext != null) {
return query.search().search(candidates, (blockX, blockZ) -> {
int quartX = blockX >> 2;
int quartZ = blockZ >> 2;
DimensionStackLayout stackLayout = stackContext.sample(quartX << 2, quartZ << 2);
return blockY -> resolveVisibleBiome(engine, quartX, blockY >> 2, quartZ,
query.sampler(), stackLayout);
});
}
BiomeSearchFilter filter = BiomeSearchFilter.of(engine, searchBiomeKeys(engine, candidates));
int minHeight = engine.getMinHeight();
return query.search().search(candidates, (blockX, blockZ) -> {
int quartX = blockX >> 2;
int quartZ = blockZ >> 2;
DimensionStackLayout stackLayout = stackContext.sample(quartX << 2, quartZ << 2);
return blockY -> resolveVisibleBiome(engine, quartX, blockY >> 2, quartZ,
query.sampler(), stackLayout);
BiomeSearchFilter.Column column = filter.column(quartX << 2, quartZ << 2);
return blockY -> column.admits(((blockY >> 2) << 2) - minHeight)
? locateVisibleBiome(engine, quartX, blockY >> 2, quartZ, query.sampler())
: null;
});
}
}
private H locateVisibleBiome(Engine engine, int quartX, int quartY, int quartZ, S sampler) {
try (GenerationHistoryRuntimeRouter.CoordinateScope ignored = openHistoryCoordinateScope(
engine, quartX << 2, quartZ << 2, "modded_locate_visible_biome")) {
return resolveVisibleBiome(engine, quartX, quartY, quartZ, sampler, null);
}
}
private Set<String> searchBiomeKeys(Engine engine, Set<H> candidates) {
NativeBiomeSourceAccess.RegistryView<H> registry = biomeRegistry();
if (registry == null) {
throw new IllegalStateException("Iris visible biome search has no biome registry");
}
H plains = resolveHolder(registry, "minecraft:plains");
boolean fallbackCandidate = plains != null && candidates.contains(plains);
Set<String> keys = new HashSet<>();
for (OwnedBiome ownedBiome : getOwnedBiomes(engine)) {
if (emitsCandidate(engine, ownedBiome, registry, candidates, fallbackCandidate)) {
keys.add(ownedBiome.biome().getLoadKey());
}
}
return keys;
}
private boolean emitsCandidate(Engine engine, OwnedBiome ownedBiome, NativeBiomeSourceAccess.RegistryView<H> registry,
Set<H> candidates, boolean fallbackCandidate) {
IrisBiome irisBiome = ownedBiome.biome();
if (irisBiome.isCustom()) {
for (IrisBiomeCustom customBiome : irisBiome.getCustomDerivitives()) {
if (customBiome == null) {
if (fallbackCandidate) {
return true;
}
continue;
}
String key = ownedBiome.dimension() == engine.getDimension() && ownedBiome.data() == engine.getData()
? ModdedWorldgenIds.biomeRef(engine, customBiome.getId())
: ownedBiome.data().customBiomeResourceKey(ownedBiome.dimension(), customBiome);
if (emits(registry, key, candidates, fallbackCandidate)) {
return true;
}
}
return false;
}
for (String key : irisBiome.getVisibleBiomeKeys(engine)) {
if (emits(registry, key, candidates, fallbackCandidate)) {
return true;
}
}
return false;
}
private static <H> boolean emits(NativeBiomeSourceAccess.RegistryView<H> registry, String key,
Set<H> candidates, boolean fallbackCandidate) {
H holder = key == null ? null : resolveHolder(registry, key);
return holder == null ? fallbackCandidate : candidates.contains(holder);
}
private H getNoiseBiome(Engine engine, int quartX, int quartY, int quartZ,
S sampler) {
if (isGuaranteedSurfaceBiome(engine, quartY)) {
@@ -6,11 +6,17 @@ import art.arcane.iris.generation.runtime.DimensionStackLayout;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.subterrain.IrisSubterrainFeature;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.pack.loading.ResourceLoader;
import art.arcane.iris.generation.terrain.IrisRegion;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.iris.generation.runtime.GenerationSessionException;
import art.arcane.iris.generation.runtime.GenerationSessionLease;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.generation.runtime.IrisEngine;
import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter;
import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourceAccess;
import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourcePolicy;
import art.arcane.volmlib.nativelib.terrain.NativeModdedBiomePolicy;
import art.arcane.volmlib.util.stream.ProceduralStream;
import art.arcane.volmlib.util.stream.interpolation.Interpolated;
import org.junit.Test;
@@ -20,7 +26,9 @@ import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
@@ -29,8 +37,12 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
@@ -140,6 +152,92 @@ public class ModdedBiomePolicyTest {
assertThrows(IllegalStateException.class, () -> boundTo(unready).getVisibleSurfaceBiome(8, 8));
}
/**
* Vanilla /locate biome samples a 6400 block radius every 32 blocks. Each Iris surface sample needs the column's
* terrain height, so the search may only resolve columns whose region and biome streams can hold the target, and
* it must answer with the visible biome a chunk is filled with.
*/
@Test
public void locateResolvesVisibleBiomesOnlyInColumnsThatCanHoldTheTarget() throws Exception {
BiomeToken mirage = new BiomeToken("terralith:mirage_isles");
BiomeToken plains = new BiomeToken("minecraft:plains");
IrisBiome isles = new IrisBiome().setDerivative("terralith:mirage_isles");
isles.setLoadKey("isles");
IrisBiome meadow = new IrisBiome().setDerivative("minecraft:plains");
meadow.setLoadKey("meadow");
IrisRegion main = new IrisRegion().setLandBiomes(new KList<>("meadow"));
main.setLoadKey("main");
IrisRegion archipelago = new IrisRegion().setLandBiomes(new KList<>("isles"));
archipelago.setLoadKey("archipelago");
IrisDimension dimension = mock(IrisDimension.class);
when(dimension.getReachableBiomes(any())).thenReturn(new KList<>(meadow, isles));
when(dimension.getAllRegions(any())).thenReturn(new KList<>(main, archipelago));
when(dimension.getCarving()).thenReturn(new KList<>());
when(dimension.getSubterrainFeatures()).thenReturn(new KList<>());
@SuppressWarnings("unchecked")
ResourceLoader<IrisBiome> biomes = (ResourceLoader<IrisBiome>) mock(ResourceLoader.class);
when(biomes.load("meadow")).thenReturn(meadow);
when(biomes.load("isles")).thenReturn(isles);
IrisData data = mock(IrisData.class);
when(data.getDataFolder()).thenReturn(new File("synthetic-pack"));
when(data.getBiomeLoader()).thenReturn(biomes);
when(data.physicalBiomeResourceKey(any(), anyString())).thenAnswer(invocation -> invocation.getArgument(1));
IrisComplex complex = mock(IrisComplex.class);
when(complex.getRegionStream()).thenReturn(ProceduralStream.of(
(x, z) -> x >= 4096D ? archipelago : main, Interpolated.of(value -> 0D, value -> main)));
when(complex.getLandBiomeStream()).thenReturn(ProceduralStream.of(
(x, z) -> x >= 4096D ? isles : meadow, Interpolated.of(value -> 0D, value -> meadow)));
when(complex.getSeaBiomeStream()).thenReturn(ProceduralStream.of(
(x, z) -> meadow, Interpolated.of(value -> 0D, value -> meadow)));
when(complex.getShoreBiomeStream()).thenReturn(ProceduralStream.of(
(x, z) -> meadow, Interpolated.of(value -> 0D, value -> meadow)));
when(complex.historicalPhysicalBiomeKeyAt(anyInt(), anyInt(), anyInt())).thenReturn(Optional.empty());
GenerationHistoryRuntimeRouter router = mock(GenerationHistoryRuntimeRouter.class);
GenerationHistoryRuntimeRouter.CoordinateScope scope = mock(GenerationHistoryRuntimeRouter.CoordinateScope.class);
when(router.openCoordinateScope(anyInt(), anyInt())).thenReturn(scope);
IrisEngine engine = mock(IrisEngine.class);
when(engine.getGenerationHistoryRuntimeRouter()).thenReturn(Optional.of(router));
when(engine.getDimension()).thenReturn(dimension);
when(engine.getData()).thenReturn(data);
when(engine.getComplex()).thenReturn(complex);
when(engine.getMinHeight()).thenReturn(-64);
when(engine.getCacheID()).thenReturn(1);
when(engine.acquireGenerationLease(anyString())).thenReturn(GenerationSessionLease.noop());
when(engine.answersFromNaturalTerrain(anyInt(), anyInt())).thenReturn(true);
AtomicInteger heightSamples = new AtomicInteger();
when(engine.getHostSurfaceBiome(anyInt(), anyInt())).thenAnswer(invocation -> {
heightSamples.incrementAndGet();
return invocation.<Integer>getArgument(0) >= 4096 ? isles : meadow;
});
ModdedBiomePolicy<BiomeToken, Object> 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));
int[] columns = {0, 1024, 2048, 3072, 4096, 5120};
NativeBiomeSourcePolicy.BiomeLocation<BiomeToken> found = policy.findClosestBiome3d(
new NativeModdedBiomePolicy.ClosestQuery<>(mirage::equals, null,
() -> {
throw new AssertionError("A bound engine must not use the unbound search");
},
(candidates, resolver) -> {
for (int x : columns) {
NativeBiomeSourcePolicy.Column<BiomeToken> column = resolver.resolve(x, 0);
BiomeToken biome = column.sample(100);
if (candidates.contains(biome)) {
return new NativeBiomeSourcePolicy.BiomeLocation<>(x, 100, 0, biome);
}
}
return null;
}));
assertEquals(new NativeBiomeSourcePolicy.BiomeLocation<>(4096, 100, 0, mirage), found);
assertEquals(1, heightSamples.get());
verify(router).openCoordinateScope(4096, 0);
verify(router, times(1)).openCoordinateScope(anyInt(), anyInt());
verify(scope).close();
}
private static ModdedBiomePolicy<BiomeToken, Object> boundTo(Engine engine) {
ModdedBiomePolicy<BiomeToken, Object> policy = new ModdedBiomePolicy<>(new Access(new ArrayList<>()));
policy.bind(new ModdedBiomePolicy.RuntimeCallbacks(() -> engine, () -> engine, bound -> true,