Merge L5-output

This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-28 12:19:03 -04:00
commit c4dcb32334
33 files changed
+1055 -432

No files matched your search

@@ -48,7 +48,7 @@ public final class FloatingIslandBoundarySampler {
}
public @Nullable IrisBiome parent(int x, int z) {
return parentField(x, z, EDGE_TAPER_WIDTH).parent(x, z);
return source.sample(x, z);
}
public double edgeFade(IrisBiome parent, int x, int z) {
@@ -230,10 +230,6 @@ public final class FloatingIslandBoundarySampler {
build(source);
}
private @Nullable IrisBiome parent(int x, int z) {
return parents[geometry.index(x - minX, z - minZ)];
}
private int edgeDistance(IrisBiome expected, int x, int z) {
int index = geometry.index(x - minX, z - minZ);
if (!sameBiome(expected, parents[index]) || !viable[index]) {
@@ -23,19 +23,35 @@ import art.arcane.volmlib.nativelib.terrain.NativeBlockVolume;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.iris.generation.block.BoundBlockState;
import art.arcane.iris.generation.block.IrisCustomData;
import art.arcane.volmlib.util.hunk.storage.AtomicHunk;
import art.arcane.volmlib.util.hunk.storage.StorageHunk;
import org.bukkit.block.data.BlockData;
import org.bukkit.generator.ChunkGenerator.ChunkData;
@SuppressWarnings("ClassCanBeRecord")
public class ChunkDataHunkHolder extends AtomicHunk<NativeBlockState> implements NativeBlockVolume {
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.Arrays;
/**
* Plain-array block buffer for one chunk. Stages write disjoint columns when they fan out and every hand-off
* between threads is a join, so the cells need no per-access fences. Each column tracks its highest stored y;
* a raise is a CAS so concurrent writers to one column can never lose the maximum.
*/
public class ChunkDataHunkHolder extends StorageHunk<NativeBlockState> implements NativeBlockVolume, ColumnExtent {
private static final BoundBlockState AIR = BoundBlockState.of("AIR");
private static final VarHandle COLUMN_TOPS = MethodHandles.arrayElementVarHandle(int[].class);
private final ChunkData chunk;
private final NativeBlockState[] data;
private final int[] columnTops;
private final int zStride;
public ChunkDataHunkHolder(ChunkData chunk) {
super(16, chunk.getMaxHeight() - chunk.getMinHeight(), 16);
this.chunk = chunk;
this.zStride = 16 * getHeight();
this.data = new NativeBlockState[zStride * 16];
this.columnTops = new int[256];
Arrays.fill(columnTops, -1);
}
@Override
@@ -56,7 +72,16 @@ public class ChunkDataHunkHolder extends AtomicHunk<NativeBlockState> implements
if (t == null) {
return;
}
super.setRaw(x, y, z, t);
data[index(x, y, z)] = t;
int column = (z << 4) | x;
int top = (int) COLUMN_TOPS.getOpaque(columnTops, column);
while (y > top) {
int witness = (int) COLUMN_TOPS.compareAndExchange(columnTops, column, top, y);
if (witness == top) {
break;
}
top = witness;
}
}
@Override
@@ -65,13 +90,23 @@ public class ChunkDataHunkHolder extends AtomicHunk<NativeBlockState> implements
return AIR.get();
}
NativeBlockState b = super.getRaw(x, y, z);
NativeBlockState b = data[index(x, y, z)];
return b != null ? b : AIR.get();
}
@Override
public NativeBlockState getStoredRaw(int x, int y, int z) {
return super.getRaw(x, y, z);
return data[index(x, y, z)];
}
@Override
public int highestStoredY(int x, int z) {
return (int) COLUMN_TOPS.getOpaque(columnTops, (z << 4) | x);
}
private int index(int x, int y, int z) {
return (z * zStride) + (y << 4) + x;
}
public void apply() {
@@ -90,7 +125,7 @@ public class ChunkDataHunkHolder extends AtomicHunk<NativeBlockState> implements
int runStart = -1;
for (int y = 0; y < height; y++) {
NativeBlockState state = super.getRaw(x, y, z);
NativeBlockState state = getStoredRaw(x, y, z);
BlockData block = state == null ? null : (BlockData) state.nativeHandle();
// Custom wrappers are not real Bukkit data; write the vanilla base like the
// NMS fast path (NMSBinding.applyChunkDataBlocks) does.
@@ -0,0 +1,112 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.generation.chunk;
import art.arcane.iris.platform.bukkit.BukkitBiome;
import art.arcane.volmlib.nativelib.terrain.NativeBiome;
import art.arcane.volmlib.util.hunk.HunkMutationSupport;
import art.arcane.volmlib.util.hunk.storage.StorageHunk;
import org.bukkit.block.Biome;
/**
* Biome output for one Bukkit chunk, read back only by the generation stages. The biome actuator writes whole
* columns, so storage stays at one entry per column until a partial column write arrives; that write switches
* the hunk to full resolution. Reads, clamping and the plains fallback match {@link LinkedTerrainChunk}.
*/
public final class ColumnBiomeHunk extends StorageHunk<NativeBiome> {
private static final int CHUNK_SIZE = 16;
private final NativeBiome[] columns = new NativeBiome[CHUNK_SIZE * CHUNK_SIZE];
private NativeBiome[] cells;
private volatile NativeBiome defaultBiome;
public ColumnBiomeHunk(int height) {
super(CHUNK_SIZE, height, CHUNK_SIZE);
}
@Override
public void set(int x1, int y1, int z1, int x2, int y2, int z2, NativeBiome biome) {
if (x1 == x2 && z1 == z2 && y1 == 0 && y2 == getHeight() - 1) {
fillColumn(x1, z1, LinkedTerrainChunk.canonicalBiome(biome));
return;
}
HunkMutationSupport.setRangeInclusive(this, x1, y1, z1, x2, y2, z2, biome);
}
@Override
public void setRaw(int x, int y, int z, NativeBiome biome) {
NativeBiome[] storage = cells == null ? expand() : cells;
storage[cellIndex(x, y, z)] = LinkedTerrainChunk.canonicalBiome(biome);
}
@Override
public NativeBiome getRaw(int x, int y, int z) {
NativeBiome[] storage = cells;
NativeBiome biome = storage == null ? columns[columnIndex(x, z)] : storage[cellIndex(x, y, z)];
if (biome != null) {
return biome;
}
NativeBiome fallback = defaultBiome;
if (fallback == null) {
fallback = BukkitBiome.of(Biome.PLAINS);
defaultBiome = fallback;
}
return fallback;
}
private void fillColumn(int x, int z, NativeBiome biome) {
NativeBiome[] storage = cells;
if (storage == null) {
columns[columnIndex(x, z)] = biome;
return;
}
int stride = CHUNK_SIZE * CHUNK_SIZE;
int index = columnIndex(x, z);
for (int y = 0; y < getHeight(); y++) {
storage[index] = biome;
index += stride;
}
}
private NativeBiome[] expand() {
NativeBiome[] storage = new NativeBiome[CHUNK_SIZE * getHeight() * CHUNK_SIZE];
int stride = CHUNK_SIZE * CHUNK_SIZE;
for (int column = 0; column < stride; column++) {
NativeBiome biome = columns[column];
if (biome == null) {
continue;
}
for (int index = column; index < storage.length; index += stride) {
storage[index] = biome;
}
}
cells = storage;
return storage;
}
private static int columnIndex(int x, int z) {
return ((z & (CHUNK_SIZE - 1)) * CHUNK_SIZE) + (x & (CHUNK_SIZE - 1));
}
private int cellIndex(int x, int y, int z) {
int clampedY = Math.max(0, Math.min(getHeight() - 1, y));
return (clampedY * CHUNK_SIZE * CHUNK_SIZE) + columnIndex(x, z);
}
}
@@ -0,0 +1,33 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.generation.chunk;
import art.arcane.volmlib.util.hunk.Hunk;
/**
* A block volume that knows, per column, the highest y it holds a value for. Every cell above that y reads as
* unwritten, so column scans can stop there without changing what they observe.
*/
public interface ColumnExtent {
int highestStoredY(int x, int z);
static int highestStoredY(Hunk<?> hunk, int x, int z) {
return hunk instanceof ColumnExtent extent ? extent.highestStoredY(x, z) : hunk.getHeight() - 1;
}
}
@@ -0,0 +1,37 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2026 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.generation.chunk;
import art.arcane.volmlib.util.function.Consumer4;
import art.arcane.volmlib.util.hunk.Hunk;
import art.arcane.volmlib.util.hunk.view.ListeningHunk;
public final class ColumnExtentListeningHunk<T> extends ListeningHunk<T> implements ColumnExtent {
private final Hunk<T> source;
public ColumnExtentListeningHunk(Hunk<T> source, Consumer4<Integer, Integer, Integer, T> listener) {
super(source, listener);
this.source = source;
}
@Override
public int highestStoredY(int x, int z) {
return ColumnExtent.highestStoredY(source, x, z);
}
}
@@ -130,7 +130,7 @@ public class LinkedTerrainChunk implements TerrainChunk {
return (clampedY * CHUNK_SIZE + clampedZ) * CHUNK_SIZE + clampedX;
}
private static NativeBiome canonicalBiome(NativeBiome biome) {
static NativeBiome canonicalBiome(NativeBiome biome) {
if (biome instanceof BukkitBiome) {
return biome;
}
@@ -230,6 +230,10 @@ public class ChunkContext {
return region;
}
public boolean hasDimensionStack() {
return dimensionStackContext != null;
}
public DimensionStackLayout getDimensionStackLayout(int localX, int localZ) {
if (dimensionStackContext == null) {
return null;
@@ -81,7 +81,7 @@ final class EngineHotloader {
previousGeneration.transitionPlan());
PreparedHydrologyCacheIdentity cacheIdentity = PreparedHydrologyCacheIdentity.capture(
assembly.target, assembly.runtimeKernel, assembly.transitionPlan, engine.isStudio());
engine.runtimeAssembly.set(assembly);
engine.threadState.setAssembly(assembly);
EngineRuntime next;
try (IrisContext.Scope ignored = IrisContext.open(engine, engine.getGenerationSessions().currentSessionId(), null)) {
assembly.complex = assembly.runtimeKernel.createComplex(engine, assembly.transitionPlan, false);
@@ -97,7 +97,7 @@ final class EngineHotloader {
biomeMaxes);
next = previous.withGeneration(nextGeneration);
} finally {
engine.runtimeAssembly.remove();
engine.threadState.setAssembly(null);
}
Throwable retirementFailure = runCleanup(null, previousGeneration.complex()::close);
if (retirementFailure != null) {
@@ -23,7 +23,7 @@ import art.arcane.iris.generation.concurrent.MultiBurst;
import art.arcane.iris.generation.context.ChunkContext;
import art.arcane.iris.generation.context.IrisContext;
import art.arcane.iris.generation.mantle.EngineMantle;
import art.arcane.iris.generation.stage.IrisBiomeActuator;
import art.arcane.iris.generation.stage.IrisDimensionStackActuator;
import art.arcane.iris.generation.stage.IrisTransitionGeometryActuator;
import art.arcane.iris.integration.Identifier;
import art.arcane.iris.generation.decoration.tree.TreeBlockMaterial;
@@ -215,7 +215,7 @@ public interface EngineMode extends Staged {
try (IrisContext.Scope chunkScope = IrisContext.open(getEngine(), generationSessionId, ctx)) {
generateTerrain(x, z, blocks, biomes, multicore, ctx);
recordNaturalTerrain(x, z, blocks, biomes, ctx);
IrisBiomeActuator.publishNaturalMetadata(getEngine(), x, z, biomes, ctx);
IrisDimensionStackActuator.clearHostNaturalMetadata(getEngine(), x, z, biomes.getHeight(), ctx);
ctx.beginContent();
List<Matter> continuations = restoreObjectContinuations(x, z);
for (EngineStage i : stages) {
@@ -93,7 +93,7 @@ final class EngineRuntimeBuilder {
mantleStorageDirectory,
selectRuntimeKernel(kernelVersion),
transitionPlan);
engine.runtimeAssembly.set(assembly);
engine.threadState.setAssembly(assembly);
try (IrisContext.Scope ignored = IrisContext.open(engine, engine.getGenerationSessions().currentSessionId(), null)) {
IrisLogging.debug("Setup Engine " + assembly.cacheId);
long started = M.ms();
@@ -148,7 +148,7 @@ final class EngineRuntimeBuilder {
}
throw new IllegalStateException("Failed to build a complete Iris engine runtime.", e);
} finally {
engine.runtimeAssembly.remove();
engine.threadState.setAssembly(null);
}
}
@@ -174,7 +174,7 @@ final class EngineRuntimeBuilder {
transitionPlan);
PreparedHydrologyCacheIdentity cacheIdentity = PreparedHydrologyCacheIdentity.capture(
runtimeTarget, assembly.runtimeKernel, transitionPlan, engine.isStudio());
engine.runtimeAssembly.set(assembly);
engine.threadState.setAssembly(assembly);
try (IrisContext.Scope ignored = IrisContext.open(engine, engine.getGenerationSessions().currentSessionId(), null)) {
IrisLogging.debug("Setup Detached Generation Runtime " + assembly.cacheId);
assembly.mantle = assembly.runtimeKernel.createMantle(engine, assembly.mantleStorageDirectory);
@@ -201,7 +201,7 @@ final class EngineRuntimeBuilder {
}
throw new IllegalStateException("Failed to build a detached Iris generation runtime.", e);
} finally {
engine.runtimeAssembly.remove();
engine.threadState.setAssembly(null);
}
}
@@ -264,20 +264,16 @@ final class EngineShutdownSequence {
return failure;
}
incompleteAssemblies.add(assembly);
RuntimeAssembly previous = engine == null ? null : engine.runtimeAssembly.get();
RuntimeAssembly previous = engine == null ? null : engine.threadState.assembly();
if (engine != null) {
engine.runtimeAssembly.set(assembly);
engine.threadState.setAssembly(assembly);
}
Throwable assemblyFailure;
try {
assemblyFailure = closeAssemblyResources(assembly);
} finally {
if (engine != null) {
if (previous == null) {
engine.runtimeAssembly.remove();
} else {
engine.runtimeAssembly.set(previous);
}
engine.threadState.setAssembly(previous);
}
}
if (assemblyFailure == null) {
@@ -401,7 +397,7 @@ final class EngineShutdownSequence {
engine,
generationRuntime);
Throwable runtimeFailure;
try (IrisEngine.GenerationRuntimeScope ignored = engine.generationRuntimeScopes.open(binding)) {
try (IrisEngine.GenerationRuntimeScope ignored = engine.threadState.open(binding)) {
runtimeFailure = closeGenerationRuntime(generationRuntime, null);
} catch (Throwable scopeFailure) {
runtimeFailure = scopeFailure;
@@ -0,0 +1,162 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.generation.runtime;
import art.arcane.iris.generation.runtime.EngineRuntimeBuilder.RuntimeAssembly;
import art.arcane.iris.generation.runtime.IrisEngine.BiomeEnvironmentBinding;
import art.arcane.iris.generation.runtime.IrisEngine.GenerationRuntimeBinding;
import art.arcane.iris.generation.runtime.IrisEngine.GenerationRuntimeScope;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;
/**
* Every per-thread engine binding (runtime assembly, biome environment, generation runtime scope) in one frame.
* The engine getters run on generation hot paths, where a ThreadLocal lookup probes long collision chains in the
* workers' crowded thread-local maps. Frames therefore live in a table indexed by thread id and owned by their
* thread; a frame whose slot is taken by another thread falls back to a ThreadLocal.
*/
final class EngineThreadState {
private static final int SLOTS = 256;
private final AtomicReferenceArray<Frame> slots = new AtomicReferenceArray<>(SLOTS);
private final ThreadLocal<Frame> overflow = new ThreadLocal<>();
private final AtomicInteger overflowFrames = new AtomicInteger();
Frame current() {
Thread thread = Thread.currentThread();
Frame frame = slots.getPlain(slot(thread));
if (frame != null && frame.owner == thread) {
return frame;
}
return overflowFrames.get() == 0 ? null : overflow.get();
}
GenerationRuntimeBinding binding() {
Frame frame = current();
return frame == null ? null : frame.binding;
}
RuntimeAssembly assembly() {
Frame frame = current();
return frame == null ? null : frame.assembly;
}
void setAssembly(RuntimeAssembly assembly) {
if (assembly != null) {
frame().assembly = assembly;
return;
}
Frame frame = current();
if (frame != null) {
frame.assembly = null;
releaseIfEmpty(frame);
}
}
BiomeEnvironmentBinding environment() {
Frame frame = current();
return frame == null ? null : frame.environment;
}
void setEnvironment(BiomeEnvironmentBinding environment) {
if (environment != null) {
frame().environment = environment;
return;
}
Frame frame = current();
if (frame != null) {
frame.environment = null;
releaseIfEmpty(frame);
}
}
GenerationRuntimeScope open(GenerationRuntimeBinding binding) {
Thread owner = Thread.currentThread();
Frame frame = frame();
GenerationRuntimeBinding previous = frame.binding;
frame.binding = binding;
return new GenerationRuntimeScope(this, owner, previous, binding);
}
void close(
Thread owner,
GenerationRuntimeBinding previous,
GenerationRuntimeBinding installed
) {
if (Thread.currentThread() != owner) {
throw new IllegalStateException("Iris generation runtime scope closed from a different thread.");
}
Frame frame = current();
if ((frame == null ? null : frame.binding) != installed) {
throw new IllegalStateException("Iris generation runtime scopes must close in LIFO order.");
}
if (previous != null) {
frame().binding = previous;
return;
}
if (frame != null) {
frame.binding = null;
releaseIfEmpty(frame);
}
}
private Frame frame() {
Frame frame = current();
if (frame != null) {
return frame;
}
Thread thread = Thread.currentThread();
frame = new Frame(thread);
if (!slots.compareAndSet(slot(thread), null, frame)) {
frame.overflow = true;
overflow.set(frame);
overflowFrames.incrementAndGet();
}
return frame;
}
private void releaseIfEmpty(Frame frame) {
if (frame.assembly != null || frame.environment != null || frame.binding != null) {
return;
}
if (frame.overflow) {
overflow.remove();
overflowFrames.decrementAndGet();
return;
}
slots.compareAndSet(slot(frame.owner), frame, null);
}
private static int slot(Thread thread) {
return (int) thread.threadId() & (SLOTS - 1);
}
static final class Frame {
private final Thread owner;
private boolean overflow;
RuntimeAssembly assembly;
BiomeEnvironmentBinding environment;
GenerationRuntimeBinding binding;
private Frame(Thread owner) {
this.owner = owner;
}
}
}
@@ -1,55 +0,0 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2022 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package art.arcane.iris.generation.runtime;
import art.arcane.iris.generation.runtime.IrisEngine.GenerationRuntimeBinding;
import art.arcane.iris.generation.runtime.IrisEngine.GenerationRuntimeScope;
final class GenerationRuntimeScopeState {
private final ThreadLocal<GenerationRuntimeBinding> current = new ThreadLocal<>();
GenerationRuntimeBinding current() {
return current.get();
}
GenerationRuntimeScope open(GenerationRuntimeBinding binding) {
Thread owner = Thread.currentThread();
GenerationRuntimeBinding previous = current.get();
current.set(binding);
return new GenerationRuntimeScope(this, owner, previous, binding);
}
void close(
Thread owner,
GenerationRuntimeBinding previous,
GenerationRuntimeBinding installed
) {
if (Thread.currentThread() != owner) {
throw new IllegalStateException("Iris generation runtime scope closed from a different thread.");
}
if (current.get() != installed) {
throw new IllegalStateException("Iris generation runtime scopes must close in LIFO order.");
}
if (previous == null) {
current.remove();
return;
}
current.set(previous);
}
}
@@ -48,6 +48,7 @@ import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.volmlib.util.atomics.AtomicRollingSequence;
import art.arcane.iris.generation.context.ChunkContext;
import art.arcane.iris.generation.context.IrisContext;
import art.arcane.iris.generation.chunk.ColumnExtentListeningHunk;
import art.arcane.volmlib.util.documentation.BlockCoordinates;
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
import art.arcane.volmlib.util.hunk.Hunk;
@@ -116,13 +117,7 @@ public class IrisEngine implements Engine {
final Object generationHistoryRuntimeRouterLock = new Object();
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
final ThreadLocal<RuntimeAssembly> runtimeAssembly = new ThreadLocal<>();
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
final ThreadLocal<BiomeEnvironmentBinding> biomeEnvironmentScopes = new ThreadLocal<>();
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
final GenerationRuntimeScopeState generationRuntimeScopes = new GenerationRuntimeScopeState();
final EngineThreadState threadState = new EngineThreadState();
@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)
final Set<GenerationRuntime> detachedGenerationRuntimes = Collections.synchronizedSet(
@@ -382,7 +377,7 @@ public class IrisEngine implements Engine {
throw new IllegalStateException("World entry hydrology preparation could not acquire its generation session.", failure);
}
try (lease;
GenerationRuntimeScope runtimeScope = generationRuntimeScopes.open(binding);
GenerationRuntimeScope runtimeScope = threadState.open(binding);
IrisContext.Scope context = IrisContext.open(this, lease.sessionId(), null)) {
getComplex().getHydrologyRuntime().prepareChunkColumns(blockX, blockZ);
} catch (Exception failure) {
@@ -572,8 +567,8 @@ public class IrisEngine implements Engine {
@Override
public BiomeEnvironment.Scope openBiomeEnvironmentScope(BiomeEnvironment environment) {
BiomeEnvironmentBinding binding = new BiomeEnvironmentBinding(
Objects.requireNonNull(environment, "environment"), biomeEnvironmentScopes.get());
biomeEnvironmentScopes.set(binding);
Objects.requireNonNull(environment, "environment"), threadState.environment());
threadState.setEnvironment(binding);
return binding;
}
@@ -979,7 +974,7 @@ public class IrisEngine implements Engine {
}
public boolean hasGenerationRuntimeScope() {
return generationRuntimeScopes.current() != null;
return threadState.binding() != null;
}
private boolean usesScopedNaturalTerrain() {
@@ -1058,7 +1053,7 @@ public class IrisEngine implements Engine {
}
public GenerationRuntimeBinding captureGenerationRuntimeBinding() {
GenerationRuntimeBinding scoped = generationRuntimeScopes.current();
GenerationRuntimeBinding scoped = threadState.binding();
return scoped == null ? getActiveGenerationRuntimeBinding() : scoped;
}
@@ -1074,7 +1069,7 @@ public class IrisEngine implements Engine {
if (!isGenerationRuntimeBindingLive(required)) {
throw new IllegalStateException("Iris generation runtime binding is closed or no longer owned.");
}
return generationRuntimeScopes.open(required);
return threadState.open(required);
}
}
@@ -1265,75 +1260,85 @@ public class IrisEngine implements Engine {
@Override
public SeedManager getSeedManager() {
RuntimeAssembly assembly = runtimeAssembly.get();
EngineThreadState.Frame frame = threadState.current();
RuntimeAssembly assembly = frame == null ? null : frame.assembly;
if (assembly != null) {
return assembly.seedManager;
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? seedManager : current.seedManager();
}
@Override
public IrisComplex getComplex() {
RuntimeAssembly assembly = runtimeAssembly.get();
EngineThreadState.Frame frame = threadState.current();
RuntimeAssembly assembly = frame == null ? null : frame.assembly;
if (assembly != null && assembly.complex != null) {
return assembly.complex;
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? null : current.complex();
}
@Override
public EngineTarget getTarget() {
RuntimeAssembly assembly = runtimeAssembly.get();
EngineThreadState.Frame frame = threadState.current();
RuntimeAssembly assembly = frame == null ? null : frame.assembly;
if (assembly != null) {
return assembly.target;
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? publishedTarget : current.target();
}
@Override
public IrisData getData() {
BiomeEnvironmentBinding environment = biomeEnvironmentScopes.get();
if (environment != null && !hasGenerationRuntimeScope()) {
return environment.environment.data();
EngineThreadState.Frame frame = threadState.current();
if (frame != null) {
BiomeEnvironmentBinding environment = frame.environment;
if (environment != null && frame.binding == null) {
return environment.environment.data();
}
RuntimeAssembly assembly = frame.assembly;
if (assembly != null) {
return assembly.target.getData();
}
}
RuntimeAssembly assembly = runtimeAssembly.get();
if (assembly != null) {
return assembly.target.getData();
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? publishedTarget.getData() : current.data();
}
@Override
public IrisDimension getDimension() {
BiomeEnvironmentBinding environment = biomeEnvironmentScopes.get();
if (environment != null && !hasGenerationRuntimeScope()) {
return environment.environment.dimension();
EngineThreadState.Frame frame = threadState.current();
if (frame != null) {
BiomeEnvironmentBinding environment = frame.environment;
if (environment != null && frame.binding == null) {
return environment.environment.dimension();
}
RuntimeAssembly assembly = frame.assembly;
if (assembly != null) {
return assembly.target.getDimension();
}
}
RuntimeAssembly assembly = runtimeAssembly.get();
if (assembly != null) {
return assembly.target.getDimension();
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? publishedTarget.getDimension() : current.dimension();
}
@Override
public EngineMantle getMantle() {
RuntimeAssembly assembly = runtimeAssembly.get();
EngineThreadState.Frame frame = threadState.current();
RuntimeAssembly assembly = frame == null ? null : frame.assembly;
if (assembly != null && assembly.mantle != null) {
return assembly.mantle;
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? null : current.mantle();
}
@Override
public EngineMode getMode() {
RuntimeAssembly assembly = runtimeAssembly.get();
RuntimeAssembly assembly = threadState.assembly();
if (assembly != null && assembly.mode != null) {
return assembly.mode;
}
@@ -1343,7 +1348,7 @@ public class IrisEngine implements Engine {
@Override
public EngineEffects getEffects() {
RuntimeAssembly assembly = runtimeAssembly.get();
RuntimeAssembly assembly = threadState.assembly();
if (assembly != null && assembly.effects != null) {
return assembly.effects;
}
@@ -1353,7 +1358,7 @@ public class IrisEngine implements Engine {
@Override
public EngineWorldManager getWorldManager() {
RuntimeAssembly assembly = runtimeAssembly.get();
RuntimeAssembly assembly = threadState.assembly();
if (assembly != null && assembly.worldManager != null) {
return assembly.worldManager;
}
@@ -1363,27 +1368,29 @@ public class IrisEngine implements Engine {
@Override
public UpperDimensionContext getUpperContext() {
RuntimeAssembly assembly = runtimeAssembly.get();
EngineThreadState.Frame frame = threadState.current();
RuntimeAssembly assembly = frame == null ? null : frame.assembly;
if (assembly != null) {
return assembly.upperContext;
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? null : current.upperContext();
}
@Override
public DimensionStackContext getDimensionStackContext() {
RuntimeAssembly assembly = runtimeAssembly.get();
EngineThreadState.Frame frame = threadState.current();
RuntimeAssembly assembly = frame == null ? null : frame.assembly;
if (assembly != null) {
return assembly.dimensionStackContext;
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? null : current.dimensionStackContext();
}
@Override
public CompletableFuture<Long> getHash32() {
RuntimeAssembly assembly = runtimeAssembly.get();
RuntimeAssembly assembly = threadState.assembly();
if (assembly != null && assembly.hash32 != null) {
return assembly.hash32;
}
@@ -1465,7 +1472,7 @@ public class IrisEngine implements Engine {
IrisContext.Scope generationScope = IrisContext.open(this, lease.sessionId(), null)) {
getEngineData().getStatistics().generatedChunk();
PrecisionStopwatch p = PrecisionStopwatch.start();
Hunk<NativeBlockState> blocks = vblocks.listen((xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t));
Hunk<NativeBlockState> blocks = new ColumnExtentListeningHunk<>(vblocks, (xx, y, zz, t) -> catchBlockUpdates(x + xx, y, z + zz, t));
if (getDimension().isDebugChunkCrossSections() && ((x >> 4) % getDimension().getDebugCrossSectionsMod() == 0 || (z >> 4) % getDimension().getDebugCrossSectionsMod() == 0)) {
NativeBlockState crossSection = B.getState("CRYING_OBSIDIAN");
@@ -1593,16 +1600,21 @@ public class IrisEngine implements Engine {
@Override
public int getCacheID() {
RuntimeAssembly assembly = runtimeAssembly.get();
EngineThreadState.Frame frame = threadState.current();
RuntimeAssembly assembly = frame == null ? null : frame.assembly;
if (assembly != null) {
return assembly.cacheId;
}
GenerationRuntime current = selectedGenerationRuntime();
GenerationRuntime current = selectedGenerationRuntime(frame);
return current == null ? -1 : current.cacheId();
}
private GenerationRuntime selectedGenerationRuntime() {
GenerationRuntimeBinding scoped = generationRuntimeScopes.current();
return selectedGenerationRuntime(threadState.current());
}
private GenerationRuntime selectedGenerationRuntime(EngineThreadState.Frame frame) {
GenerationRuntimeBinding scoped = frame == null ? null : frame.binding;
if (scoped != null && isGenerationRuntimeBindingLive(scoped)) {
return scoped.runtime;
}
@@ -1611,7 +1623,7 @@ public class IrisEngine implements Engine {
}
public GenerationKernelRegistry.Version getGenerationKernelVersion() {
RuntimeAssembly assembly = runtimeAssembly.get();
RuntimeAssembly assembly = threadState.assembly();
if (assembly != null) {
return assembly.kernelVersion;
}
@@ -1619,7 +1631,7 @@ public class IrisEngine implements Engine {
}
public GenerationKernelRegistry.RuntimeKernel getGenerationRuntimeKernel() {
RuntimeAssembly assembly = runtimeAssembly.get();
RuntimeAssembly assembly = threadState.assembly();
if (assembly != null) {
return assembly.runtimeKernel;
}
@@ -1627,7 +1639,7 @@ public class IrisEngine implements Engine {
}
public TransitionGenerationPlan getTransitionGenerationPlan() {
RuntimeAssembly assembly = runtimeAssembly.get();
RuntimeAssembly assembly = threadState.assembly();
if (assembly != null) {
return assembly.transitionPlan;
}
@@ -1751,14 +1763,14 @@ public class IrisEngine implements Engine {
}
public static final class GenerationRuntimeScope implements AutoCloseable {
private final GenerationRuntimeScopeState state;
private final EngineThreadState state;
private final Thread owner;
private final GenerationRuntimeBinding previous;
private final GenerationRuntimeBinding installed;
private boolean closed;
GenerationRuntimeScope(
GenerationRuntimeScopeState state,
EngineThreadState state,
Thread owner,
GenerationRuntimeBinding previous,
GenerationRuntimeBinding installed
@@ -1780,7 +1792,7 @@ public class IrisEngine implements Engine {
}
private final class BiomeEnvironmentBinding implements BiomeEnvironment.Scope {
final class BiomeEnvironmentBinding implements BiomeEnvironment.Scope {
private final BiomeEnvironment environment;
private final BiomeEnvironmentBinding previous;
private final Thread owner = Thread.currentThread();
@@ -1799,14 +1811,10 @@ public class IrisEngine implements Engine {
if (closed) {
return;
}
if (biomeEnvironmentScopes.get() != this) {
if (threadState.environment() != this) {
throw new IllegalStateException("Biome environment scopes must close in reverse order.");
}
if (previous == null) {
biomeEnvironmentScopes.remove();
} else {
biomeEnvironmentScopes.set(previous);
}
threadState.setEnvironment(previous);
closed = true;
}
}
@@ -27,7 +27,6 @@ import art.arcane.iris.world.history.TransitionGenerationPlan;
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.spi.IrisPlatform;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.volmlib.nativelib.terrain.NativeBiome;
import art.arcane.iris.generation.context.ChunkContext;
@@ -35,12 +34,7 @@ import art.arcane.iris.generation.context.ChunkedDataCache;
import art.arcane.volmlib.util.hunk.Hunk;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.documentation.BlockCoordinates;
import art.arcane.volmlib.util.mantle.runtime.Mantle;
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.Matter;
import art.arcane.volmlib.util.matter.MatterBiomeInject;
import art.arcane.volmlib.util.matter.slices.BiomeInjectMatter;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import java.util.List;
@@ -48,8 +42,8 @@ import java.util.Objects;
public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
private final RNG rng;
private final KMap<String, ResolvedBiome> resolvedBiomes = new KMap<>();
private final KMap<String, ResolvedBiome> resolvedPhysicalBiomes = new KMap<>();
private final KMap<String, NativeBiome> resolvedBiomes = new KMap<>();
private final KMap<String, NativeBiome> resolvedPhysicalBiomes = new KMap<>();
public IrisBiomeActuator(Engine engine) {
super(engine, "Biome");
@@ -64,7 +58,6 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
int depth = h.getDepth();
int height = h.getHeight();
Engine engine = getEngine();
Mantle<Matter> mantle = engine.getMantle().getMantle();
ChunkedDataCache<IrisBiome> biomeCache = context.getBiome();
IrisComplex complex = context.getComplex();
DimensionStackContext dimensionStackContext = engine.getDimensionStackContext();
@@ -74,7 +67,7 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
int worldX = x + xf;
int worldZ = z + zf;
IrisBiome biome = biomeCache.get(xf, zf);
ResolvedBiome resolved = resolve(biomeKey(
NativeBiome platformBiome = resolve(biomeKey(
biome,
getDimension(),
engine,
@@ -82,8 +75,9 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
0,
worldZ
));
NativeBiome platformBiome = resolved.biome();
writeColumn(h, mantle, xf, zf, worldX, worldZ, height, resolved);
if (platformBiome != null) {
h.set(xf, 0, zf, xf, height - 1, zf, platformBiome);
}
if (dimensionStackContext != null) {
NativeBiome bottomBiome = platformBiome == null
@@ -95,48 +89,19 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
worldX,
worldZ,
h,
mantle,
engine,
new ResolvedBiome(bottomBiome, resolved.matter()),
bottomBiome,
context.getDimensionStackLayout(xf, zf)
);
}
writeHistoricalColumn(h, mantle, xf, zf, worldX, worldZ, height, engine, complex);
writeHistoricalColumn(h, xf, zf, worldX, worldZ, height, engine, complex);
}
}
engine.getMetrics().getBiome().put(p.getMilliseconds());
}
public static void publishNaturalMetadata(Engine engine, int x, int z, Hunk<NativeBiome> biomes,
ChunkContext context) {
if (!context.getComplex().allowsMantleChunkWrite(x >> 4, z >> 4)) {
return;
}
Mantle<Matter> mantle = engine.getMantle().getMantle();
MantleChunk<Matter> chunk = mantle.getChunk(x >> 4, z >> 4).use();
try {
synchronized (chunk) {
IrisDimensionStackActuator.clearNaturalMetadata(chunk, context, biomes.getHeight());
chunk.deleteSlices(MatterBiomeInject.class);
for (int localX = 0; localX < biomes.getWidth(); localX += 4) {
for (int localZ = 0; localZ < biomes.getDepth(); localZ += 4) {
for (int y = 0; y < biomes.getHeight(); y += 4) {
NativeBiome biome = biomes.getRaw(localX, y, localZ);
if (biome != null) {
mantle.set(x + localX, y, z + localZ, BiomeInjectMatter.get(biome.key()));
}
}
}
}
}
} finally {
chunk.release();
}
}
private void writeHistoricalColumn(
Hunk<NativeBiome> output,
Mantle<Matter> mantle,
int localX,
int localZ,
int worldX,
@@ -167,12 +132,9 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
applyBiomeRange(
localX,
localZ,
worldX,
worldZ,
rangeStart,
internalY - 1,
output,
mantle,
resolvePhysicalKey(activeKey)
);
}
@@ -181,33 +143,14 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
}
}
private void writeColumn(
Hunk<NativeBiome> output,
Mantle<Matter> mantle,
int localX,
int localZ,
int worldX,
int worldZ,
int height,
ResolvedBiome resolved
) {
if (resolved.biome() != null) {
output.set(localX, 0, localZ, localX, height - 1, localZ, resolved.biome());
}
if (mantle != null) {
mantle.set(worldX, 0, worldZ, resolved.matter());
}
}
private void applyDimensionStackBiomes(
int localX,
int localZ,
int worldX,
int worldZ,
Hunk<NativeBiome> output,
Mantle<Matter> mantle,
Engine engine,
ResolvedBiome bottom,
NativeBiome bottom,
DimensionStackLayout layout
) {
List<DimensionStackLayout.Layer> layers = layout.layersBottomToTop();
@@ -222,18 +165,15 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
applyBiomeRange(
localX,
localZ,
worldX,
worldZ,
gapMinY,
gapMaxY,
output,
mantle,
bottom
);
if (!layer.visible()) {
continue;
}
ResolvedBiome resolved = bottom;
NativeBiome resolved = bottom;
if (layer.biome() != null) {
IrisDimension dimension = layer.terrainContext().getDimension();
resolved = resolve(biomeKey(
@@ -248,12 +188,9 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
applyBiomeRange(
localX,
localZ,
worldX,
worldZ,
layer.renderMinY(),
layer.renderMaxY(),
output,
mantle,
resolved
);
}
@@ -262,46 +199,15 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
private void applyBiomeRange(
int localX,
int localZ,
int worldX,
int worldZ,
int minimumY,
int maximumY,
Hunk<NativeBiome> output,
Mantle<Matter> mantle,
ResolvedBiome resolved
NativeBiome biome
) {
if (minimumY > maximumY) {
if (minimumY > maximumY || biome == null) {
return;
}
if (mantle != null) {
clearBiomeMatterRange(mantle, worldX, worldZ, minimumY, maximumY);
}
if (resolved.biome() != null) {
output.set(
localX,
minimumY,
localZ,
localX,
maximumY,
localZ,
resolved.biome()
);
}
if (mantle != null && resolved.matter() != null) {
mantle.set(worldX, minimumY, worldZ, resolved.matter());
}
}
static void clearBiomeMatterRange(
Mantle<Matter> mantle,
int worldX,
int worldZ,
int minimumY,
int maximumY
) {
for (int y = minimumY; y <= maximumY; y++) {
mantle.remove(worldX, y, worldZ, MatterBiomeInject.class);
}
output.set(localX, minimumY, localZ, localX, maximumY, localZ, biome);
}
private String biomeKey(
@@ -319,37 +225,28 @@ public class IrisBiomeActuator extends EngineAssignedActuator<NativeBiome> {
return biome.getSkyBiomeKey(rng, engine, x, y, z);
}
private ResolvedBiome resolve(String key) {
ResolvedBiome cached = key == null ? null : resolvedBiomes.get(key);
private NativeBiome resolve(String key) {
NativeBiome cached = key == null ? null : resolvedBiomes.get(key);
if (cached != null) {
return cached;
}
IrisPlatform platform = IrisPlatforms.get();
NativeBiome biome = platform.registries().biome(key);
ResolvedBiome resolved = new ResolvedBiome(
biome,
BiomeInjectMatter.get(platform.biomeWriter().biomeIdFor(key))
);
NativeBiome biome = IrisPlatforms.get().registries().biome(key);
if (key != null && biome != null) {
resolvedBiomes.put(key, resolved);
resolvedBiomes.put(key, biome);
}
return resolved;
return biome;
}
private ResolvedBiome resolvePhysicalKey(String key) {
ResolvedBiome cached = resolvedPhysicalBiomes.get(key);
private NativeBiome resolvePhysicalKey(String key) {
NativeBiome cached = resolvedPhysicalBiomes.get(key);
if (cached != null) {
return cached;
}
NativeBiome biome = IrisPlatforms.get().registries().biome(key);
ResolvedBiome resolved = new ResolvedBiome(biome, BiomeInjectMatter.get(key));
if (biome != null) {
resolvedPhysicalBiomes.put(key, resolved);
resolvedPhysicalBiomes.put(key, biome);
}
return resolved;
}
private record ResolvedBiome(NativeBiome biome, MatterBiomeInject matter) {
return biome;
}
}
@@ -40,6 +40,7 @@ import art.arcane.iris.generation.decoration.IrisProceduralBlocks;
import art.arcane.iris.generation.hydrology.IrisRiverMaterialConfig;
import art.arcane.iris.generation.hydrology.cave.HydrologyCaveAction;
import art.arcane.iris.generation.hydrology.cave.HydrologyCaveCell;
import art.arcane.iris.generation.chunk.ColumnExtent;
import art.arcane.iris.generation.context.ChunkContext;
import art.arcane.iris.generation.context.IrisContext;
import art.arcane.iris.generation.block.B;
@@ -896,12 +897,17 @@ public class IrisCarveModifier extends EngineAssignedModifier<NativeBlockState>
int width = output.getWidth();
int depth = output.getDepth();
int height = output.getHeight();
ColumnExtent extent = output instanceof ColumnExtent columnExtent ? columnExtent : null;
for (int localX = 0; localX < width; localX++) {
for (int localZ = 0; localZ < depth; localZ++) {
int worldX = blockX + localX;
int worldZ = blockZ + localZ;
int floor = -1;
int top = extent == null ? height - 1 : extent.highestStoredY(localX, localZ);
for (int y = 1; y < height; y++) {
if (y > top) {
break;
}
NativeBlockState state = output.getRaw(localX, y, localZ);
if (B.isSolid(state)) {
if (floor >= 0) {
@@ -922,6 +928,9 @@ public class IrisCarveModifier extends EngineAssignedModifier<NativeBlockState>
worldX, worldZ, resolver, caveBiomes, customBiomes);
decorateZone(output, zone, localX, localZ, worldX, worldZ,
floorBiome, ceilingBiome);
if (extent != null) {
top = extent.highestStoredY(localX, localZ);
}
}
floor = -1;
}
@@ -106,7 +106,21 @@ public final class IrisDimensionStackActuator extends EngineAssignedActuator<Nat
}
}
public static void clearNaturalMetadata(MantleChunk<Matter> chunk, ChunkContext context, int height) {
public static void clearHostNaturalMetadata(Engine engine, int x, int z, int height, ChunkContext context) {
if (!context.hasDimensionStack() || !context.getComplex().allowsMantleChunkWrite(x >> 4, z >> 4)) {
return;
}
MantleChunk<Matter> chunk = engine.getMantle().getMantle().getChunk(x >> 4, z >> 4).use();
try {
synchronized (chunk) {
clearNaturalMetadata(chunk, context, height);
}
} finally {
chunk.release();
}
}
static void clearNaturalMetadata(MantleChunk<Matter> chunk, ChunkContext context, int height) {
MetadataCleaner metadata = new MetadataCleaner(chunk, height);
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
@@ -44,7 +44,6 @@ import art.arcane.volmlib.util.hunk.Hunk;
import art.arcane.volmlib.util.noise.CNG;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.slices.BiomeInjectMatter;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
@@ -431,7 +430,6 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Nativ
}
context.setNaturalBiome(wx & 15, y, wz & 15, selected);
context.floatingBiomes(chunkHeight).record(wx & 15, y, wz & 15, matter.identity());
getEngine().getMantle().getMantle().set(wx, y, wz, BiomeInjectMatter.get(selected.key()));
}
} catch (Throwable e) {
IrisLogging.reportError(e);
@@ -18,6 +18,7 @@
package art.arcane.iris.generation.stage;
import art.arcane.iris.generation.chunk.ColumnExtent;
import art.arcane.iris.generation.decoration.IrisSpeleothems;
import art.arcane.iris.generation.runtime.Engine;
import art.arcane.iris.generation.runtime.EngineAssignedModifier;
@@ -203,7 +204,7 @@ public class IrisPerfectionModifier extends EngineAssignedModifier<NativeBlockSt
}
private int getHeight(Hunk<NativeBlockState> output, int x, int z) {
for (int i = output.getHeight() - 1; i >= 0; i--) {
for (int i = Math.min(output.getHeight() - 1, ColumnExtent.highestStoredY(output, x, z)); i >= 0; i--) {
NativeBlockState b = output.get(x, i, z);
if (b != null) {
@@ -57,29 +57,32 @@ public final class BukkitBlockState implements NativeBlockState {
// the canonical NMS state): a hit skips getAsString(), which built the full property
// string on EVERY of() call — the dominant cost of the 99.9% hit case.
private static final ConcurrentHashMap<BlockData, BukkitBlockState> DATA_CACHE = new ConcurrentHashMap<>();
private static final byte UNKNOWN = 0;
private static final byte FALSE = 1;
private static final byte TRUE = 2;
private final BlockData data;
private final String key;
private final String namespace;
private volatile String materialKey;
private volatile Boolean air;
private volatile Boolean solid;
private volatile Boolean occluding;
private volatile Boolean fluid;
private volatile Boolean water;
private volatile Boolean waterLogged;
private volatile Boolean lit;
private volatile Boolean updatable;
private volatile Boolean foliage;
private volatile Boolean treeBlock;
private volatile Boolean foliagePlantable;
private volatile Boolean decorant;
private volatile Boolean storage;
private volatile Boolean storageChest;
private volatile Boolean ore;
private volatile Boolean deepSlate;
private volatile Boolean vineBlock;
private volatile Boolean tileEntity;
private String materialKey;
private byte air;
private byte solid;
private byte occluding;
private byte fluid;
private byte water;
private byte waterLogged;
private byte lit;
private byte updatable;
private byte foliage;
private byte treeBlock;
private byte foliagePlantable;
private byte decorant;
private byte storage;
private byte storageChest;
private byte ore;
private byte deepSlate;
private byte vineBlock;
private byte tileEntity;
private BukkitBlockState(BlockData data, String key) {
this.data = data;
@@ -87,6 +90,10 @@ public final class BukkitBlockState implements NativeBlockState {
this.namespace = BlockStateKey.namespace(key);
}
private static byte flag(boolean value) {
return value ? TRUE : FALSE;
}
public static BukkitBlockState of(BlockData data) {
if (data instanceof IrisCustomData custom) {
return new BukkitBlockState(data, custom.getCustom().toString());
@@ -198,32 +205,32 @@ public final class BukkitBlockState implements NativeBlockState {
@Override
public boolean isAir() {
Boolean cached = air;
if (cached == null) {
cached = BukkitBlockResolution.isAir(data);
byte cached = air;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isAir(data));
air = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isSolid() {
Boolean cached = solid;
if (cached == null) {
cached = BukkitBlockResolution.isSolid(data);
byte cached = solid;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isSolid(data));
solid = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isOccluding() {
Boolean cached = occluding;
if (cached == null) {
cached = data.getMaterial().isOccluding();
byte cached = occluding;
if (cached == UNKNOWN) {
cached = flag(data.getMaterial().isOccluding());
occluding = cached;
}
return cached;
return cached == TRUE;
}
@Override
@@ -248,142 +255,142 @@ public final class BukkitBlockState implements NativeBlockState {
@Override
public boolean isFluid() {
Boolean cached = fluid;
if (cached == null) {
cached = BukkitBlockResolution.isFluid(data);
byte cached = fluid;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isFluid(data));
fluid = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isWater() {
Boolean cached = water;
if (cached == null) {
cached = BukkitBlockResolution.isWater(data);
byte cached = water;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isWater(data));
water = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isWaterLogged() {
Boolean cached = waterLogged;
if (cached == null) {
cached = BukkitBlockResolution.isWaterLogged(data);
byte cached = waterLogged;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isWaterLogged(data));
waterLogged = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isLit() {
Boolean cached = lit;
if (cached == null) {
cached = BukkitBlockResolution.isLit(data);
byte cached = lit;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isLit(data));
lit = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isUpdatable() {
Boolean cached = updatable;
if (cached == null) {
cached = BukkitBlockResolution.isUpdatable(data);
byte cached = updatable;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isUpdatable(data));
updatable = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isFoliage() {
Boolean cached = foliage;
if (cached == null) {
cached = BukkitBlockResolution.isFoliage(data);
byte cached = foliage;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isFoliage(data));
foliage = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isTreeBlock() {
Boolean cached = treeBlock;
if (cached == null) {
cached = Tag.LOGS.isTagged(data.getMaterial()) || Tag.LEAVES.isTagged(data.getMaterial());
byte cached = treeBlock;
if (cached == UNKNOWN) {
cached = flag(Tag.LOGS.isTagged(data.getMaterial()) || Tag.LEAVES.isTagged(data.getMaterial()));
treeBlock = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isFoliagePlantable() {
Boolean cached = foliagePlantable;
if (cached == null) {
cached = BukkitBlockResolution.isFoliagePlantable(data);
byte cached = foliagePlantable;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isFoliagePlantable(data));
foliagePlantable = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isDecorant() {
Boolean cached = decorant;
if (cached == null) {
cached = BukkitBlockResolution.isDecorant(data);
byte cached = decorant;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isDecorant(data));
decorant = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isStorage() {
Boolean cached = storage;
if (cached == null) {
cached = BukkitBlockResolution.isStorage(data);
byte cached = storage;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isStorage(data));
storage = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isStorageChest() {
Boolean cached = storageChest;
if (cached == null) {
cached = BukkitBlockResolution.isStorageChest(data);
byte cached = storageChest;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isStorageChest(data));
storageChest = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isOre() {
Boolean cached = ore;
if (cached == null) {
cached = BukkitBlockResolution.isOre(data);
byte cached = ore;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isOre(data));
ore = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isDeepSlate() {
Boolean cached = deepSlate;
if (cached == null) {
cached = BukkitBlockResolution.isDeepSlate(data);
byte cached = deepSlate;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isDeepSlate(data));
deepSlate = cached;
}
return cached;
return cached == TRUE;
}
@Override
public boolean isVineBlock() {
Boolean cached = vineBlock;
if (cached == null) {
cached = BukkitBlockResolution.isVineBlock(data);
byte cached = vineBlock;
if (cached == UNKNOWN) {
cached = flag(BukkitBlockResolution.isVineBlock(data));
vineBlock = cached;
}
return cached;
return cached == TRUE;
}
@Override
@@ -398,12 +405,12 @@ public final class BukkitBlockState implements NativeBlockState {
@Override
public boolean hasTileEntity() {
Boolean cached = tileEntity;
if (cached == null) {
cached = INMS.get().hasTile(data.getMaterial());
byte cached = tileEntity;
if (cached == UNKNOWN) {
cached = flag(INMS.get().hasTile(data.getMaterial()));
tileEntity = cached;
}
return cached;
return cached == TRUE;
}
@Override
@@ -633,7 +633,7 @@ public final class BukkitBiomePolicy<H, V> implements NativeBiomeSourcePolicy<H>
return existingHolder;
}
if (noiseBiomeCache.size() > NOISE_BIOME_CACHE_MAX) {
if ((y & 63) == 0 && noiseBiomeCache.size() > NOISE_BIOME_CACHE_MAX) {
noiseBiomeCache.clear();
}
@@ -72,7 +72,7 @@ import art.arcane.volmlib.util.bukkit.WorldIdentity;
import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.math.M;
import art.arcane.volmlib.util.hunk.Hunk;
import art.arcane.iris.generation.chunk.TerrainChunkBiomeHunkView;
import art.arcane.iris.generation.chunk.ColumnBiomeHunk;
import art.arcane.iris.generation.chunk.ChunkDataHunkHolder;
import art.arcane.iris.generation.context.ChunkContext;
import art.arcane.iris.generation.context.IrisContext;
@@ -1298,12 +1298,11 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
try {
Engine engine = getEngine(world);
StudioGenerator selected = computeStudioGenerator();
TerrainChunk tc = TerrainChunk.create(d);
if (selected != null) {
selected.generateChunk(engine, tc, x, z);
selected.generateChunk(engine, TerrainChunk.create(d), x, z);
} else {
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(d);
Hunk<NativeBiome> biomes = new TerrainChunkBiomeHunkView(tc);
Hunk<NativeBiome> biomes = new ColumnBiomeHunk(d.getMaxHeight() - d.getMinHeight());
try (GenerationHistoryRuntimeRouter.CoordinateScope historyScope =
openGenerationHistoryCoordinateScope(engine, x << 4, z << 4);
GenerationSessionLease lease = engine.acquireGenerationLease("bukkit_terrain_stage");