Platform glue diet

- Chunk-scoped visible biome resolver without global cache, per-quart RNG or repeated runtime lookups
- Subterrain probe skips per-sample subterrain lookups in chunks without candidates
- Striped structure volume caches
- Saved biome store reuses deflaters and remembers ready region files
- Semantic journal skips repeated storage and journal stat checks
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-22 06:40:12 -04:00
1 parent af90765a92
commit 0fe2363feb
12 files changed
+450 -115

No files matched your search

@@ -342,15 +342,24 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
@BlockCoordinates
default SubterrainCell getSubterrainCell(int x, int y, int z) {
if (getComplex() == null || getDimension() == null
|| !getDimension().allowsSubterrainFeatures()
|| getDimension().getSubterrainFeatures() == null || getDimension().getSubterrainFeatures().isEmpty()) {
IrisComplex complex = getComplex();
IrisDimension dimension = getDimension();
if (complex == null || dimension == null
|| !dimension.allowsSubterrainFeatures()
|| dimension.getSubterrainFeatures() == null || dimension.getSubterrainFeatures().isEmpty()) {
return SubterrainCell.OUTSIDE;
}
SubterrainPlanner planner = getComplex().getSubterrainPlanner();
SubterrainPlanner planner = complex.getSubterrainPlanner();
return planner == null ? SubterrainCell.OUTSIDE : planner.sample(x, y + getWorld().minHeight(), z);
}
@ChunkCoordinates
default boolean subterrainAbsentInChunk(int chunkX, int chunkZ) {
IrisComplex complex = getComplex();
SubterrainPlanner planner = complex == null ? null : complex.getSubterrainPlanner();
return planner == null || !planner.hasCandidates(chunkX, chunkZ);
}
@BlockCoordinates
default IrisBiome getSubterrainBiome(int x, int y, int z) {
SubterrainCell cell = getSubterrainCell(x, y, z);
@@ -410,6 +419,7 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
return;
}
IrisComplex complex = getComplex();
boolean subterrain = !subterrainAbsentInChunk(x >> 4, z >> 4);
int caveTop = getHeight(x, z) - 2;
IrisRegion region = getRegion(x, 0, z);
IrisBiome surface = null;
@@ -423,7 +433,7 @@ public interface Engine extends DataProvider, Fallible, BlockUpdater, Renderer,
for (int index = 0; index < biomes.length; index++) {
int y = index * step;
regions[index] = region;
IrisBiome featureBiome = getSubterrainBiome(x, y, z);
IrisBiome featureBiome = subterrain ? getSubterrainBiome(x, y, z) : null;
if (featureBiome != null) {
biomes[index] = featureBiome;
continue;
@@ -623,6 +623,15 @@ public class IrisEngine implements Engine {
return Engine.super.getSubterrainCell(x, y, z);
}
@ChunkCoordinates
@Override
public boolean subterrainAbsentInChunk(int chunkX, int chunkZ) {
if (!hasGenerationRuntimeScope() && generationHistoryRuntimeRouter != null) {
return false;
}
return Engine.super.subterrainAbsentInChunk(chunkX, chunkZ);
}
@BlockCoordinates
@Override
public IrisBiome getSubterrainBiome(int x, int y, int z) {
@@ -106,6 +106,10 @@ public final class SubterrainPlanner {
return List.copyOf(result);
}
public boolean hasCandidates(int chunkX, int chunkZ) {
return !isEmpty() && !candidates(chunkX, chunkZ).isEmpty();
}
public SubterrainCell sample(int x, int y, int z) {
if (isEmpty() || y < minY || y >= maxYExclusive) {
return SubterrainCell.OUTSIDE;
@@ -8,6 +8,7 @@ import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.generation.runtime.DimensionStackContext;
import art.arcane.iris.generation.runtime.DimensionStackLayout;
import art.arcane.iris.generation.runtime.DimensionTerrainContext;
import art.arcane.iris.generation.runtime.IrisComplex;
import art.arcane.iris.generation.runtime.IrisEngine;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.stream.ProvisionalSampling;
@@ -27,6 +28,7 @@ import org.bukkit.block.Biome;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
@@ -46,7 +48,7 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
private static final int STRONGHOLD_RING_SEARCH_RADIUS = 112;
private static final int STRONGHOLD_RING_SEARCH_QUART_STEP = 4;
private final long seed;
private final RNG biomeRng;
private final Engine engine;
private final BukkitChunkGenerator platformGenerator;
private final NativeBiomeRegistry<H, V> biomeCustomRegistry;
@@ -64,7 +66,7 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
this.engine = engine;
this.platformGenerator = options.platformGenerator();
this.possibleBiomeRegistry = options.possibleBiomeRegistry();
this.seed = options.seed();
this.biomeRng = new RNG(options.seed());
this.biomeCustomRegistry = registry;
this.biomeRegistry = biomeCustomRegistry;
this.fallbackBiome = resolveFallbackBiome(this.biomeRegistry, this.biomeCustomRegistry);
@@ -826,7 +828,7 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
int z,
IrisDimensionCarvingResolver.State resolverState
) {
return resolveVisibleBiomeHolder(x, y, z, resolverState, null);
return resolveVisibleBiomeHolder(x, y, z, resolverState, null, null);
}
private H resolveVisibleBiomeHolder(
@@ -835,32 +837,44 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
int z,
IrisDimensionCarvingResolver.State resolverState,
DimensionStackLayout stackLayout
) {
return resolveVisibleBiomeHolder(x, y, z, resolverState, stackLayout, null);
}
private H resolveVisibleBiomeHolder(
int x,
int y,
int z,
IrisDimensionCarvingResolver.State resolverState,
DimensionStackLayout stackLayout,
ChunkResolution chunk
) {
int blockX = x << 2;
int blockY = y << 2;
int blockZ = z << 2;
Optional<String> historicalKey = engine.getComplex().historicalPhysicalBiomeKeyAt(
IrisComplex complex = chunk == null ? engine.getComplex() : chunk.complex;
Optional<String> historicalKey = complex.historicalPhysicalBiomeKeyAt(
blockX, blockY, blockZ);
if (historicalKey.isPresent()) {
return resolvePhysicalBiomeHolder(historicalKey.get());
}
BiomeResolution resolution = resolveBiomeResolution(
x, y, z, resolverState, true, stackLayout);
x, y, z, resolverState, true, stackLayout, chunk);
if (resolution == null) {
return getFallbackBiome();
}
return resolveVisibleBiomeHolder(resolution);
return resolveVisibleBiomeHolder(resolution, chunk);
}
private H resolveVisibleBiomeHolder(BiomeResolution resolution) {
private H resolveVisibleBiomeHolder(BiomeResolution resolution, ChunkResolution chunk) {
if (resolution.irisBiome.isCustom()) {
return resolveCustomHolder(resolution);
return resolveCustomHolder(resolution, chunk);
}
Biome vanillaBiome = resolution.underground
? resolution.irisBiome.getGroundBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ)
: resolution.irisBiome.getSkyBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ);
? resolution.irisBiome.getGroundBiome(biomeRng, engine, resolution.blockX, resolution.blockY, resolution.blockZ)
: resolution.irisBiome.getSkyBiome(biomeRng, engine, resolution.blockX, resolution.blockY, resolution.blockZ);
H holder = biomeRegistry.lookup(vanillaBiome);
if (holder != null) {
return holder;
@@ -869,11 +883,12 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
return getFallbackBiome();
}
private H resolveCustomHolder(BiomeResolution resolution) {
IrisBiomeCustom customBiome = resolution.irisBiome.getCustomBiome(resolution.rng, engine, resolution.blockX, resolution.blockY, resolution.blockZ);
private H resolveCustomHolder(BiomeResolution resolution, ChunkResolution chunk) {
IrisBiomeCustom customBiome = resolution.irisBiome.getCustomBiome(biomeRng, engine, resolution.blockX, resolution.blockY, resolution.blockZ);
if (customBiome != null) {
H holder = runtimeBiomeState().customBiomes().get(customBiomeKey(
resolution.dimension, customBiome.getId()));
H holder = chunk == null
? runtimeBiomeState().customBiomes().get(customBiomeKey(resolution.dimension, customBiome.getId()))
: chunk.customHolder(resolution.dimension, customBiome);
if (holder != null) {
return holder;
}
@@ -883,7 +898,7 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
}
private BiomeResolution resolveStructureBiomeResolution(int x, int y, int z) {
return resolveBiomeResolution(x, y, z, null, false);
return resolveBiomeResolution(x, y, z, null, false, null, null);
}
private H resolvePhysicalBiomeHolder(String physicalKey) {
@@ -898,65 +913,50 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
return holder;
}
private BiomeResolution resolveBiomeResolution(
int x,
int y,
int z,
IrisDimensionCarvingResolver.State resolverState
) {
return resolveBiomeResolution(x, y, z, resolverState, true);
}
private BiomeResolution resolveBiomeResolution(
int x,
int y,
int z,
IrisDimensionCarvingResolver.State resolverState,
boolean includeDimensionStack
) {
return resolveBiomeResolution(
x, y, z, resolverState, includeDimensionStack, null);
}
private BiomeResolution resolveBiomeResolution(
int x,
int y,
int z,
IrisDimensionCarvingResolver.State resolverState,
boolean includeDimensionStack,
DimensionStackLayout stackLayout
DimensionStackLayout stackLayout,
ChunkResolution chunk
) {
if (engine == null || engine.isClosed()) {
return null;
}
if (engine.getComplex() == null) {
if ((chunk == null ? engine.getComplex() : chunk.complex) == null) {
return null;
}
int blockX = x << 2;
int blockZ = z << 2;
int blockY = y << 2;
int worldMinHeight = engine.getWorld().minHeight();
int worldMinHeight = chunk == null ? engine.getWorld().minHeight() : chunk.worldMinHeight;
int internalY = blockY - worldMinHeight;
int caveSwitchInternalY = Math.max(-8 - worldMinHeight, 40);
IrisBiome featureBiome = engine.getSubterrainBiome(blockX, internalY, blockZ);
IrisBiome featureBiome = chunk != null && !chunk.subterrain
? null : engine.getSubterrainBiome(blockX, internalY, blockZ);
if (featureBiome != null) {
return createBiomeResolution(featureBiome, true, engine.getDimension(), blockX, blockY, blockZ);
return new BiomeResolution(featureBiome, true, chunk == null ? engine.getDimension() : chunk.dimension,
blockX, blockY, blockZ);
}
DimensionStackLayout.Layer stackLayer = includeDimensionStack
? stackLayout == null
? resolveDimensionStackLayer(engine, blockX, internalY, blockZ)
? resolveDimensionStackLayer(
chunk == null ? engine.getDimensionStackContext() : chunk.stackContext,
blockX, internalY, blockZ)
: stackLayout.layerAt(internalY)
: null;
IrisDimension owningDimension = stackLayer == null
? engine.getDimension()
? chunk == null ? engine.getDimension() : chunk.dimension
: stackLayer.terrainContext().getDimension();
if (stackLayer != null && !stackLayer.terrainContext().isSelfReferencing()) {
IrisBiome stackedBiome = stackLayer.biome();
return stackedBiome == null
? null
: createBiomeResolution(
: new BiomeResolution(
stackedBiome, false, owningDimension, blockX, blockY, blockZ);
}
boolean deepUnderground = internalY <= caveSwitchInternalY;
@@ -976,41 +976,31 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
)
: stackLayer.biome();
} else if (deepUnderground) {
int surfaceInternalY = Engine.hostHeight(engine, blockX, blockZ, true);
int surfaceInternalY = chunk == null
? Engine.hostHeight(engine, blockX, blockZ, true)
: chunk.hostHeight(blockX, blockZ);
underground = internalY <= surfaceInternalY - 8;
irisBiome = underground
? engine.getCaveBiome(blockX, internalY, blockZ, resolverState)
: engine.getHostSurfaceBiome(blockX, blockZ);
: hostSurfaceBiome(chunk, blockX, blockZ);
} else {
irisBiome = engine.getHostSurfaceBiome(blockX, blockZ);
irisBiome = hostSurfaceBiome(chunk, blockX, blockZ);
}
if (irisBiome == null && underground) {
irisBiome = stackLayer == null
? engine.getHostSurfaceBiome(blockX, blockZ)
? hostSurfaceBiome(chunk, blockX, blockZ)
: stackLayer.biome();
}
if (irisBiome == null) {
return null;
}
return createBiomeResolution(
return new BiomeResolution(
irisBiome, underground, owningDimension, blockX, blockY, blockZ);
}
private BiomeResolution createBiomeResolution(
IrisBiome irisBiome,
boolean underground,
IrisDimension dimension,
int blockX,
int blockY,
int blockZ
) {
RNG noiseRng = new RNG(seed
^ (((long) blockX) * 341873128712L)
^ (((long) blockY) * 132897987541L)
^ (((long) blockZ) * 42317861L));
return new BiomeResolution(
irisBiome, underground, dimension, blockX, blockY, blockZ, noiseRng);
private IrisBiome hostSurfaceBiome(ChunkResolution chunk, int blockX, int blockZ) {
return chunk == null ? engine.getHostSurfaceBiome(blockX, blockZ) : chunk.hostSurfaceBiome(blockX, blockZ);
}
private H getFallbackBiome() {
@@ -1094,12 +1084,11 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
}
private static DimensionStackLayout.Layer resolveDimensionStackLayer(
Engine engine,
DimensionStackContext stackContext,
int blockX,
int internalY,
int blockZ
) {
DimensionStackContext stackContext = engine.getDimensionStackContext();
return stackContext == null ? null : stackContext.getLayerAt(blockX, internalY, blockZ);
}
@@ -1136,7 +1125,6 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
private final int blockX;
private final int blockY;
private final int blockZ;
private final RNG rng;
private BiomeResolution(
IrisBiome irisBiome,
@@ -1144,8 +1132,7 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
IrisDimension dimension,
int blockX,
int blockY,
int blockZ,
RNG rng
int blockZ
) {
this.irisBiome = irisBiome;
this.underground = underground;
@@ -1153,7 +1140,100 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
this.blockX = blockX;
this.blockY = blockY;
this.blockZ = blockZ;
this.rng = rng;
}
}
private final class ChunkVisibleResolver implements VisibleResolver<H> {
private final IrisDimensionCarvingResolver.State resolverState = new IrisDimensionCarvingResolver.State();
private final boolean flat = platformGenerator != null && platformGenerator.usesFlatStudioTerrain();
private ChunkResolution chunk;
@Override
public H biome(int quartX, int quartY, int quartZ) {
if (flat) {
return fallbackBiome;
}
int chunkX = quartX >> 2;
int chunkZ = quartZ >> 2;
ChunkResolution current = chunk;
if (current == null || current.chunkX != chunkX || current.chunkZ != chunkZ) {
current = new ChunkResolution(chunkX, chunkZ);
chunk = current;
}
return resolveVisibleBiomeHolder(quartX, quartY, quartZ, resolverState, null, current);
}
}
private final class ChunkResolution {
private static final int COLUMNS = 16;
private final int chunkX;
private final int chunkZ;
private final IrisComplex complex;
private final IrisDimension dimension;
private final DimensionStackContext stackContext;
private final int worldMinHeight;
private final boolean subterrain;
private final RuntimeBiomeState<H, V> runtimeState;
private final int[] hostHeights = new int[COLUMNS];
private final boolean[] hostHeightResolved = new boolean[COLUMNS];
private final IrisBiome[] hostSurfaces = new IrisBiome[COLUMNS];
private final boolean[] hostSurfaceResolved = new boolean[COLUMNS];
private IrisDimension[] customDimensions = new IrisDimension[4];
private IrisBiomeCustom[] customBiomes = new IrisBiomeCustom[4];
private Object[] customHolders = new Object[4];
private int customCount;
private ChunkResolution(int chunkX, int chunkZ) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
this.complex = engine.getComplex();
this.dimension = engine.getDimension();
this.stackContext = engine.getDimensionStackContext();
this.worldMinHeight = engine.getWorld().minHeight();
this.subterrain = !engine.subterrainAbsentInChunk(chunkX, chunkZ);
this.runtimeState = runtimeBiomeState();
}
private int hostHeight(int blockX, int blockZ) {
int column = column(blockX, blockZ);
if (!hostHeightResolved[column]) {
hostHeights[column] = Engine.hostHeight(engine, blockX, blockZ, true);
hostHeightResolved[column] = true;
}
return hostHeights[column];
}
private IrisBiome hostSurfaceBiome(int blockX, int blockZ) {
int column = column(blockX, blockZ);
if (!hostSurfaceResolved[column]) {
hostSurfaces[column] = engine.getHostSurfaceBiome(blockX, blockZ);
hostSurfaceResolved[column] = true;
}
return hostSurfaces[column];
}
@SuppressWarnings("unchecked")
private H customHolder(IrisDimension owner, IrisBiomeCustom custom) {
for (int index = 0; index < customCount; index++) {
if (customBiomes[index] == custom && customDimensions[index] == owner) {
return (H) customHolders[index];
}
}
H holder = runtimeState.customBiomes().get(customBiomeKey(owner, custom.getId()));
if (customCount == customBiomes.length) {
customDimensions = Arrays.copyOf(customDimensions, customCount * 2);
customBiomes = Arrays.copyOf(customBiomes, customCount * 2);
customHolders = Arrays.copyOf(customHolders, customCount * 2);
}
customDimensions[customCount] = owner;
customBiomes[customCount] = custom;
customHolders[customCount++] = holder;
return holder;
}
private static int column(int blockX, int blockZ) {
return ((blockX & 15) >> 2) << 2 | ((blockZ & 15) >> 2);
}
}
@@ -1181,8 +1261,7 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
}
@Override
public VisibleResolver<H> visibleResolver() {
IrisDimensionCarvingResolver.State state = new IrisDimensionCarvingResolver.State();
return (x, y, z) -> getVisibleNoiseBiomeWithActiveGenerationLease(x, y, z, state);
return new ChunkVisibleResolver();
}
}
@@ -12,8 +12,10 @@ import art.arcane.volmlib.util.cache.CacheKey;
import art.arcane.volmlib.util.collection.KList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
@@ -34,12 +36,13 @@ public final class NativeStructureVolumeIndex {
private static final int ORIGIN_REACH_CHUNKS = StructureOwnershipRecordView.MAX_REFERENCE_DISTANCE_CHUNKS;
private static final int MAX_CACHED_ORIGIN_CHUNKS = 16_384;
private static final int MAX_CACHED_QUERY_CHUNKS = 4_096;
private static final int CACHE_STRIPES = 32;
private static final Map<Engine, NativeStructureVolumeIndex> INDEXES =
Collections.synchronizedMap(new WeakHashMap<>());
private final OriginResolver origins;
private final Map<RuntimeChunkKey, KList<NativeStructureVolume>> originCache = lru(MAX_CACHED_ORIGIN_CHUNKS);
private final Map<RuntimeChunkKey, KList<NativeStructureVolume>> queryCache = lru(MAX_CACHED_QUERY_CHUNKS);
private final VolumeCache originCache = new VolumeCache(MAX_CACHED_ORIGIN_CHUNKS);
private final VolumeCache queryCache = new VolumeCache(MAX_CACHED_QUERY_CHUNKS);
private final ConcurrentHashMap<RuntimeChunkKey, CompletableFuture<KList<NativeStructureVolume>>> originBuilds =
new ConcurrentHashMap<>();
private final ConcurrentHashMap<RuntimeChunkKey, CompletableFuture<KList<NativeStructureVolume>>> queryBuilds =
@@ -180,12 +183,10 @@ public final class NativeStructureVolumeIndex {
Engine engine, int chunkX, int chunkZ, Integer pinnedRuntimeId
) {
if (pinnedRuntimeId != null) {
synchronized (originCache) {
KList<NativeStructureVolume> cached = originCache.get(
new RuntimeChunkKey(pinnedRuntimeId, chunkKey(chunkX, chunkZ)));
if (cached != null) {
return cached;
}
KList<NativeStructureVolume> cached = originCache.get(
new RuntimeChunkKey(pinnedRuntimeId, chunkKey(chunkX, chunkZ)));
if (cached != null) {
return cached;
}
}
return originVolumes(engine, chunkX, chunkZ);
@@ -210,22 +211,19 @@ public final class NativeStructureVolumeIndex {
}
void evictRuntime(int runtimeId) {
synchronized (originCache) {
originBuilds.keySet().removeIf(key -> key.runtimeId() == runtimeId);
originCache.keySet().removeIf(key -> key.runtimeId() == runtimeId);
}
synchronized (queryCache) {
queryBuilds.keySet().removeIf(key -> key.runtimeId() == runtimeId);
queryCache.keySet().removeIf(key -> key.runtimeId() == runtimeId);
}
originBuilds.keySet().removeIf(key -> key.runtimeId() == runtimeId);
originCache.evictRuntime(runtimeId);
queryBuilds.keySet().removeIf(key -> key.runtimeId() == runtimeId);
queryCache.evictRuntime(runtimeId);
}
private KList<NativeStructureVolume> cached(
Map<RuntimeChunkKey, KList<NativeStructureVolume>> cache,
VolumeCache volumes,
ConcurrentHashMap<RuntimeChunkKey, CompletableFuture<KList<NativeStructureVolume>>> builds,
RuntimeChunkKey key,
Supplier<KList<NativeStructureVolume>> loader
) {
Map<RuntimeChunkKey, KList<NativeStructureVolume>> cache = volumes.stripe(key);
synchronized (cache) {
KList<NativeStructureVolume> hit = cache.get(key);
if (hit != null) {
@@ -314,6 +312,39 @@ public final class NativeStructureVolumeIndex {
};
}
private static final class VolumeCache {
private final List<Map<RuntimeChunkKey, KList<NativeStructureVolume>>> stripes;
private VolumeCache(int capacity) {
int stripeCapacity = Math.max(1, capacity / CACHE_STRIPES);
List<Map<RuntimeChunkKey, KList<NativeStructureVolume>>> created = new ArrayList<>(CACHE_STRIPES);
for (int stripe = 0; stripe < CACHE_STRIPES; stripe++) {
created.add(lru(stripeCapacity));
}
stripes = List.copyOf(created);
}
private Map<RuntimeChunkKey, KList<NativeStructureVolume>> stripe(RuntimeChunkKey key) {
int hash = key.hashCode();
return stripes.get((hash ^ (hash >>> 16)) & (CACHE_STRIPES - 1));
}
private KList<NativeStructureVolume> get(RuntimeChunkKey key) {
Map<RuntimeChunkKey, KList<NativeStructureVolume>> cache = stripe(key);
synchronized (cache) {
return cache.get(key);
}
}
private void evictRuntime(int runtimeId) {
for (Map<RuntimeChunkKey, KList<NativeStructureVolume>> cache : stripes) {
synchronized (cache) {
cache.keySet().removeIf(key -> key.runtimeId() == runtimeId);
}
}
}
}
static int runtimeChunkHash(int runtimeId, long chunkKey) {
return Long.hashCode(CacheKey.mix(chunkKey)) * 31 + runtimeId;
}
@@ -155,7 +155,7 @@ public final class GenerationSemanticCapture {
int minimumX = Math.multiplyExact(chunkX, CHUNK_SIZE);
int minimumZ = Math.multiplyExact(chunkZ, CHUNK_SIZE);
BitSet resolvedCaves = new BitSet();
CaveBiomes caveBiomes = new CaveBiomes(engine, scoped);
CaveBiomes caveBiomes = new CaveBiomes(engine, scoped, chunkX, chunkZ);
mantle.iterateChunk(chunkX, chunkZ, MatterCavern.class, (localX, y, localZ, cavern) -> {
if (!caveSpace.test(localX, y, localZ)) {
return;
@@ -276,15 +276,17 @@ public final class GenerationSemanticCapture {
private static final class CaveBiomes {
private final Engine engine;
private final boolean scoped;
private final boolean subterrain;
private final CaveFallback fallback;
private final IrisDimensionCarvingResolver.State fallbackState = new IrisDimensionCarvingResolver.State();
private IrisDimensionCarvingResolver.Snapshot snapshot;
private int minimumY;
private String lastKey;
private CaveBiomes(Engine engine, boolean scoped) {
private CaveBiomes(Engine engine, boolean scoped, int chunkX, int chunkZ) {
this.engine = engine;
this.scoped = scoped;
this.subterrain = scoped && !engine.subterrainAbsentInChunk(chunkX, chunkZ);
this.fallback = scoped && engine instanceof IrisEngine irisEngine
&& irisEngine.hasGenerationRuntimeScope() && !engine.getPlatformHooks().isMainThread()
? new CaveFallback(engine.getComplex(), engine.getDimensionStackContext())
@@ -306,7 +308,7 @@ public final class GenerationSemanticCapture {
snapshot = IrisDimensionCarvingResolver.snapshot(engine);
minimumY = engine.getWorld().minHeight();
}
IrisBiome featureBiome = engine.getSubterrainBiome(x, y, z);
IrisBiome featureBiome = subterrain ? engine.getSubterrainBiome(x, y, z) : null;
if (featureBiome != null) {
return featureBiome;
}
@@ -99,6 +99,7 @@ public final class GenerationSemanticIndex {
private volatile IOException journalFailure;
private long appendingRegionKey;
private RegionShard appendingBase;
private boolean journalStorageReady;
private GenerationSemanticIndex(
Path dimensionRoot,
@@ -264,12 +265,13 @@ public final class GenerationSemanticIndex {
if (updates.isEmpty()) {
return;
}
ensureStorageDirectory();
ensureJournalStorage();
boolean existingJournal = journalEntries.containsKey(regionKey);
appendingRegionKey = regionKey;
appendingBase = baseRegion;
publicationLock.unlock();
try {
SemanticJournal.append(directory, regionX, regionZ, updates, unpublished, durability);
SemanticJournal.append(directory, regionX, regionZ, updates, unpublished, durability, existingJournal);
} catch (JournalAppendFailure failure) {
journalFailure = failure;
throw failure;
@@ -1193,6 +1195,14 @@ public final class GenerationSemanticIndex {
return true;
}
private void ensureJournalStorage() throws IOException {
if (journalStorageReady) {
return;
}
ensureStorageDirectory();
journalStorageReady = true;
}
private void ensureStorageDirectory() throws IOException {
validateDimensionRoot();
Path irisDirectory = ensureChildDirectory(dimensionRoot, "iris");
@@ -1958,18 +1968,17 @@ public final class GenerationSemanticIndex {
int regionZ,
List<ChunkGenerationSemantics> claims,
boolean unpublished,
DeferredDurability durability
DeferredDurability durability,
boolean existing
) throws IOException {
Path file = directory.resolve(journalFileName(regionX, regionZ));
boolean created = !Files.exists(file, LinkOption.NOFOLLOW_LINKS);
if (!created && !Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS)) {
boolean created = !existing && !Files.exists(file, LinkOption.NOFOLLOW_LINKS);
if (!existing && !created && !Files.isRegularFile(file, LinkOption.NOFOLLOW_LINKS)) {
throw invalid(file, "path is not a regular file");
}
FileChannel opened = FileChannel.open(
file,
StandardOpenOption.CREATE,
StandardOpenOption.WRITE
);
FileChannel opened = existing
? FileChannel.open(file, StandardOpenOption.WRITE)
: FileChannel.open(file, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
boolean uncertain = created;
boolean restored = false;
try (FileChannel channel = opened) {
@@ -36,7 +36,8 @@ public final class SavedBiomeCapture {
Math.addExact(startX, 15), Math.addExact(startZ, 15)).isEmpty();
Map<Integer, List<SavedBiomeChunk.Span>> vertical = new HashMap<>(exactVolume ? 256 : 16);
Cells cells = new Cells(activationId);
Column column = new Column(engine, minimumY, height, exactVolume ? 1 : 4);
Column column = new Column(engine, minimumY, height, exactVolume ? 1 : 4,
!engine.subterrainAbsentInChunk(stage.chunkX(), stage.chunkZ()));
for (int localX = 0; localX < 16; localX++) {
for (int localZ = 0; localZ < 16; localZ++) {
int blockX = startX + localX;
@@ -82,7 +83,8 @@ public final class SavedBiomeCapture {
for (int index = 0; index < column.biomes().length; index++) {
int worldY = minimumY + index * column.step();
SavedBiomeChunk.Cell current = cells.cell(column.biomes()[index], column.regions()[index]);
if (floating != null && !column.engine().getSubterrainCell(blockX, index * column.step(), blockZ).occupied()) {
if (floating != null && (!column.subterrain()
|| !column.engine().getSubterrainCell(blockX, index * column.step(), blockZ).occupied())) {
current = overlay(current, floating.volumeAt(blockX & 15, worldY - minimumY, blockZ & 15));
}
if (transition != null) {
@@ -139,9 +141,10 @@ public final class SavedBiomeCapture {
Objects.requireNonNull(region, "region").getLoadKey());
}
private record Column(Engine engine, int minimumY, int height, int step, IrisBiome[] biomes, IrisRegion[] regions) {
private Column(Engine engine, int minimumY, int height, int step) {
this(engine, minimumY, height, step, new IrisBiome[(height + step - 1) / step],
private record Column(Engine engine, int minimumY, int height, int step, boolean subterrain, IrisBiome[] biomes,
IrisRegion[] regions) {
private Column(Engine engine, int minimumY, int height, int step, boolean subterrain) {
this(engine, minimumY, height, step, subterrain, new IrisBiome[(height + step - 1) / step],
new IrisRegion[(height + step - 1) / step]);
}
}
@@ -49,6 +49,7 @@ public final class SavedBiomeStore {
private static final int MAXIMUM_CACHED_CHUNKS = 128;
private static final long MAXIMUM_CACHED_BYTES = 64L * 1024L * 1024L;
private static final int MAXIMUM_WRITE_BATCH = 64;
private static final ThreadLocal<Deflater> DEFLATERS = ThreadLocal.withInitial(() -> new Deflater(Deflater.BEST_SPEED));
private final Path dimensionRoot;
private final Path directory;
@@ -464,12 +465,16 @@ public final class SavedBiomeStore {
}
private void ensureRegionFile(RegionIndex region) throws IOException {
if (region.fileReady) {
return;
}
requireSafeAncestors();
Files.createDirectories(directory);
if (Files.exists(region.path, LinkOption.NOFOLLOW_LINKS)) {
if (!Files.isRegularFile(region.path, LinkOption.NOFOLLOW_LINKS)) {
throw new IOException("Saved biome region is not a regular file: " + region.path);
}
region.fileReady = true;
return;
}
ByteBuffer header = ByteBuffer.allocate(HEADER_BYTES);
@@ -486,6 +491,7 @@ public final class SavedBiomeStore {
}
Files.move(temporary, region.path, StandardCopyOption.ATOMIC_MOVE);
forceDirectory(directory);
region.fileReady = true;
} finally {
Files.deleteIfExists(temporary);
}
@@ -674,11 +680,10 @@ public final class SavedBiomeStore {
output.writeInt(chunk.chunkX());
output.writeInt(chunk.chunkZ());
output.writeInt(raw.size());
Deflater deflater = new Deflater(Deflater.BEST_SPEED);
Deflater deflater = DEFLATERS.get();
deflater.reset();
try (DeflaterOutputStream compressed = new DeflaterOutputStream(output, deflater)) {
raw.writeTo(compressed);
} finally {
deflater.end();
}
if (encoded.size() > MAXIMUM_RECORD_BYTES) {
throw new IOException("Saved biome chunk exceeds its encoded size limit");
@@ -847,6 +852,7 @@ public final class SavedBiomeStore {
private final int regionZ;
private final long[] offsets = new long[1024];
private final int[] lengths = new int[1024];
private boolean fileReady;
private RegionIndex(Path path, int regionX, int regionZ) {
this.path = path;
@@ -0,0 +1,111 @@
package art.arcane.iris.platform.bukkit.nms;
import art.arcane.iris.generation.biome.IrisBiome;
import art.arcane.iris.generation.biome.IrisBiomeCustom;
import art.arcane.iris.generation.mantle.EngineMantle;
import art.arcane.iris.generation.runtime.Engine;
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.generation.terrain.IrisDimension;
import art.arcane.iris.generation.terrain.IrisDimensionCarvingResolver;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.world.IrisWorld;
import art.arcane.volmlib.nativelib.terrain.NativeBiomeRegistry;
import art.arcane.volmlib.nativelib.terrain.NativeBiomeSourcePolicy;
import art.arcane.volmlib.util.collection.KList;
import org.junit.Test;
import java.lang.reflect.Field;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import static org.junit.Assert.assertEquals;
import static org.mockito.Answers.CALLS_REAL_METHODS;
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.when;
public class BukkitBiomeVisibleResolverTest {
@Test
public void chunkResolverMatchesThePerQuartResolution() throws Exception {
IrisBiome surface = customBiome("surface", "meadow");
IrisBiome cave = customBiome("cave", "grotto");
IrisBiome custom = customBiome("custom", "mirage");
IrisBiome feature = customBiome("feature", "bloom");
IrisDimension dimension = mock(IrisDimension.class);
when(dimension.getReachableBiomes(any())).thenReturn(new KList<>(surface, cave, custom, feature));
when(dimension.getCustomBiomeKey(anyString())).thenAnswer(invocation -> "pack:" + invocation.getArgument(0));
IrisData data = mock(IrisData.class);
when(data.customBiomeResourceKey(any(IrisDimension.class), anyString()))
.thenAnswer(invocation -> "pack:" + invocation.getArgument(1));
IrisComplex complex = mock(IrisComplex.class);
when(complex.historicalPhysicalBiomeKeyAt(anyInt(), anyInt(), anyInt())).thenReturn(Optional.empty());
EngineMantle mantle = mock(EngineMantle.class);
when(mantle.trueHeight(anyInt(), anyInt())).thenAnswer(invocation ->
120 + Math.floorMod(invocation.<Integer>getArgument(0) * 7 + invocation.<Integer>getArgument(1) * 3, 160));
IrisEngine engine = mock(IrisEngine.class);
when(engine.getDimension()).thenReturn(dimension);
when(engine.getData()).thenReturn(data);
when(engine.getComplex()).thenReturn(complex);
when(engine.getMantle()).thenReturn(mantle);
when(engine.getWorld()).thenReturn(IrisWorld.builder().minHeight(-64).maxHeight(320).build());
when(engine.getMinHeight()).thenReturn(-64);
when(engine.getCacheID()).thenReturn(1);
when(engine.acquireGenerationLease(anyString())).thenReturn(GenerationSessionLease.noop());
when(engine.answersFromNaturalTerrain(anyInt(), anyInt())).thenReturn(false);
when(engine.getHostSurfaceBiome(anyInt(), anyInt())).thenAnswer(invocation ->
Math.floorMod(invocation.<Integer>getArgument(0) + invocation.<Integer>getArgument(1), 8) == 0 ? custom : surface);
when(engine.getCaveBiome(anyInt(), anyInt(), anyInt(), any(IrisDimensionCarvingResolver.State.class)))
.thenAnswer(invocation -> invocation.<Integer>getArgument(1) % 3 == 0 ? custom : cave);
when(engine.getSubterrainBiome(anyInt(), anyInt(), anyInt())).thenAnswer(invocation ->
invocation.<Integer>getArgument(0) == 4 && invocation.<Integer>getArgument(1) == 40 ? feature : null);
when(engine.subterrainAbsentInChunk(1, 0)).thenReturn(true);
@SuppressWarnings("unchecked")
NativeBiomeRegistry<String, String> registry = mock(NativeBiomeRegistry.class);
when(registry.lookupCustom(anyString())).thenAnswer(invocation -> invocation.getArgument(0));
BukkitBiomePolicy<String, String> policy = policy(engine, registry, "fallback");
for (int chunkX = 0; chunkX <= 1; chunkX++) {
NativeBiomeSourcePolicy.VisibleResolver<String> resolver = policy.visibleResolver();
for (int quartY = -16; quartY < 80; quartY++) {
for (int localX = 0; localX < 4; localX++) {
for (int localZ = 0; localZ < 4; localZ++) {
int quartX = (chunkX << 2) + localX;
String expected = policy.getVisibleNoiseBiomeWithActiveGenerationLease(
quartX, quartY, localZ, new IrisDimensionCarvingResolver.State());
assertEquals("quart " + quartX + "," + quartY + "," + localZ, expected,
resolver.biome(quartX, quartY, localZ));
}
}
}
}
}
private static IrisBiome customBiome(String key, String customId) {
IrisBiome biome = new IrisBiome().setCustomDerivitives(new KList<>(new IrisBiomeCustom().setId(customId)));
biome.setLoadKey(key);
return biome;
}
@SuppressWarnings("unchecked")
private static BukkitBiomePolicy<String, String> policy(Engine engine, NativeBiomeRegistry<String, String> registry,
String fallback) throws Exception {
BukkitBiomePolicy<String, String> policy = mock(BukkitBiomePolicy.class, CALLS_REAL_METHODS);
set(policy, "engine", engine);
set(policy, "biomeCustomRegistry", registry);
set(policy, "biomeRegistry", registry);
set(policy, "fallbackBiome", fallback);
set(policy, "runtimeBiomeStates", new ConcurrentHashMap<>());
set(policy, "noiseBiomeCache", new ConcurrentHashMap<>());
return policy;
}
private static void set(Object target, String name, Object value) throws Exception {
Field field = BukkitBiomePolicy.class.getDeclaredField(name);
field.setAccessible(true);
field.set(target, value);
}
}
@@ -36,10 +36,12 @@ public class BukkitSubterrainBiomePrecedenceTest {
owner.setAccessible(true);
owner.set(policy, engine);
DimensionStackLayout stack = mock(DimensionStackLayout.class);
Class<?> chunkResolution = Class.forName(BukkitBiomePolicy.class.getName() + "$ChunkResolution");
Method resolve = BukkitBiomePolicy.class.getDeclaredMethod("resolveBiomeResolution", int.class, int.class,
int.class, IrisDimensionCarvingResolver.State.class, boolean.class, DimensionStackLayout.class);
int.class, IrisDimensionCarvingResolver.State.class, boolean.class, DimensionStackLayout.class,
chunkResolution);
resolve.setAccessible(true);
Object resolved = resolve.invoke(policy, 3, 40, -5, null, true, stack);
Object resolved = resolve.invoke(policy, 3, 40, -5, null, true, stack, null);
assertSame(feature, field(resolved, "irisBiome"));
assertSame(host, field(resolved, "dimension"));
assertEquals(true, field(resolved, "underground"));
@@ -0,0 +1,69 @@
package art.arcane.iris.structure.nativegen;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.volmlib.nativelib.terrain.structure.NativeStructureVolume;
import art.arcane.volmlib.util.collection.KList;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class NativeStructureVolumeIndexCacheTest {
@Test
public void concurrentQueriesBuildEachOriginOnceAndAnswerIdentically() throws Exception {
Map<Long, AtomicInteger> builds = new ConcurrentHashMap<>();
NativeStructureVolumeIndex index = NativeStructureVolumeIndex.forTesting((engine, chunkX, chunkZ) -> {
builds.computeIfAbsent(((long) chunkX << 32) ^ (chunkZ & 0xffffffffL), key -> new AtomicInteger()).incrementAndGet();
return volumesAt(chunkX, chunkZ);
});
Engine engine = mock(Engine.class);
when(engine.getCacheID()).thenReturn(7);
KList<NativeStructureVolume> expected = NativeStructureVolumeIndex.forTesting(
(ignored, chunkX, chunkZ) -> volumesAt(chunkX, chunkZ)).resolve(engine, 0, 0, 47, 47);
ExecutorService pool = Executors.newFixedThreadPool(8);
try {
List<Future<KList<NativeStructureVolume>>> results = new ArrayList<>();
for (int task = 0; task < 32; task++) {
results.add(pool.submit(() -> index.resolve(engine, 0, 0, 47, 47)));
}
for (Future<KList<NativeStructureVolume>> result : results) {
assertEquals(expected, result.get(30, TimeUnit.SECONDS));
}
} finally {
pool.shutdown();
assertTrue(pool.awaitTermination(30, TimeUnit.SECONDS));
}
for (AtomicInteger count : builds.values()) {
assertEquals(1, count.get());
}
int distinct = builds.size();
index.evictRuntime(7);
assertEquals(expected, index.resolve(engine, 0, 0, 47, 47));
assertEquals(distinct, builds.size());
for (AtomicInteger count : builds.values()) {
assertEquals(2, count.get());
}
}
private static KList<NativeStructureVolume> volumesAt(int chunkX, int chunkZ) {
if (Math.floorMod(chunkX * 31 + chunkZ * 17, 5) != 0) {
return NativeStructureVolume.NONE;
}
int minX = chunkX << 4;
int minZ = chunkZ << 4;
return new KList<>(new NativeStructureVolume("test:" + chunkX + "_" + chunkZ,
minX - 20, -10, minZ - 20, minX + 36, 40, minZ + 36, null));
}
}