This commit is contained in:
Brian Neumann-Fopiano
2026-07-04 10:35:34 -04:00
parent 927c48f8c7
commit d1cc83a40b
9 changed files with 259 additions and 44 deletions
@@ -68,7 +68,12 @@ import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public final class IrisModdedChunkGenerator extends ChunkGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
@@ -77,6 +82,41 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
Codec.STRING.fieldOf("dimension").forGetter((IrisModdedChunkGenerator generator) -> generator.dimensionKey)
).apply(instance, IrisModdedChunkGenerator::new));
private static final AtomicInteger GEN_THREAD_SEQ = new AtomicInteger();
private static final boolean PARALLEL_CHUNK_SYSTEM = detectParallelChunkSystem();
private static final ExecutorService GEN_POOL = createGenPool();
private static boolean detectParallelChunkSystem() {
String[] markers = {
"com.ishland.c2me.base.ModProperties",
"com.ishland.c2me.base.common.config.C2MEConfig",
"com.ishland.c2me.opts.chunkio.ModProperties",
"ca.spottedleaf.moonrise.common.util.MoonriseCommon"
};
for (String marker : markers) {
try {
Class.forName(marker, false, IrisModdedChunkGenerator.class.getClassLoader());
return true;
} catch (Throwable ignored) {
}
}
return false;
}
private static ExecutorService createGenPool() {
int threads = Math.max(2, Runtime.getRuntime().availableProcessors());
ThreadPoolExecutor pool = new ThreadPoolExecutor(
threads, threads, 30L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
runnable -> {
Thread thread = new Thread(runnable, "Iris ModGen-" + GEN_THREAD_SEQ.incrementAndGet());
thread.setDaemon(true);
return thread;
});
pool.allowCoreThreadTimeOut(true);
return pool;
}
private final String dimensionKey;
private final String defaultPack;
private final String defaultDimensionKey;
@@ -221,6 +261,20 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
int dimMaxY = generationEngine.getMaxHeight();
int height = dimMaxY - dimMinY;
PlatformBlockState air = IrisPlatforms.get().registries().air();
Registry<Biome> biomeRegistry = structureManager.registryAccess().lookupOrThrow(Registries.BIOME);
if (PARALLEL_CHUNK_SYSTEM) {
return CompletableFuture.completedFuture(
generateTerrain(chunk, generationEngine, pos, dimMinY, height, air, biomeRegistry, randomState));
}
return CompletableFuture.supplyAsync(
() -> generateTerrain(chunk, generationEngine, pos, dimMinY, height, air, biomeRegistry, randomState),
GEN_POOL);
}
private ChunkAccess generateTerrain(ChunkAccess chunk, Engine generationEngine, ChunkPos pos,
int dimMinY, int height, PlatformBlockState air,
Registry<Biome> biomeRegistry, RandomState randomState) {
ModdedBlockBuffer blocks = new ModdedBlockBuffer(height, air);
Hunk<PlatformBiome> biomes = Hunk.newArrayHunk(16, height, 16);
try {
@@ -228,7 +282,7 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
} catch (GenerationSessionException e) {
if (e.isExpectedTeardown()) {
LOGGER.debug("Iris chunk {},{} skipped: engine sealed for hotload/teardown", pos.x(), pos.z());
return CompletableFuture.completedFuture(chunk);
return chunk;
}
LOGGER.error("Iris failed to generate chunk {},{}", pos.x(), pos.z(), e);
throw new IllegalStateException("Iris generation failed for chunk " + pos.x() + "," + pos.z(), e);
@@ -239,9 +293,8 @@ public final class IrisModdedChunkGenerator extends ChunkGenerator {
writeBlocks(chunk, blocks, dimMinY, height);
Heightmap.primeHeightmaps(chunk, EnumSet.of(Heightmap.Types.WORLD_SURFACE_WG, Heightmap.Types.OCEAN_FLOOR_WG));
Registry<Biome> biomeRegistry = structureManager.registryAccess().lookupOrThrow(Registries.BIOME);
chunk.fillBiomesFromNoise(new HunkBiomeResolver(this, biomes, biomeRegistry, pos, dimMinY, height), randomState.sampler());
return CompletableFuture.completedFuture(chunk);
return chunk;
}
private Holder<Biome> fallbackBiome(Registry<Biome> registry) {
@@ -24,6 +24,7 @@ import art.arcane.iris.core.pregenerator.PregenMantleBackpressure;
import art.arcane.iris.core.pregenerator.PregeneratorMethod;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.volmlib.util.mantle.runtime.Mantle;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ChunkResult;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.TicketType;
@@ -31,21 +32,36 @@ import net.minecraft.world.level.ChunkPos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public final class ModdedPregenMethod implements PregeneratorMethod {
private static final Logger LOGGER = LoggerFactory.getLogger("Iris");
private static final TicketType PREGEN_TICKET = new TicketType(TicketType.NO_TIMEOUT, TicketType.FLAG_LOADING | TicketType.FLAG_KEEP_DIMENSION_ACTIVE);
private static final int ADAPTIVE_TIMEOUT_STEP = 3;
private static final long ADAPTIVE_RECOVERY_INTERVAL = 64L;
private static final boolean PARALLEL_CHUNK_SYSTEM = detectParallelChunkSystem();
private final ServerLevel level;
private final Engine engine;
private final boolean sync;
private final int maxInFlight;
private final int minInFlight;
private final Semaphore semaphore;
private final int permits;
private final Object permitMonitor = new Object();
private final AtomicInteger inFlight = new AtomicInteger();
private final AtomicInteger inFlightPeak = new AtomicInteger();
private final AtomicInteger adaptiveLimit;
private final AtomicInteger timeoutStreak = new AtomicInteger();
private final AtomicLong completed = new AtomicLong();
private final int timeoutSeconds;
private final PregenMantleBackpressure backpressure;
@@ -57,9 +73,11 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
this.level = level;
this.engine = engine;
this.sync = sync;
this.permits = Math.min(96, Math.max(8, Runtime.getRuntime().availableProcessors() * 2));
this.semaphore = new Semaphore(permits, true);
IrisSettings.IrisSettingsPregen pregen = IrisSettings.get().getPregen();
this.maxInFlight = Math.max(8, pregen.getModdedPregenInFlight());
this.minInFlight = Math.max(4, Math.min(16, maxInFlight / 4));
this.semaphore = new Semaphore(maxInFlight, true);
this.adaptiveLimit = new AtomicInteger(maxInFlight);
this.timeoutSeconds = Math.max(120, pregen.getChunkLoadTimeoutSeconds());
this.backpressure = new PregenMantleBackpressure(
this::getMantle,
@@ -73,19 +91,29 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
@Override
public void init() {
LOGGER.info("Iris modded pregen init: dim={} mode={} inFlightCap={} timeout={}s",
level.dimension().identifier(), sync ? "sync" : "async", sync ? 1 : permits, timeoutSeconds);
LOGGER.info("Iris modded pregen init: dim={} mode={} inFlightCap={} timeout={}s workerPool={} parallelChunkSystem={}",
level.dimension().identifier(),
sync ? "sync" : "async",
sync ? 1 : maxInFlight,
timeoutSeconds,
describeWorkerPool(),
PARALLEL_CHUNK_SYSTEM ? "yes" : "no");
if (!sync && !PARALLEL_CHUNK_SYSTEM) {
LOGGER.info("Iris pregen note: this loader uses the vanilla main-thread chunk system, which caps pregen throughput. For Bukkit-level speed on Fabric install C2ME (Concurrent Chunk Management Engine); on servers use Paper.");
}
}
@Override
public void close() {
if (!sync) {
try {
semaphore.tryAcquire(permits, 5, TimeUnit.SECONDS);
semaphore.tryAcquire(maxInFlight, 5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
LOGGER.info("Iris modded pregen done: dim={} completed={} peakInFlight={} finalLimit={}",
level.dimension().identifier(), completed.get(), inFlightPeak.get(), adaptiveLimit.get());
saveLevel(true);
}
@@ -174,11 +202,17 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
private void generateChunkAsync(int x, int z, PregenListener listener) {
listener.onChunkGenerating(x, z);
try {
synchronized (permitMonitor) {
while (inFlight.get() >= adaptiveLimit.get()) {
permitMonitor.wait(500L);
}
}
semaphore.acquire();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
markSubmitted();
ChunkPos pos = new ChunkPos(x, z);
CompletableFuture<?> loadFuture = CompletableFuture
@@ -189,6 +223,9 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
level.getServer().execute(() -> level.getChunkSource().removeTicketWithRadius(PREGEN_TICKET, pos, 0));
try {
if (error != null) {
if (unwrap(error) instanceof TimeoutException) {
onTimeout();
}
LOGGER.warn("Iris pregen chunk {},{} failed: {}", x, z, error.toString());
listener.onChunkFailed(x, z);
return;
@@ -198,15 +235,70 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
listener.onChunkFailed(x, z);
return;
}
onSuccess();
listener.onChunkGenerated(x, z);
cleanupMantleChunk(x, z);
listener.onChunkCleaned(x, z);
} finally {
markFinished();
semaphore.release();
}
});
}
private void markSubmitted() {
int current = inFlight.incrementAndGet();
inFlightPeak.accumulateAndGet(current, Math::max);
}
private void markFinished() {
inFlight.decrementAndGet();
completed.incrementAndGet();
synchronized (permitMonitor) {
permitMonitor.notifyAll();
}
}
private void onTimeout() {
if (timeoutStreak.incrementAndGet() % ADAPTIVE_TIMEOUT_STEP == 0) {
adjustAdaptiveLimit(-1);
}
}
private void onSuccess() {
int streak = timeoutStreak.get();
if (streak > 0) {
timeoutStreak.compareAndSet(streak, Math.max(0, streak - 2));
return;
}
if ((completed.get() & (ADAPTIVE_RECOVERY_INTERVAL - 1L)) == 0L) {
adjustAdaptiveLimit(1);
}
}
private void adjustAdaptiveLimit(int direction) {
while (true) {
int current = adaptiveLimit.get();
int next;
if (direction < 0) {
next = Math.max(minInFlight, current - 1);
} else {
int deficit = maxInFlight - current;
int step = deficit > (maxInFlight / 2) ? Math.max(2, maxInFlight / 8) : 1;
next = Math.min(maxInFlight, current + step);
}
if (next == current) {
return;
}
if (adaptiveLimit.compareAndSet(current, next)) {
synchronized (permitMonitor) {
permitMonitor.notifyAll();
}
return;
}
}
}
private void cleanupMantleChunk(int x, int z) {
try {
engine.getMantle().forceCleanupChunk(x, z);
@@ -214,6 +306,47 @@ public final class ModdedPregenMethod implements PregeneratorMethod {
}
}
private String describeWorkerPool() {
try {
Field field = MinecraftServer.class.getDeclaredField("executor");
field.setAccessible(true);
Object exec = field.get(level.getServer());
if (exec == null) {
return "unknown";
}
if (exec instanceof ThreadPoolExecutor tpe) {
return "ThreadPoolExecutor(core=" + tpe.getCorePoolSize() + ",max=" + tpe.getMaximumPoolSize() + ")";
}
if (exec instanceof ForkJoinPool fjp) {
return "ForkJoinPool(parallelism=" + fjp.getParallelism() + ")";
}
return exec.getClass().getSimpleName();
} catch (Throwable e) {
return "unknown";
}
}
private static Throwable unwrap(Throwable error) {
return error != null && error.getCause() != null ? error.getCause() : error;
}
private static boolean detectParallelChunkSystem() {
String[] markers = {
"com.ishland.c2me.base.ModProperties",
"com.ishland.c2me.base.common.config.C2MEConfig",
"com.ishland.c2me.opts.chunkio.ModProperties",
"ca.spottedleaf.moonrise.common.util.MoonriseCommon"
};
for (String marker : markers) {
try {
Class.forName(marker, false, ModdedPregenMethod.class.getClassLoader());
return true;
} catch (Throwable ignored) {
}
}
return false;
}
@Override
public Mantle getMantle() {
return engine.getMantle().getMantle();
@@ -119,6 +119,9 @@ public final class ModdedEngineMaintenanceService implements ModdedTickableServi
if (engine == null || engine.isClosed() || engine.getMantle().getMantle().isClosed()) {
return;
}
if (pregenTargets(engine)) {
return;
}
if (flush) {
try {
engine.save();
@@ -154,11 +154,21 @@ public class IrisSettings {
public int maxResidentTectonicPlates = 96;
public int mantleBackpressureWaitMs = 25;
public int mantleBackpressureTimeoutMs = 60_000;
public int moddedPregenInFlight = 0;
public int getChunkLoadTimeoutSeconds() {
return Math.max(5, Math.min(chunkLoadTimeoutSeconds, 120));
}
public int getModdedPregenInFlight() {
if (moddedPregenInFlight > 0) {
return Math.min(512, moddedPregenInFlight);
}
int cpu = Math.max(1, Runtime.getRuntime().availableProcessors());
return Math.max(16, Math.min(48, cpu * 2));
}
public int getMaxResidentTectonicPlates() {
return Math.max(16, maxResidentTectonicPlates);
}
@@ -23,7 +23,7 @@ import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.data.cache.Cache;
import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.object.*;
import art.arcane.iris.platform.bukkit.BukkitBiome;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.IrisLogging;
import art.arcane.iris.spi.PlatformBiome;
import art.arcane.iris.spi.PlatformBlockState;
@@ -215,7 +215,7 @@ public class IrisComplex implements DataProvider {
regionStream.contextInjecting((c, xx, zz) -> IrisContext.getOr(engine).getChunkContext().getRegion().get(xx, zz)).get(x, z), x, z, fluidHeight))
.cache2D("trueBiomeStream", engine, cacheSize).waste("True Biome Stream");
trueBiomeDerivativeStream = trueBiomeStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getBiome().get(x, z))
.convert((b) -> (PlatformBiome) BukkitBiome.of(b.getDerivative())).cache2D("trueBiomeDerivativeStream", engine, cacheSize).waste("True Biome Derivative Stream");
.convert((b) -> IrisPlatforms.get().registries().biome(b.getDerivativeKey())).cache2D("trueBiomeDerivativeStream", engine, cacheSize).waste("True Biome Derivative Stream");
heightFluidStream = heightStream.contextInjecting((c, x, z) -> IrisContext.getOr(engine).getChunkContext().getHeight().getDouble(x, z))
.max(fluidHeight).cache2DDouble("heightFluidStream", engine, cacheSize).waste("Height Fluid Stream");
maxHeightStream = ProceduralStream.ofDouble((x, z) -> height).waste("Max Height Stream");
@@ -22,7 +22,6 @@ import art.arcane.iris.engine.framework.Engine;
import art.arcane.iris.engine.framework.EngineAssignedActuator;
import art.arcane.iris.engine.object.IrisBiome;
import art.arcane.iris.engine.object.IrisBiomeCustom;
import art.arcane.iris.platform.bukkit.BukkitBiome;
import art.arcane.iris.util.project.context.ChunkContext;
import art.arcane.volmlib.util.documentation.BlockCoordinates;
import art.arcane.iris.util.project.hunk.Hunk;
@@ -33,7 +32,6 @@ import art.arcane.volmlib.util.scheduling.ChronoLatch;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformBiome;
import org.bukkit.block.Biome;
public class IrisBiomeActuator extends EngineAssignedActuator<PlatformBiome> {
private final RNG rng;
@@ -62,11 +60,9 @@ public class IrisBiomeActuator extends EngineAssignedActuator<PlatformBiome> {
biome = IrisPlatforms.get().registries().biome(key);
matter = BiomeInjectMatter.get(IrisPlatforms.get().biomeWriter().biomeIdFor(key));
} else {
Biome v = ib.getSkyBiome(rng, x, 0, z);
PlatformBiome fallback = BukkitBiome.of(v);
PlatformBiome resolved = IrisPlatforms.get().registries().biome(fallback.key());
biome = resolved == null ? fallback : resolved;
matter = BiomeInjectMatter.get(v);
String skyKey = ib.getSkyBiomeKey(rng, x, 0, z);
biome = IrisPlatforms.get().registries().biome(skyKey);
matter = BiomeInjectMatter.get(IrisPlatforms.get().biomeWriter().biomeIdFor(skyKey));
}
if (biome != null) {
@@ -18,7 +18,6 @@
package art.arcane.iris.engine.modifier;
import art.arcane.iris.platform.bukkit.BukkitBlockResolution;
import art.arcane.iris.core.loader.IrisData;
import art.arcane.iris.engine.IrisComplex;
@@ -46,11 +45,8 @@ import art.arcane.volmlib.util.math.RNG;
import art.arcane.volmlib.util.matter.MatterBiomeInject;
import art.arcane.volmlib.util.matter.slices.BiomeInjectMatter;
import art.arcane.volmlib.util.scheduling.PrecisionStopwatch;
import art.arcane.iris.platform.bukkit.BukkitBlockState;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformBlockState;
import org.bukkit.block.Biome;
import org.bukkit.block.data.BlockData;
import java.util.IdentityHashMap;
@@ -261,11 +257,10 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Platf
IrisFloatingChildBiomes entry = sample.entry;
Integer localFluidHeight = entry.getLocalFluidHeight();
if (localFluidHeight != null && localFluidHeight > 0) {
BlockData rawFluid = BukkitBlockResolution.get(entry.getFluidBlock());
if (rawFluid == null) {
rawFluid = BukkitBlockResolution.get("minecraft:water");
PlatformBlockState fluid = B.getStateOrNull(entry.getFluidBlock());
if (fluid == null) {
fluid = B.getState("minecraft:water");
}
PlatformBlockState fluid = BukkitBlockState.of(rawFluid);
int fluidCap = Math.min(sample.thickness - 1, localFluidHeight);
for (int k = 1; k <= fluidCap; k++) {
if (sample.solidMask[k]) {
@@ -324,7 +319,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Platf
int max = Math.max(1, chunkHeight - topY);
if (topY + 1 < chunkHeight) {
PlatformBlockState above = output.get(xf, topY + 1, zf);
if (above == null || BukkitBlockResolution.isAir((BlockData) above.nativeHandle())) {
if (above == null || above.isAir()) {
try {
RNG colRng = rng.nextParallelRNG((int) FloatingIslandSample.columnSeed(baseSeed, wx, wz));
FloatingDecorator.decorateColumn(getEngine(), target, IrisDecorationPart.NONE, xf, zf, wx, wz, topY, max, output, colRng, NOOP_DECORATION_MISS);
@@ -357,7 +352,7 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Platf
fluidTopY = y;
}
}
if (fluidTopY > 0 && fluidTopY + 1 < chunkHeight && BukkitBlockResolution.isAir(unwrap(output.get(xf, fluidTopY + 1, zf)))) {
if (fluidTopY > 0 && fluidTopY + 1 < chunkHeight && B.isAir(output.get(xf, fluidTopY + 1, zf))) {
try {
seaSurfaceDecorator.decorate(xf, zf, wx, wx + 1, wx - 1, wz, wz + 1, wz - 1, output, target, fluidTopY, chunkHeight);
} catch (Throwable e) {
@@ -397,17 +392,12 @@ public class IrisFloatingChildBiomeModifier extends EngineAssignedModifier<Platf
}
}
private static BlockData unwrap(PlatformBlockState state) {
return state == null ? null : (BlockData) state.nativeHandle();
}
private MatterBiomeInject createSkyBiomeMatter(IrisBiome target, int wx, int wz) {
if (target.isCustom()) {
IrisBiomeCustom custom = target.getCustomBiome(rng, wx, 0, wz);
return BiomeInjectMatter.get(IrisPlatforms.get().biomeWriter().biomeIdFor(getDimension().getLoadKey() + ":" + custom.getId()));
}
Biome v = target.getSkyBiome(rng, wx, 0, wz);
return BiomeInjectMatter.get(v);
return BiomeInjectMatter.get(IrisPlatforms.get().biomeWriter().biomeIdFor(target.getSkyBiomeKey(rng, wx, 0, wz)));
}
}
@@ -32,7 +32,7 @@ import art.arcane.volmlib.util.collection.KSet;
import art.arcane.iris.util.common.data.B;
import art.arcane.iris.util.common.data.DataProvider;
import art.arcane.iris.util.common.data.registry.RegistryUtil;
import art.arcane.volmlib.util.data.VanillaBiomeMap;
import art.arcane.volmlib.util.data.VanillaBiomeColors;
import art.arcane.volmlib.util.inventorygui.RandomColor;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.volmlib.util.math.RNG;
@@ -298,6 +298,10 @@ public class IrisBiome extends IrisRegistrant implements IRare {
return resolved == null ? namespacedBiomeKey(derivative) : resolved;
}
public String getDerivativeKey() {
return namespacedBiomeKey(derivative);
}
private static String namespacedBiomeKey(String key) {
if (key == null || key.isBlank()) {
return null;
@@ -802,6 +806,30 @@ public class IrisBiome extends IrisRegistrant implements IRare {
return getBiomeGenerator(rng).fit(getBiomeScatterResolved(), x, y, z);
}
public String getSkyBiomeKey(RNG rng, double x, double y, double z) {
if (biomeSkyScatter.size() == 1) {
return namespacedBiomeKey(biomeSkyScatter.get(0));
}
if (biomeSkyScatter.isEmpty()) {
return getGroundBiomeKey(rng, x, y, z);
}
return namespacedBiomeKey(biomeSkyScatter.get(getBiomeGenerator(rng).fit(0, biomeSkyScatter.size() - 1, x, y, z)));
}
public String getGroundBiomeKey(RNG rng, double x, double y, double z) {
if (biomeScatter.isEmpty()) {
return namespacedBiomeKey(derivative);
}
if (biomeScatter.size() == 1) {
return namespacedBiomeKey(biomeScatter.get(0));
}
return namespacedBiomeKey(biomeScatter.get(getBiomeGenerator(rng).fit(0, biomeScatter.size() - 1, x, y, z)));
}
public PlatformBlockState getSurfaceBlock(int x, int z, RNG rng, IrisData idm) {
if (getLayers().isEmpty()) {
return B.getState("AIR");
@@ -816,13 +844,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
return this.cacheColor.aquire(() -> {
if (this.color == null) {
RandomColor randomColor = new RandomColor(getName().hashCode());
if (this.getVanillaDerivative() == null) {
String vanillaKey = this.getVanillaDerivativeKey();
RandomColor.Color col = vanillaKey == null ? null : VanillaBiomeColors.getColorType(vanillaKey);
if (col == null) {
IrisLogging.warn("No vanilla biome found for " + getName());
return new Color(randomColor.randomColor());
}
RandomColor.Color col = VanillaBiomeMap.getColorType(this.getVanillaDerivative());
RandomColor.Luminosity lum = VanillaBiomeMap.getColorLuminosity(this.getVanillaDerivative());
RandomColor.SaturationType sat = VanillaBiomeMap.getColorSaturatiom(this.getVanillaDerivative());
RandomColor.Luminosity lum = VanillaBiomeColors.getColorLuminosity(vanillaKey);
RandomColor.SaturationType sat = VanillaBiomeColors.getColorSaturation(vanillaKey);
int newColorI = randomColor.randomColor(col, col == RandomColor.Color.MONOCHROME ? RandomColor.SaturationType.MONOCHROME : sat, lum);
return new Color(newColorI);
@@ -29,7 +29,7 @@ import art.arcane.volmlib.util.collection.KList;
import art.arcane.volmlib.util.collection.KMap;
import art.arcane.volmlib.util.collection.KSet;
import art.arcane.iris.util.common.data.DataProvider;
import art.arcane.volmlib.util.data.VanillaBiomeMap;
import art.arcane.volmlib.util.data.VanillaBiomeColors;
import art.arcane.volmlib.util.inventorygui.RandomColor;
import art.arcane.volmlib.util.json.JSONObject;
import art.arcane.volmlib.util.math.RNG;
@@ -448,10 +448,11 @@ public class IrisRegion extends IrisRegistrant implements IRare {
int index = rand.nextInt(biomes.size());
IrisBiome biome = biomes.get(index);
if (biome.getVanillaDerivative() != null) {
RandomColor.Color col = VanillaBiomeMap.getColorType(biome.getVanillaDerivative());
RandomColor.Luminosity lum = VanillaBiomeMap.getColorLuminosity(biome.getVanillaDerivative());
RandomColor.SaturationType sat = VanillaBiomeMap.getColorSaturatiom(biome.getVanillaDerivative());
String vanillaKey = biome.getVanillaDerivativeKey();
RandomColor.Color col = vanillaKey == null ? null : VanillaBiomeColors.getColorType(vanillaKey);
if (col != null) {
RandomColor.Luminosity lum = VanillaBiomeColors.getColorLuminosity(vanillaKey);
RandomColor.SaturationType sat = VanillaBiomeColors.getColorSaturation(vanillaKey);
int newColorI = randomColor.randomColor(col, col == RandomColor.Color.MONOCHROME ? RandomColor.SaturationType.MONOCHROME : sat, lum);
return new Color(newColorI);
}