mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 22:10:18 +00:00
Perf pass 2
This commit is contained in:
1 parent
79e80bef33
commit
3cfbcbf8ed
22 files changed
+800
-211
No files matched your search
+69
@@ -16,6 +16,9 @@ import art.arcane.volmlib.util.collection.KList;
|
|||||||
import art.arcane.volmlib.util.collection.KMap;
|
import art.arcane.volmlib.util.collection.KMap;
|
||||||
import art.arcane.iris.util.common.format.C;
|
import art.arcane.iris.util.common.format.C;
|
||||||
import art.arcane.iris.util.project.hunk.Hunk;
|
import art.arcane.iris.util.project.hunk.Hunk;
|
||||||
|
import art.arcane.iris.util.project.hunk.view.ChunkDataHunkHolder;
|
||||||
|
import art.arcane.iris.spi.PlatformBlockState;
|
||||||
|
import art.arcane.iris.util.common.data.IrisCustomData;
|
||||||
import art.arcane.volmlib.util.json.JSONObject;
|
import art.arcane.volmlib.util.json.JSONObject;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||||
import art.arcane.volmlib.util.matter.Matter;
|
import art.arcane.volmlib.util.matter.Matter;
|
||||||
@@ -772,6 +775,72 @@ public class NMSBinding implements INMSBinding {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applyChunkDataBlocks(ChunkGenerator.ChunkData chunkData, Hunk<PlatformBlockState> data) {
|
||||||
|
if (!(chunkData instanceof CraftChunkData craftChunkData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ChunkAccess access = craftChunkData.getHandle();
|
||||||
|
int minY = craftChunkData.getMinHeight();
|
||||||
|
int height = craftChunkData.getMaxHeight() - minY;
|
||||||
|
int accessMinY = access.getMinY();
|
||||||
|
int baseX = access.getPos().getMinBlockX();
|
||||||
|
int baseZ = access.getPos().getMinBlockZ();
|
||||||
|
boolean[] dirtySections = new boolean[access.getSections().length];
|
||||||
|
ChunkDataHunkHolder holder = data instanceof ChunkDataHunkHolder chunkDataHolder ? chunkDataHolder : null;
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
int blockY = y + minY;
|
||||||
|
int sectionIndex = (blockY - accessMinY) >> 4;
|
||||||
|
LevelChunkSection section = access.getSection(sectionIndex);
|
||||||
|
int sectionY = blockY & 15;
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
PlatformBlockState platformState = holder == null ? data.getRaw(x, y, z) : holder.getStoredRaw(x, y, z);
|
||||||
|
if (platformState == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockData blockData = (BlockData) platformState.nativeHandle();
|
||||||
|
if (blockData instanceof IrisCustomData customData) {
|
||||||
|
blockData = customData.getBase();
|
||||||
|
}
|
||||||
|
if (!(blockData instanceof CraftBlockData craftBlockData)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockState state = craftBlockData.getState();
|
||||||
|
BlockState oldState = section.states.getAndSetUnchecked(x, sectionY, z, state);
|
||||||
|
dirtySections[sectionIndex] = true;
|
||||||
|
if (state.hasBlockEntity()) {
|
||||||
|
BlockPos pos = new BlockPos(baseX + x, blockY, baseZ + z);
|
||||||
|
BlockEntity entity = ((EntityBlock) state.getBlock()).newBlockEntity(pos, state);
|
||||||
|
if (entity == null) {
|
||||||
|
access.removeBlockEntity(pos);
|
||||||
|
} else {
|
||||||
|
access.setBlockEntity(entity);
|
||||||
|
}
|
||||||
|
} else if (oldState != null && oldState.hasBlockEntity()) {
|
||||||
|
access.removeBlockEntity(new BlockPos(baseX + x, blockY, baseZ + z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < dirtySections.length; i++) {
|
||||||
|
if (dirtySections[i]) {
|
||||||
|
access.getSection(i).recalcBlockCounts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
IrisLogging.reportError(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean clearChunkBlocks(Chunk bukkitChunk) {
|
public boolean clearChunkBlocks(Chunk bukkitChunk) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import art.arcane.iris.core.nms.datapack.DataVersion;
|
|||||||
import art.arcane.iris.engine.data.chunk.TerrainChunk;
|
import art.arcane.iris.engine.data.chunk.TerrainChunk;
|
||||||
import art.arcane.iris.engine.framework.Engine;
|
import art.arcane.iris.engine.framework.Engine;
|
||||||
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
|
import art.arcane.iris.engine.platform.PlatformChunkGenerator;
|
||||||
|
import art.arcane.iris.spi.PlatformBlockState;
|
||||||
|
import art.arcane.iris.util.project.hunk.Hunk;
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.volmlib.util.collection.KMap;
|
import art.arcane.volmlib.util.collection.KMap;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||||
@@ -170,6 +172,10 @@ public interface INMSBinding {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default boolean applyChunkDataBlocks(ChunkGenerator.ChunkData chunkData, Hunk<PlatformBlockState> data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
default boolean clearChunkBlocks(Chunk chunk) {
|
default boolean clearChunkBlocks(Chunk chunk) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
+63
-20
@@ -41,6 +41,7 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -52,7 +53,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
|
private static final AtomicInteger THREAD_COUNT = new AtomicInteger();
|
||||||
private static final int ADAPTIVE_TIMEOUT_STEP = 3;
|
private static final int ADAPTIVE_TIMEOUT_STEP = 3;
|
||||||
private static final int ADAPTIVE_RECOVERY_INTERVAL = 8;
|
private static final int ADAPTIVE_RECOVERY_INTERVAL = 8;
|
||||||
private static final long CHUNK_CLEANUP_INTERVAL_MS = 15_000L;
|
private static final long CHUNK_CLEANUP_INTERVAL_MS = 10_000L;
|
||||||
private static final long CHUNK_CLEANUP_MIN_AGE_MS = 5_000L;
|
private static final long CHUNK_CLEANUP_MIN_AGE_MS = 5_000L;
|
||||||
private final World world;
|
private final World world;
|
||||||
private final IrisRuntimeSchedulerMode runtimeSchedulerMode;
|
private final IrisRuntimeSchedulerMode runtimeSchedulerMode;
|
||||||
@@ -73,6 +74,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
private final int timeoutWarnIntervalMs;
|
private final int timeoutWarnIntervalMs;
|
||||||
private final boolean urgent;
|
private final boolean urgent;
|
||||||
private final Map<Chunk, Long> lastUse;
|
private final Map<Chunk, Long> lastUse;
|
||||||
|
private final ConcurrentLinkedQueue<ChunkUse> chunkUseQueue;
|
||||||
private final AtomicInteger adaptiveInFlightLimit;
|
private final AtomicInteger adaptiveInFlightLimit;
|
||||||
private final int adaptiveMinInFlightLimit;
|
private final int adaptiveMinInFlightLimit;
|
||||||
private final AtomicInteger timeoutStreak = new AtomicInteger();
|
private final AtomicInteger timeoutStreak = new AtomicInteger();
|
||||||
@@ -85,6 +87,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
private final AtomicLong failed = new AtomicLong();
|
private final AtomicLong failed = new AtomicLong();
|
||||||
private final AtomicLong lastProgressAt = new AtomicLong(M.ms());
|
private final AtomicLong lastProgressAt = new AtomicLong(M.ms());
|
||||||
private final AtomicLong lastChunkCleanup = new AtomicLong(M.ms());
|
private final AtomicLong lastChunkCleanup = new AtomicLong(M.ms());
|
||||||
|
private final AtomicBoolean chunkCleanupRunning = new AtomicBoolean(false);
|
||||||
private final Object permitMonitor = new Object();
|
private final Object permitMonitor = new Object();
|
||||||
private volatile Engine metricsEngine;
|
private volatile Engine metricsEngine;
|
||||||
private volatile Mantle cachedMantle;
|
private volatile Mantle cachedMantle;
|
||||||
@@ -138,6 +141,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
this.timeoutWarnIntervalMs = pregen.getTimeoutWarnIntervalMs();
|
this.timeoutWarnIntervalMs = pregen.getTimeoutWarnIntervalMs();
|
||||||
this.urgent = false;
|
this.urgent = false;
|
||||||
this.lastUse = new ConcurrentHashMap<>();
|
this.lastUse = new ConcurrentHashMap<>();
|
||||||
|
this.chunkUseQueue = new ConcurrentLinkedQueue<>();
|
||||||
this.adaptiveInFlightLimit = new AtomicInteger(this.threads);
|
this.adaptiveInFlightLimit = new AtomicInteger(this.threads);
|
||||||
this.adaptiveMinInFlightLimit = Math.max(4, Math.min(16, Math.max(1, this.threads / 4)));
|
this.adaptiveMinInFlightLimit = Math.max(4, Math.min(16, Math.max(1, this.threads / 4)));
|
||||||
this.maxResidentTectonicPlates = pregen.getMaxResidentTectonicPlates();
|
this.maxResidentTectonicPlates = pregen.getMaxResidentTectonicPlates();
|
||||||
@@ -172,6 +176,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
private void unloadAndSaveAllChunks() {
|
private void unloadAndSaveAllChunks() {
|
||||||
if (foliaRuntime) {
|
if (foliaRuntime) {
|
||||||
lastUse.clear();
|
lastUse.clear();
|
||||||
|
chunkUseQueue.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +208,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
if (unloaded.get()) {
|
if (unloaded.get()) {
|
||||||
world.save();
|
world.save();
|
||||||
}
|
}
|
||||||
|
if (lastUse.isEmpty()) {
|
||||||
|
chunkUseQueue.clear();
|
||||||
|
}
|
||||||
}).get();
|
}).get();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -224,6 +232,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
int sizeBefore = lastUse.size();
|
int sizeBefore = lastUse.size();
|
||||||
if (sizeBefore > 0) {
|
if (sizeBefore > 0) {
|
||||||
lastUse.clear();
|
lastUse.clear();
|
||||||
|
chunkUseQueue.clear();
|
||||||
IrisLogging.info("Periodic chunk cleanup: cleared " + sizeBefore + " Folia chunk references");
|
IrisLogging.info("Periodic chunk cleanup: cleared " + sizeBefore + " Folia chunk references");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -234,21 +243,52 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long minTime = now - CHUNK_CLEANUP_MIN_AGE_MS;
|
if (!chunkCleanupRunning.compareAndSet(false, true)) {
|
||||||
AtomicInteger removed = new AtomicInteger();
|
return;
|
||||||
lastUse.entrySet().removeIf(entry -> {
|
|
||||||
Long lastUseTime = entry.getValue();
|
|
||||||
if (lastUseTime == null || lastUseTime < minTime) {
|
|
||||||
removed.incrementAndGet();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
int removedCount = removed.get();
|
|
||||||
if (removedCount > 0) {
|
|
||||||
IrisLogging.info("Periodic chunk cleanup: removed " + removedCount + "/" + sizeBefore + " stale chunk references");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long minTime = now - CHUNK_CLEANUP_MIN_AGE_MS;
|
||||||
|
J.a(() -> {
|
||||||
|
try {
|
||||||
|
int removedCount = cleanupQueuedChunkUses(minTime);
|
||||||
|
if (removedCount > 0) {
|
||||||
|
IrisLogging.info("Periodic chunk cleanup: removed " + removedCount + "/" + sizeBefore + " stale chunk references");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
chunkCleanupRunning.set(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int cleanupQueuedChunkUses(long minTime) {
|
||||||
|
int removed = 0;
|
||||||
|
while (true) {
|
||||||
|
ChunkUse chunkUse = chunkUseQueue.peek();
|
||||||
|
if (chunkUse == null) {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long latestUse = lastUse.get(chunkUse.chunk());
|
||||||
|
if (latestUse != null && latestUse > chunkUse.lastUseTime()) {
|
||||||
|
chunkUseQueue.poll();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (latestUse != null && latestUse >= minTime) {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkUseQueue.poll();
|
||||||
|
if (latestUse != null && lastUse.remove(chunkUse.chunk(), latestUse)) {
|
||||||
|
removed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recordChunkUse(Chunk chunk) {
|
||||||
|
long now = M.ms();
|
||||||
|
lastUse.put(chunk, now);
|
||||||
|
chunkUseQueue.offer(new ChunkUse(chunk, now));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Chunk onChunkFutureFailure(int x, int z, Throwable throwable) {
|
private Chunk onChunkFutureFailure(int x, int z, Throwable throwable) {
|
||||||
@@ -351,7 +391,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
|
|
||||||
static int computePaperLikeRecommendedCap(int workerThreads) {
|
static int computePaperLikeRecommendedCap(int workerThreads) {
|
||||||
int normalizedWorkers = Math.max(1, workerThreads);
|
int normalizedWorkers = Math.max(1, workerThreads);
|
||||||
int recommendedCap = normalizedWorkers * 4;
|
int recommendedCap = normalizedWorkers * 8;
|
||||||
if (recommendedCap < 16) {
|
if (recommendedCap < 16) {
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
@@ -723,7 +763,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
public static void increaseWorkerThreads() {
|
public static void increaseWorkerThreads() {
|
||||||
THREAD_COUNT.updateAndGet(i -> {
|
THREAD_COUNT.updateAndGet(i -> {
|
||||||
if (i > 0) {
|
if (i > 0) {
|
||||||
return 1;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads();
|
int adjusted = IrisSettings.get().getConcurrency().getWorldGenThreads();
|
||||||
@@ -810,7 +850,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
listener.onChunkGenerated(x, z);
|
listener.onChunkGenerated(x, z);
|
||||||
cleanupMantleChunk(x, z);
|
cleanupMantleChunk(x, z);
|
||||||
listener.onChunkCleaned(x, z);
|
listener.onChunkCleaned(x, z);
|
||||||
lastUse.put(chunk, M.ms());
|
recordChunkUse(chunk);
|
||||||
success = true;
|
success = true;
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
IrisLogging.reportError(e);
|
IrisLogging.reportError(e);
|
||||||
@@ -841,7 +881,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
listener.onChunkGenerated(x, z);
|
listener.onChunkGenerated(x, z);
|
||||||
cleanupMantleChunk(x, z);
|
cleanupMantleChunk(x, z);
|
||||||
listener.onChunkCleaned(x, z);
|
listener.onChunkCleaned(x, z);
|
||||||
lastUse.put(i, M.ms());
|
recordChunkUse(i);
|
||||||
success = true;
|
success = true;
|
||||||
} catch (InterruptedException ignored) {
|
} catch (InterruptedException ignored) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
@@ -877,7 +917,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
listener.onChunkGenerated(x, z);
|
listener.onChunkGenerated(x, z);
|
||||||
cleanupMantleChunk(x, z);
|
cleanupMantleChunk(x, z);
|
||||||
listener.onChunkCleaned(x, z);
|
listener.onChunkCleaned(x, z);
|
||||||
lastUse.put(i, M.ms());
|
recordChunkUse(i);
|
||||||
success = true;
|
success = true;
|
||||||
} finally {
|
} finally {
|
||||||
markFinished(success);
|
markFinished(success);
|
||||||
@@ -887,6 +927,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record ChunkUse(Chunk chunk, long lastUseTime) {
|
||||||
|
}
|
||||||
|
|
||||||
private record ChunkAsyncMethodSelection(Method urgentMethod, Method standardMethod, String mode) {
|
private record ChunkAsyncMethodSelection(Method urgentMethod, Method standardMethod, String mode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,6 +110,7 @@ public class IrisEngine implements Engine {
|
|||||||
private IrisComplex complex;
|
private IrisComplex complex;
|
||||||
private UpperDimensionContext upperContext;
|
private UpperDimensionContext upperContext;
|
||||||
private final AtomicBoolean modeFallbackLogged;
|
private final AtomicBoolean modeFallbackLogged;
|
||||||
|
private final AtomicBoolean prefetchSaveStarted;
|
||||||
|
|
||||||
public IrisEngine(EngineTarget target, boolean studio) {
|
public IrisEngine(EngineTarget target, boolean studio) {
|
||||||
this.studio = studio;
|
this.studio = studio;
|
||||||
@@ -136,6 +137,7 @@ public class IrisEngine implements Engine {
|
|||||||
context = new IrisContext(this);
|
context = new IrisContext(this);
|
||||||
cleaning = new AtomicBoolean(false);
|
cleaning = new AtomicBoolean(false);
|
||||||
modeFallbackLogged = new AtomicBoolean(false);
|
modeFallbackLogged = new AtomicBoolean(false);
|
||||||
|
prefetchSaveStarted = new AtomicBoolean(false);
|
||||||
if (studio) {
|
if (studio) {
|
||||||
_t0 = M.ms();
|
_t0 = M.ms();
|
||||||
getData().dump();
|
getData().dump();
|
||||||
@@ -582,6 +584,7 @@ public class IrisEngine implements Engine {
|
|||||||
if (currentWorldManager != null) {
|
if (currentWorldManager != null) {
|
||||||
currentWorldManager.close();
|
currentWorldManager.close();
|
||||||
}
|
}
|
||||||
|
savePrefetchOnce();
|
||||||
getTarget().close();
|
getTarget().close();
|
||||||
saveEngineData();
|
saveEngineData();
|
||||||
getMantle().close();
|
getMantle().close();
|
||||||
@@ -603,6 +606,17 @@ public class IrisEngine implements Engine {
|
|||||||
IrisLogging.debug("Engine Fully Shutdown!");
|
IrisLogging.debug("Engine Fully Shutdown!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isPregeneratorActiveForThisWorld() {
|
||||||
|
PregeneratorJob pregeneratorJob = PregeneratorJob.getInstance();
|
||||||
|
return pregeneratorJob != null && getWorld().hasRealWorld() && pregeneratorJob.targetsWorld(getWorld().realWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void savePrefetchOnce() {
|
||||||
|
if (prefetchSaveStarted.compareAndSet(false, true)) {
|
||||||
|
getData().savePrefetch(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IrisComplex getComplex() {
|
public IrisComplex getComplex() {
|
||||||
return ensureComplex();
|
return ensureComplex();
|
||||||
@@ -680,8 +694,8 @@ public class IrisEngine implements Engine {
|
|||||||
getMetrics().getTotal().put(p.getMilliseconds());
|
getMetrics().getTotal().put(p.getMilliseconds());
|
||||||
generated.incrementAndGet();
|
generated.incrementAndGet();
|
||||||
|
|
||||||
if (generated.get() == 661) {
|
if (generated.get() == 661 && !isPregeneratorActiveForThisWorld()) {
|
||||||
J.a(() -> getData().savePrefetch(this));
|
J.a(this::savePrefetchOnce);
|
||||||
}
|
}
|
||||||
} catch (GenerationSessionException e) {
|
} catch (GenerationSessionException e) {
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import art.arcane.iris.engine.UpperDimensionContext;
|
|||||||
import art.arcane.iris.engine.object.IrisBiome;
|
import art.arcane.iris.engine.object.IrisBiome;
|
||||||
import art.arcane.iris.engine.object.IrisDimension;
|
import art.arcane.iris.engine.object.IrisDimension;
|
||||||
import art.arcane.iris.engine.object.IrisOreGenerator;
|
import art.arcane.iris.engine.object.IrisOreGenerator;
|
||||||
|
import art.arcane.iris.engine.object.IrisOreGeneratorBounds;
|
||||||
import art.arcane.iris.engine.object.IrisRegion;
|
import art.arcane.iris.engine.object.IrisRegion;
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.iris.util.project.context.ChunkedDataCache;
|
import art.arcane.iris.util.project.context.ChunkedDataCache;
|
||||||
@@ -243,8 +244,14 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<PlatformBl
|
|||||||
KList<IrisOreGenerator> biomeUndergroundOres = hideOres ? null : biome.getUndergroundOreGenerators();
|
KList<IrisOreGenerator> biomeUndergroundOres = hideOres ? null : biome.getUndergroundOreGenerators();
|
||||||
KList<IrisOreGenerator> regionUndergroundOres = hideOres ? null : region.getUndergroundOreGenerators();
|
KList<IrisOreGenerator> regionUndergroundOres = hideOres ? null : region.getUndergroundOreGenerators();
|
||||||
KList<IrisOreGenerator> dimensionUndergroundOres = hideOres ? null : dimension.getUndergroundOreGenerators();
|
KList<IrisOreGenerator> dimensionUndergroundOres = hideOres ? null : dimension.getUndergroundOreGenerators();
|
||||||
boolean hasSurfaceOres = !hideOres && (!biomeSurfaceOres.isEmpty() || !regionSurfaceOres.isEmpty() || !dimensionSurfaceOres.isEmpty());
|
IrisOreGeneratorBounds biomeSurfaceOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : biome.getSurfaceOreGeneratorBounds();
|
||||||
boolean hasUndergroundOres = !hideOres && (!biomeUndergroundOres.isEmpty() || !regionUndergroundOres.isEmpty() || !dimensionUndergroundOres.isEmpty());
|
IrisOreGeneratorBounds regionSurfaceOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : region.getSurfaceOreGeneratorBounds();
|
||||||
|
IrisOreGeneratorBounds dimensionSurfaceOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : dimension.getSurfaceOreGeneratorBounds();
|
||||||
|
IrisOreGeneratorBounds biomeUndergroundOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : biome.getUndergroundOreGeneratorBounds();
|
||||||
|
IrisOreGeneratorBounds regionUndergroundOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : region.getUndergroundOreGeneratorBounds();
|
||||||
|
IrisOreGeneratorBounds dimensionUndergroundOreBounds = hideOres ? IrisOreGeneratorBounds.EMPTY : dimension.getUndergroundOreGeneratorBounds();
|
||||||
|
boolean hasSurfaceOres = biomeSurfaceOreBounds.hasOres() || regionSurfaceOreBounds.hasOres() || dimensionSurfaceOreBounds.hasOres();
|
||||||
|
boolean hasUndergroundOres = biomeUndergroundOreBounds.hasOres() || regionUndergroundOreBounds.hasOres() || dimensionUndergroundOreBounds.hasOres();
|
||||||
KList<PlatformBlockState> blocks = null;
|
KList<PlatformBlockState> blocks = null;
|
||||||
KList<PlatformBlockState> fblocks = null;
|
KList<PlatformBlockState> fblocks = null;
|
||||||
|
|
||||||
@@ -257,9 +264,15 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<PlatformBl
|
|||||||
|
|
||||||
PlatformBlockState ore = null;
|
PlatformBlockState ore = null;
|
||||||
if (hasSurfaceOres) {
|
if (hasSurfaceOres) {
|
||||||
ore = generateOres(biomeSurfaceOres, realX, i, realZ, localRng, data);
|
if (biomeSurfaceOreBounds.contains(i)) {
|
||||||
ore = ore == null ? generateOres(regionSurfaceOres, realX, i, realZ, localRng, data) : ore;
|
ore = generateOres(biomeSurfaceOres, realX, i, realZ, localRng, data);
|
||||||
ore = ore == null ? generateOres(dimensionSurfaceOres, realX, i, realZ, localRng, data) : ore;
|
}
|
||||||
|
if (ore == null && regionSurfaceOreBounds.contains(i)) {
|
||||||
|
ore = generateOres(regionSurfaceOres, realX, i, realZ, localRng, data);
|
||||||
|
}
|
||||||
|
if (ore == null && dimensionSurfaceOreBounds.contains(i)) {
|
||||||
|
ore = generateOres(dimensionSurfaceOres, realX, i, realZ, localRng, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ore != null) {
|
if (ore != null) {
|
||||||
h.setRaw(xf, i, zf, ore);
|
h.setRaw(xf, i, zf, ore);
|
||||||
@@ -292,9 +305,15 @@ public class IrisTerrainNormalActuator extends EngineAssignedActuator<PlatformBl
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasUndergroundOres) {
|
if (hasUndergroundOres) {
|
||||||
ore = generateOres(biomeUndergroundOres, realX, i, realZ, localRng, data);
|
if (biomeUndergroundOreBounds.contains(i)) {
|
||||||
ore = ore == null ? generateOres(regionUndergroundOres, realX, i, realZ, localRng, data) : ore;
|
ore = generateOres(biomeUndergroundOres, realX, i, realZ, localRng, data);
|
||||||
ore = ore == null ? generateOres(dimensionUndergroundOres, realX, i, realZ, localRng, data) : ore;
|
}
|
||||||
|
if (ore == null && regionUndergroundOreBounds.contains(i)) {
|
||||||
|
ore = generateOres(regionUndergroundOres, realX, i, realZ, localRng, data);
|
||||||
|
}
|
||||||
|
if (ore == null && dimensionUndergroundOreBounds.contains(i)) {
|
||||||
|
ore = generateOres(dimensionUndergroundOres, realX, i, realZ, localRng, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ore != null) {
|
if (ore != null) {
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ import art.arcane.volmlib.util.mantle.flag.MantleFlag;
|
|||||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
||||||
import art.arcane.volmlib.util.matter.Matter;
|
import art.arcane.volmlib.util.matter.Matter;
|
||||||
|
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@@ -38,27 +37,69 @@ public interface MatterGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int writeRadius = getRadius();
|
int writeRadius = getRadius();
|
||||||
Set<Long> partialChunks = new HashSet<>();
|
LongOpenHashSet partialChunks = new LongOpenHashSet();
|
||||||
|
|
||||||
try (MantleWriter writer = new MantleWriter(getEngine().getMantle(), getMantle(), x, z, writeRadius, multicore)) {
|
try (MantleWriter writer = new MantleWriter(getEngine().getMantle(), getMantle(), x, z, writeRadius, multicore)) {
|
||||||
for (Pair<List<MantleComponent>, Integer> pair : getComponents()) {
|
for (Pair<List<MantleComponent>, Integer> pair : getComponents()) {
|
||||||
int passRadius = pair.getB();
|
int passRadius = pair.getB();
|
||||||
Set<CompletableFuture<Void>> launchedTasks = multicore ? new HashSet<>() : null;
|
List<MantleComponent> passComponents = pair.getA();
|
||||||
|
MantleComponent[] enabledComponents = new MantleComponent[passComponents.size()];
|
||||||
|
int[] componentPassRadii = new int[passComponents.size()];
|
||||||
|
int enabledComponentCount = 0;
|
||||||
|
for (MantleComponent component : passComponents) {
|
||||||
|
if (component.isEnabled()) {
|
||||||
|
int componentRadius = component.getRadius();
|
||||||
|
componentPassRadii[enabledComponentCount] = componentRadius > 0 ? Math.ceilDiv(componentRadius, 16) : 0;
|
||||||
|
enabledComponents[enabledComponentCount++] = component;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enabledComponentCount == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean inlineComponents = multicore && DISPATCHER.ownsCurrentThread();
|
||||||
|
List<CompletableFuture<Void>> launchedTasks = multicore && !inlineComponents ? new ArrayList<>() : null;
|
||||||
|
MantleComponent[] eligibleComponents = new MantleComponent[enabledComponentCount];
|
||||||
|
|
||||||
for (int i = -passRadius; i <= passRadius; i++) {
|
for (int i = -passRadius; i <= passRadius; i++) {
|
||||||
|
int absI = Math.abs(i);
|
||||||
for (int j = -passRadius; j <= passRadius; j++) {
|
for (int j = -passRadius; j <= passRadius; j++) {
|
||||||
|
int absJ = Math.abs(j);
|
||||||
int passX = x + i;
|
int passX = x + i;
|
||||||
int passZ = z + j;
|
int passZ = z + j;
|
||||||
long passKey = chunkKey(passX, passZ);
|
long passKey = chunkKey(passX, passZ);
|
||||||
|
boolean partial = false;
|
||||||
|
boolean anyComponentInRadius = false;
|
||||||
|
|
||||||
|
for (int componentIndex = 0; componentIndex < enabledComponentCount; componentIndex++) {
|
||||||
|
int componentPassRadius = componentPassRadii[componentIndex];
|
||||||
|
if (absI > componentPassRadius || absJ > componentPassRadius) {
|
||||||
|
partial = true;
|
||||||
|
} else {
|
||||||
|
anyComponentInRadius = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyComponentInRadius) {
|
||||||
|
partialChunks.add(passKey);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (partial) {
|
||||||
|
partialChunks.add(passKey);
|
||||||
|
}
|
||||||
|
|
||||||
MantleChunk<Matter> chunk = writer.acquireChunk(passX, passZ);
|
MantleChunk<Matter> chunk = writer.acquireChunk(passX, passZ);
|
||||||
if (chunk.isFlagged(MantleFlag.PLANNED)) {
|
if (chunk.isFlagged(MantleFlag.PLANNED)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<MantleComponent> eligibleComponents = new ArrayList<>(pair.getA().size());
|
int eligibleComponentCount = 0;
|
||||||
for (MantleComponent component : pair.getA()) {
|
for (int componentIndex = 0; componentIndex < enabledComponentCount; componentIndex++) {
|
||||||
if (!component.isEnabled()) {
|
MantleComponent component = enabledComponents[componentIndex];
|
||||||
|
int componentPassRadius = componentPassRadii[componentIndex];
|
||||||
|
if (absI > componentPassRadius || absJ > componentPassRadius) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,15 +107,6 @@ public interface MatterGenerator {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int componentRadius = component.getRadius();
|
|
||||||
if (componentRadius > 0) {
|
|
||||||
int componentPassRadius = Math.ceilDiv(componentRadius, 16);
|
|
||||||
if (Math.abs(i) > componentPassRadius || Math.abs(j) > componentPassRadius) {
|
|
||||||
partialChunks.add(passKey);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MantleFlag[] prerequisites = component.getPrerequisiteFlags();
|
MantleFlag[] prerequisites = component.getPrerequisiteFlags();
|
||||||
if (prerequisites.length > 0) {
|
if (prerequisites.length > 0) {
|
||||||
boolean prerequisitesMet = true;
|
boolean prerequisitesMet = true;
|
||||||
@@ -90,32 +122,37 @@ public interface MatterGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eligibleComponents.add(component);
|
eligibleComponents[eligibleComponentCount++] = component;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eligibleComponents.isEmpty()) {
|
if (eligibleComponentCount == 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int finalPassX = passX;
|
int finalPassX = passX;
|
||||||
int finalPassZ = passZ;
|
int finalPassZ = passZ;
|
||||||
Runnable task = () -> {
|
|
||||||
for (MantleComponent component : eligibleComponents) {
|
|
||||||
runComponentInline(chunk, component, writer, finalPassX, finalPassZ, context);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (multicore) {
|
if (multicore) {
|
||||||
for (MantleComponent component : eligibleComponents) {
|
if (inlineComponents) {
|
||||||
launchedTasks.add(runComponentAsync(chunk, component, writer, finalPassX, finalPassZ, context));
|
for (int componentIndex = 0; componentIndex < eligibleComponentCount; componentIndex++) {
|
||||||
|
MantleComponent component = eligibleComponents[componentIndex];
|
||||||
|
runComponentInline(chunk, component, writer, finalPassX, finalPassZ, context);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int componentIndex = 0; componentIndex < eligibleComponentCount; componentIndex++) {
|
||||||
|
MantleComponent component = eligibleComponents[componentIndex];
|
||||||
|
launchedTasks.add(runComponentAsync(chunk, component, writer, finalPassX, finalPassZ, context));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
task.run();
|
for (int componentIndex = 0; componentIndex < eligibleComponentCount; componentIndex++) {
|
||||||
|
MantleComponent component = eligibleComponents[componentIndex];
|
||||||
|
runComponentInline(chunk, component, writer, finalPassX, finalPassZ, context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (multicore) {
|
if (launchedTasks != null) {
|
||||||
for (CompletableFuture<Void> launchedTask : launchedTasks) {
|
for (CompletableFuture<Void> launchedTask : launchedTasks) {
|
||||||
launchedTask.join();
|
launchedTask.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1593,7 +1593,13 @@ public class IrisCaveCarver3D {
|
|||||||
|
|
||||||
private int snapWarp(int c) {
|
private int snapWarp(int c) {
|
||||||
int g = warpResolution;
|
int g = warpResolution;
|
||||||
return g <= 1 ? c : Math.floorDiv(c, g) * g;
|
if (g <= 1) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
if ((g & (g - 1)) == 0) {
|
||||||
|
return c & -g;
|
||||||
|
}
|
||||||
|
return Math.floorDiv(c, g) * g;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean classifyDensityPointWarpOnly(int x, int y, int z, double thresholdLimit) {
|
private boolean classifyDensityPointWarpOnly(int x, int y, int z, double thresholdLimit) {
|
||||||
@@ -1751,6 +1757,10 @@ public class IrisCaveCarver3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isAdaptivePlaneSampleAligned(int localX, int localZ, int adaptiveSampleStep) {
|
private boolean isAdaptivePlaneSampleAligned(int localX, int localZ, int adaptiveSampleStep) {
|
||||||
|
if ((adaptiveSampleStep & (adaptiveSampleStep - 1)) == 0) {
|
||||||
|
int mask = adaptiveSampleStep - 1;
|
||||||
|
return (localX & mask) == 0 && (localZ & mask) == 0;
|
||||||
|
}
|
||||||
return localX % adaptiveSampleStep == 0 && localZ % adaptiveSampleStep == 0;
|
return localX % adaptiveSampleStep == 0 && localZ % adaptiveSampleStep == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -42,6 +42,7 @@ import art.arcane.iris.engine.object.ObjectPlaceMode;
|
|||||||
import art.arcane.iris.spi.IrisLogging;
|
import art.arcane.iris.spi.IrisLogging;
|
||||||
import art.arcane.iris.util.common.math.IrisBlockVector;
|
import art.arcane.iris.util.common.math.IrisBlockVector;
|
||||||
import art.arcane.iris.util.project.context.ChunkContext;
|
import art.arcane.iris.util.project.context.ChunkContext;
|
||||||
|
import art.arcane.iris.util.project.context.ChunkedDataCache;
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
|
import art.arcane.volmlib.util.documentation.ChunkCoordinates;
|
||||||
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
|
import art.arcane.volmlib.util.mantle.flag.ReservedFlag;
|
||||||
@@ -73,6 +74,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent {
|
|||||||
int minZ = z << 4;
|
int minZ = z << 4;
|
||||||
long baseSeed = getEngineMantle().getEngine().getSeedManager().getTerrain() ^ IrisFloatingChildBiomeModifier.FLOATING_BASE_SEED_SALT;
|
long baseSeed = getEngineMantle().getEngine().getSeedManager().getTerrain() ^ IrisFloatingChildBiomeModifier.FLOATING_BASE_SEED_SALT;
|
||||||
RNG chunkRng = new RNG(Cache.key(x, z) + seed() + 0x0FA710BEL);
|
RNG chunkRng = new RNG(Cache.key(x, z) + seed() + 0x0FA710BEL);
|
||||||
|
ChunkedDataCache<IrisBiome> biomeCache = context.getBiome();
|
||||||
|
|
||||||
FloatingIslandSample.clearChunkMemo();
|
FloatingIslandSample.clearChunkMemo();
|
||||||
|
|
||||||
@@ -81,7 +83,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent {
|
|||||||
for (int zf = 0; zf < 16; zf++) {
|
for (int zf = 0; zf < 16; zf++) {
|
||||||
int wx = minX + xf;
|
int wx = minX + xf;
|
||||||
int wz = minZ + zf;
|
int wz = minZ + zf;
|
||||||
IrisBiome parent = complex.getTrueBiomeStream().get(wx, wz);
|
IrisBiome parent = biomeCache.get(xf, zf);
|
||||||
if (parent == null || parent.getFloatingChildBiomes() == null || parent.getFloatingChildBiomes().isEmpty()) {
|
if (parent == null || parent.getFloatingChildBiomes() == null || parent.getFloatingChildBiomes().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -113,7 +115,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrisBiome parent = complex.getTrueBiomeStream().get(minX + (columns.get(0) & 15), minZ + (columns.get(0) >> 4));
|
IrisBiome parent = biomeCache.get(columns.get(0) & 15, columns.get(0) >> 4);
|
||||||
IrisBiome target = entry.getRealBiome(parent, data);
|
IrisBiome target = entry.getRealBiome(parent, data);
|
||||||
|
|
||||||
KList<IrisObjectPlacement> floating = entry.getFloatingObjects();
|
KList<IrisObjectPlacement> floating = entry.getFloatingObjects();
|
||||||
@@ -150,7 +152,7 @@ public class MantleFloatingObjectComponent extends IrisMantleComponent {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
IrisBiome parent = complex.getTrueBiomeStream().get(minX + (columns.get(0) & 15), minZ + (columns.get(0) >> 4));
|
IrisBiome parent = biomeCache.get(columns.get(0) & 15, columns.get(0) >> 4);
|
||||||
IrisBiome target = entry.getRealBiome(parent, data);
|
IrisBiome target = entry.getRealBiome(parent, data);
|
||||||
KList<IrisObjectPlacement> bottom = target != null ? entry.resolveBottomObjects(target) : null;
|
KList<IrisObjectPlacement> bottom = target != null ? entry.resolveBottomObjects(target) : null;
|
||||||
if (bottom != null && !bottom.isEmpty()) {
|
if (bottom != null && !bottom.isEmpty()) {
|
||||||
|
|||||||
+190
-38
@@ -46,6 +46,7 @@ import art.arcane.volmlib.util.math.RNG;
|
|||||||
import art.arcane.volmlib.util.matter.MatterStructurePOI;
|
import art.arcane.volmlib.util.matter.MatterStructurePOI;
|
||||||
import art.arcane.iris.util.project.noise.CNG;
|
import art.arcane.iris.util.project.noise.CNG;
|
||||||
import art.arcane.iris.util.project.noise.NoiseType;
|
import art.arcane.iris.util.project.noise.NoiseType;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ByteOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
import art.arcane.iris.spi.PlatformBlockState;
|
import art.arcane.iris.spi.PlatformBlockState;
|
||||||
@@ -251,6 +252,8 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
IrisCaveProfile regionCaveProfile = resolveCaveProfile(region.getCaveProfile(), caveBiome.getCaveProfile());
|
IrisCaveProfile regionCaveProfile = resolveCaveProfile(region.getCaveProfile(), caveBiome.getCaveProfile());
|
||||||
int biomeSurfaceExclusionDepth = resolveSurfaceObjectExclusionDepth(biomeCaveProfile);
|
int biomeSurfaceExclusionDepth = resolveSurfaceObjectExclusionDepth(biomeCaveProfile);
|
||||||
int regionSurfaceExclusionDepth = resolveSurfaceObjectExclusionDepth(regionCaveProfile);
|
int regionSurfaceExclusionDepth = resolveSurfaceObjectExclusionDepth(regionCaveProfile);
|
||||||
|
SurfaceExposureCache surfaceExposureCache = new SurfaceExposureCache();
|
||||||
|
CaveAnchorCache caveAnchorCache = new CaveAnchorCache();
|
||||||
|
|
||||||
for (IrisObjectPlacement i : surfaceBiome.getSurfaceObjects()) {
|
for (IrisObjectPlacement i : surfaceBiome.getSurfaceObjects()) {
|
||||||
biomeSurfaceChecked++;
|
biomeSurfaceChecked++;
|
||||||
@@ -266,7 +269,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
if (chance) {
|
if (chance) {
|
||||||
biomeSurfaceTriggered++;
|
biomeSurfaceTriggered++;
|
||||||
try {
|
try {
|
||||||
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, biomeSurfaceExclusionDepth, complex, traceRegen, x, z, "biome-surface", surfaceHeightLookup);
|
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, biomeSurfaceExclusionDepth, complex, traceRegen, x, z, "biome-surface", surfaceHeightLookup, surfaceExposureCache, caveAnchorCache);
|
||||||
attempts += result.attempts();
|
attempts += result.attempts();
|
||||||
placed += result.placed();
|
placed += result.placed();
|
||||||
rejected += result.rejected();
|
rejected += result.rejected();
|
||||||
@@ -300,7 +303,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
if (chance) {
|
if (chance) {
|
||||||
biomeCaveTriggered++;
|
biomeCaveTriggered++;
|
||||||
try {
|
try {
|
||||||
ObjectPlacementResult result = placeCaveObject(writer, rng, x, z, i, biomeCaveProfile, complex, traceRegen, x, z, "biome-cave", caveBiome.getLoadKey());
|
ObjectPlacementResult result = placeCaveObject(writer, rng, x, z, i, biomeCaveProfile, complex, traceRegen, x, z, "biome-cave", caveBiome.getLoadKey(), caveAnchorCache);
|
||||||
attempts += result.attempts();
|
attempts += result.attempts();
|
||||||
placed += result.placed();
|
placed += result.placed();
|
||||||
rejected += result.rejected();
|
rejected += result.rejected();
|
||||||
@@ -331,7 +334,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
if (chance) {
|
if (chance) {
|
||||||
regionSurfaceTriggered++;
|
regionSurfaceTriggered++;
|
||||||
try {
|
try {
|
||||||
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, regionSurfaceExclusionDepth, complex, traceRegen, x, z, "region-surface", surfaceHeightLookup);
|
ObjectPlacementResult result = placeObject(writer, rng, x << 4, z << 4, i, regionSurfaceExclusionDepth, complex, traceRegen, x, z, "region-surface", surfaceHeightLookup, surfaceExposureCache, caveAnchorCache);
|
||||||
attempts += result.attempts();
|
attempts += result.attempts();
|
||||||
placed += result.placed();
|
placed += result.placed();
|
||||||
rejected += result.rejected();
|
rejected += result.rejected();
|
||||||
@@ -365,7 +368,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
if (chance) {
|
if (chance) {
|
||||||
regionCaveTriggered++;
|
regionCaveTriggered++;
|
||||||
try {
|
try {
|
||||||
ObjectPlacementResult result = placeCaveObject(writer, rng, x, z, i, regionCaveProfile, complex, traceRegen, x, z, "region-cave", null);
|
ObjectPlacementResult result = placeCaveObject(writer, rng, x, z, i, regionCaveProfile, complex, traceRegen, x, z, "region-cave", null, caveAnchorCache);
|
||||||
attempts += result.attempts();
|
attempts += result.attempts();
|
||||||
placed += result.placed();
|
placed += result.placed();
|
||||||
rejected += result.rejected();
|
rejected += result.rejected();
|
||||||
@@ -417,6 +420,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
int blockX = x << 4;
|
int blockX = x << 4;
|
||||||
int blockZ = z << 4;
|
int blockZ = z << 4;
|
||||||
boolean golden = isGoldenDebugChunk(x, z);
|
boolean golden = isGoldenDebugChunk(x, z);
|
||||||
|
CaveAnchorCache caveAnchorCache = new CaveAnchorCache();
|
||||||
for (IrisProceduralPlacement p : proceduralObjects.getAllPlacements()) {
|
for (IrisProceduralPlacement p : proceduralObjects.getAllPlacements()) {
|
||||||
boolean chancePassed = rng.chance(p.getChance() + rng.d(-0.005, 0.005));
|
boolean chancePassed = rng.chance(p.getChance() + rng.d(-0.005, 0.005));
|
||||||
if (golden) {
|
if (golden) {
|
||||||
@@ -468,7 +472,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
try {
|
try {
|
||||||
int placeResult = -1;
|
int placeResult = -1;
|
||||||
if (carving) {
|
if (carving) {
|
||||||
int caveFloorY = findNearestCaveFloor(writer, xx, zz);
|
int caveFloorY = findNearestCaveFloor(writer, xx, zz, caveAnchorCache);
|
||||||
if (golden) {
|
if (golden) {
|
||||||
IrisLogging.info("Goldendebug procedural caveFloor: chunk=" + x + "," + z
|
IrisLogging.info("Goldendebug procedural caveFloor: chunk=" + x + "," + z
|
||||||
+ " placement=" + p.getName()
|
+ " placement=" + p.getName()
|
||||||
@@ -522,7 +526,9 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
int chunkX,
|
int chunkX,
|
||||||
int chunkZ,
|
int chunkZ,
|
||||||
String scope,
|
String scope,
|
||||||
SurfaceHeightLookup surfaceHeightLookup
|
SurfaceHeightLookup surfaceHeightLookup,
|
||||||
|
SurfaceExposureCache surfaceExposureCache,
|
||||||
|
CaveAnchorCache caveAnchorCache
|
||||||
) {
|
) {
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
int placed = 0;
|
int placed = 0;
|
||||||
@@ -550,7 +556,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
int zz = rng.i(z, z + 15);
|
int zz = rng.i(z, z + 15);
|
||||||
int surfaceObjectExclusionDepth = resolveSurfaceObjectExclusionDepth(surfaceObjectExclusionBaseDepth, v, objectPlacement);
|
int surfaceObjectExclusionDepth = resolveSurfaceObjectExclusionDepth(surfaceObjectExclusionBaseDepth, v, objectPlacement);
|
||||||
int surfaceObjectExclusionRadius = resolveSurfaceObjectExclusionRadius(v, objectPlacement);
|
int surfaceObjectExclusionRadius = resolveSurfaceObjectExclusionRadius(v, objectPlacement);
|
||||||
boolean overCave = surfaceObjectExclusionDepth > 0 && hasSurfaceCarveExposure(writer, surfaceHeightLookup, xx, zz, surfaceObjectExclusionDepth, surfaceObjectExclusionRadius);
|
boolean overCave = surfaceObjectExclusionDepth > 0 && hasSurfaceCarveExposure(writer, surfaceHeightLookup, xx, zz, surfaceObjectExclusionDepth, surfaceObjectExclusionRadius, surfaceExposureCache);
|
||||||
int id = rng.i(0, Integer.MAX_VALUE);
|
int id = rng.i(0, Integer.MAX_VALUE);
|
||||||
IrisObjectPlacement effectivePlacement = resolveEffectivePlacement(objectPlacement, v);
|
IrisObjectPlacement effectivePlacement = resolveEffectivePlacement(objectPlacement, v);
|
||||||
if (effectivePlacement.getMode() == ObjectPlaceMode.FLOATING) {
|
if (effectivePlacement.getMode() == ObjectPlaceMode.FLOATING) {
|
||||||
@@ -574,7 +580,7 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
String fallbackPath = "surface";
|
String fallbackPath = "surface";
|
||||||
|
|
||||||
if (overCave) {
|
if (overCave) {
|
||||||
int caveFloorY = findNearestCaveFloor(writer, xx, zz);
|
int caveFloorY = findNearestCaveFloor(writer, xx, zz, caveAnchorCache);
|
||||||
if (caveFloorY > 0) {
|
if (caveFloorY > 0) {
|
||||||
IrisObjectPlacement floorPlacement = effectivePlacement.toPlacement(v.getLoadKey());
|
IrisObjectPlacement floorPlacement = effectivePlacement.toPlacement(v.getLoadKey());
|
||||||
floorPlacement.setMode(ObjectPlaceMode.FAST_MIN_HEIGHT);
|
floorPlacement.setMode(ObjectPlaceMode.FAST_MIN_HEIGHT);
|
||||||
@@ -690,7 +696,8 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
int metricChunkX,
|
int metricChunkX,
|
||||||
int metricChunkZ,
|
int metricChunkZ,
|
||||||
String scope,
|
String scope,
|
||||||
String expectedCaveBiomeKey
|
String expectedCaveBiomeKey,
|
||||||
|
CaveAnchorCache anchorCache
|
||||||
) {
|
) {
|
||||||
int attempts = 0;
|
int attempts = 0;
|
||||||
int placed = 0;
|
int placed = 0;
|
||||||
@@ -700,7 +707,6 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
int minX = chunkX << 4;
|
int minX = chunkX << 4;
|
||||||
int minZ = chunkZ << 4;
|
int minZ = chunkZ << 4;
|
||||||
int density = objectPlacement.getDensity(rng, minX, minZ, getData());
|
int density = objectPlacement.getDensity(rng, minX, minZ, getData());
|
||||||
KMap<Long, KList<Integer>> anchorCache = new KMap<>();
|
|
||||||
IrisCaveAnchorMode anchorMode = resolveAnchorMode(objectPlacement, caveProfile);
|
IrisCaveAnchorMode anchorMode = resolveAnchorMode(objectPlacement, caveProfile);
|
||||||
if (objectPlacement.getMode() == ObjectPlaceMode.CEILING_HANG) {
|
if (objectPlacement.getMode() == ObjectPlaceMode.CEILING_HANG) {
|
||||||
anchorMode = IrisCaveAnchorMode.CEILING;
|
anchorMode = IrisCaveAnchorMode.CEILING;
|
||||||
@@ -1104,8 +1110,8 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
return effectivePlacement;
|
return effectivePlacement;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findNearestCaveFloor(MantleWriter writer, int x, int z) {
|
private int findNearestCaveFloor(MantleWriter writer, int x, int z, CaveAnchorCache anchorCache) {
|
||||||
KList<Integer> anchors = scanCaveAnchorColumn(writer, IrisCaveAnchorMode.FLOOR, 1, 0, x, z);
|
KList<Integer> anchors = anchorCache.get(writer, IrisCaveAnchorMode.FLOOR, 1, 0, x, z);
|
||||||
if (anchors.isEmpty()) {
|
if (anchors.isEmpty()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -1298,9 +1304,8 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int findCaveAnchorY(MantleWriter writer, RNG rng, int x, int z, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, KMap<Long, KList<Integer>> anchorCache) {
|
private int findCaveAnchorY(MantleWriter writer, RNG rng, int x, int z, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, CaveAnchorCache anchorCache) {
|
||||||
long key = Cache.key(x, z);
|
KList<Integer> anchors = anchorCache.get(writer, anchorMode, anchorScanStep, objectMinDepthBelowSurface, x, z);
|
||||||
KList<Integer> anchors = anchorCache.computeIfAbsent(key, (k) -> scanCaveAnchorColumn(writer, anchorMode, anchorScanStep, objectMinDepthBelowSurface, x, z));
|
|
||||||
if (anchors.isEmpty()) {
|
if (anchors.isEmpty()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -1312,24 +1317,26 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
return anchors.get(rng.i(0, anchors.size() - 1));
|
return anchors.get(rng.i(0, anchors.size() - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private KList<Integer> scanCaveAnchorColumn(MantleWriter writer, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, int x, int z) {
|
private KList<Integer> scanCaveAnchorColumn(MantleWriter writer, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, int x, int z, CaveAnchorCache anchorCache) {
|
||||||
int height = getEngineMantle().getEngine().getHeight();
|
int height = getEngineMantle().getEngine().getHeight();
|
||||||
int step = Math.max(1, anchorScanStep);
|
int step = Math.max(1, anchorScanStep);
|
||||||
int surfaceY = getEngineMantle().getEngine().getHeight(x, z);
|
int surfaceY = anchorCache.getSurfaceHeight(x, z);
|
||||||
int baseMaxAnchorY = Math.min(height - 1, surfaceY - Math.max(0, objectMinDepthBelowSurface));
|
int baseMaxAnchorY = Math.min(height - 1, surfaceY - Math.max(0, objectMinDepthBelowSurface));
|
||||||
if (baseMaxAnchorY <= 1) {
|
if (baseMaxAnchorY <= 1) {
|
||||||
return new KList<>();
|
return new KList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
KList<Integer> anchors = scanCaveAnchorRange(writer, anchorMode, step, x, z, height, baseMaxAnchorY);
|
int widenedMaxAnchorY = Math.min(height - 1, surfaceY - 3);
|
||||||
|
widenedMaxAnchorY = Math.min(widenedMaxAnchorY, baseMaxAnchorY + Math.max(0, objectMinDepthBelowSurface) / 2);
|
||||||
|
int carvedHeight = Math.min(height, Math.max(baseMaxAnchorY, widenedMaxAnchorY) + 4);
|
||||||
|
byte[] carvedColumn = anchorCache.getCarvedColumn(writer, x, z, carvedHeight);
|
||||||
|
KList<Integer> anchors = scanCaveAnchorRange(anchorMode, step, carvedHeight, BEDROCK_CLEARANCE, baseMaxAnchorY, carvedColumn);
|
||||||
if (!anchors.isEmpty()) {
|
if (!anchors.isEmpty()) {
|
||||||
return anchors;
|
return anchors;
|
||||||
}
|
}
|
||||||
|
|
||||||
int widenedMaxAnchorY = Math.min(height - 1, surfaceY - 3);
|
|
||||||
widenedMaxAnchorY = Math.min(widenedMaxAnchorY, baseMaxAnchorY + Math.max(0, objectMinDepthBelowSurface) / 2);
|
|
||||||
if (widenedMaxAnchorY > baseMaxAnchorY) {
|
if (widenedMaxAnchorY > baseMaxAnchorY) {
|
||||||
anchors = scanCaveAnchorRange(writer, anchorMode, step, x, z, height, widenedMaxAnchorY);
|
anchors = scanCaveAnchorRange(anchorMode, step, carvedHeight, baseMaxAnchorY, widenedMaxAnchorY, carvedColumn);
|
||||||
if (!anchors.isEmpty()) {
|
if (!anchors.isEmpty()) {
|
||||||
return anchors;
|
return anchors;
|
||||||
}
|
}
|
||||||
@@ -1338,15 +1345,16 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
return anchors;
|
return anchors;
|
||||||
}
|
}
|
||||||
|
|
||||||
private KList<Integer> scanCaveAnchorRange(MantleWriter writer, IrisCaveAnchorMode anchorMode, int step, int x, int z, int height, int maxAnchorY) {
|
private KList<Integer> scanCaveAnchorRange(IrisCaveAnchorMode anchorMode, int step, int height, int minAnchorY, int maxAnchorY, byte[] carvedColumn) {
|
||||||
KList<Integer> anchors = new KList<>();
|
KList<Integer> anchors = new KList<>();
|
||||||
for (int y = BEDROCK_CLEARANCE; y < maxAnchorY; y += step) {
|
int startY = alignAnchorScanStart(minAnchorY, step);
|
||||||
if (!writer.isCarved(x, y, z)) {
|
for (int y = startY; y < maxAnchorY; y += step) {
|
||||||
|
if (!isCarved(carvedColumn, y)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean solidBelow = hasSolidNeighbor(writer, x, y, z, height, -1);
|
boolean solidBelow = hasSolidNeighbor(carvedColumn, y, height, -1);
|
||||||
boolean solidAbove = hasSolidNeighbor(writer, x, y, z, height, 1);
|
boolean solidAbove = hasSolidNeighbor(carvedColumn, y, height, 1);
|
||||||
if (matchesCaveAnchor(anchorMode, solidBelow, solidAbove)) {
|
if (matchesCaveAnchor(anchorMode, solidBelow, solidAbove)) {
|
||||||
anchors.add(y);
|
anchors.add(y);
|
||||||
}
|
}
|
||||||
@@ -1354,13 +1362,27 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
return anchors;
|
return anchors;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasSolidNeighbor(MantleWriter writer, int x, int y, int z, int height, int direction) {
|
private int alignAnchorScanStart(int minAnchorY, int step) {
|
||||||
|
if (minAnchorY <= BEDROCK_CLEARANCE) {
|
||||||
|
return BEDROCK_CLEARANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int delta = minAnchorY - BEDROCK_CLEARANCE;
|
||||||
|
int steps = (delta + step - 1) / step;
|
||||||
|
return BEDROCK_CLEARANCE + (steps * step);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCarved(byte[] carvedColumn, int y) {
|
||||||
|
return y >= 0 && y < carvedColumn.length && carvedColumn[y] == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasSolidNeighbor(byte[] carvedColumn, int y, int height, int direction) {
|
||||||
for (int d = 1; d <= 3; d++) {
|
for (int d = 1; d <= 3; d++) {
|
||||||
int ny = y + (direction * d);
|
int ny = y + (direction * d);
|
||||||
if (ny < 0 || ny >= height) {
|
if (ny < 0 || ny >= height) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!writer.isCarved(x, ny, z)) {
|
if (!isCarved(carvedColumn, ny)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1469,20 +1491,26 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
return Math.max(1, caveProfile.getAnchorSearchAttempts());
|
return Math.max(1, caveProfile.getAnchorSearchAttempts());
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasSurfaceCarveExposure(MantleWriter writer, SurfaceHeightLookup surfaceHeightLookup, int x, int z, int depth, int radius) {
|
private boolean hasSurfaceCarveExposure(MantleWriter writer, SurfaceHeightLookup surfaceHeightLookup, int x, int z, int depth, int radius, SurfaceExposureCache surfaceExposureCache) {
|
||||||
int horizontalRadius = Math.max(0, radius);
|
int horizontalRadius = Math.max(0, radius);
|
||||||
int maxY = getEngineMantle().getEngine().getHeight() - 1;
|
int maxY = getEngineMantle().getEngine().getHeight() - 1;
|
||||||
for (int dx = -horizontalRadius; dx <= horizontalRadius; dx++) {
|
if (surfaceExposureCache.get(writer, surfaceHeightLookup, x, z, depth, maxY)) {
|
||||||
for (int dz = -horizontalRadius; dz <= horizontalRadius; dz++) {
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sampleCount = surfaceExposureSampleCount(horizontalRadius);
|
||||||
|
for (int sx = 0; sx < sampleCount; sx++) {
|
||||||
|
int dx = surfaceExposureSampleOffset(sx, sampleCount, horizontalRadius);
|
||||||
|
for (int sz = 0; sz < sampleCount; sz++) {
|
||||||
|
int dz = surfaceExposureSampleOffset(sz, sampleCount, horizontalRadius);
|
||||||
|
if (dx == 0 && dz == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
int columnX = x + dx;
|
int columnX = x + dx;
|
||||||
int columnZ = z + dz;
|
int columnZ = z + dz;
|
||||||
int surfaceY = surfaceHeightLookup.getRoundedHeight(columnX, columnZ);
|
if (surfaceExposureCache.get(writer, surfaceHeightLookup, columnX, columnZ, depth, maxY)) {
|
||||||
int fromY = Math.max(1, surfaceY - Math.max(0, depth));
|
return true;
|
||||||
int toY = Math.min(maxY, surfaceY + 1);
|
|
||||||
for (int y = fromY; y <= toY; y++) {
|
|
||||||
if (writer.isCarved(columnX, y, columnZ)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1490,6 +1518,39 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int surfaceExposureSampleCount(int radius) {
|
||||||
|
if (radius <= 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (radius <= 3) {
|
||||||
|
return (radius << 1) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int surfaceExposureSampleOffset(int sample, int sampleCount, int radius) {
|
||||||
|
if (sampleCount <= 1) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -radius + Math.floorDiv((sample * radius * 2) + ((sampleCount - 1) >> 1), sampleCount - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasSurfaceCarveExposureColumn(MantleWriter writer, SurfaceHeightLookup surfaceHeightLookup, int x, int z, int depth, int maxY) {
|
||||||
|
int surfaceY = surfaceHeightLookup.getRoundedHeight(x, z);
|
||||||
|
int fromY = Math.max(1, surfaceY - Math.max(0, depth));
|
||||||
|
int toY = Math.min(maxY, surfaceY + 1);
|
||||||
|
for (int y = fromY; y <= toY; y++) {
|
||||||
|
if (writer.isCarved(x, y, z)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isRegenTraceThread() {
|
private boolean isRegenTraceThread() {
|
||||||
return Thread.currentThread().getName().startsWith("Iris-Regen-")
|
return Thread.currentThread().getName().startsWith("Iris-Regen-")
|
||||||
&& IrisSettings.get().getGeneral().isDebug();
|
&& IrisSettings.get().getGeneral().isDebug();
|
||||||
@@ -1703,6 +1764,97 @@ public class MantleObjectComponent extends IrisMantleComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class SurfaceExposureCache {
|
||||||
|
private final Long2ObjectOpenHashMap<Long2ByteOpenHashMap> depthCaches;
|
||||||
|
|
||||||
|
private SurfaceExposureCache() {
|
||||||
|
this.depthCaches = new Long2ObjectOpenHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean get(MantleWriter writer, SurfaceHeightLookup surfaceHeightLookup, int x, int z, int depth, int maxY) {
|
||||||
|
long depthKey = depth;
|
||||||
|
Long2ByteOpenHashMap columnCache = depthCaches.get(depthKey);
|
||||||
|
if (columnCache == null) {
|
||||||
|
columnCache = new Long2ByteOpenHashMap();
|
||||||
|
columnCache.defaultReturnValue((byte) -1);
|
||||||
|
depthCaches.put(depthKey, columnCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
long columnKey = Cache.key(x, z);
|
||||||
|
byte cached = columnCache.get(columnKey);
|
||||||
|
if (cached != -1) {
|
||||||
|
return cached == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean exposed = hasSurfaceCarveExposureColumn(writer, surfaceHeightLookup, x, z, depth, maxY);
|
||||||
|
columnCache.put(columnKey, (byte) (exposed ? 1 : 0));
|
||||||
|
return exposed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class CaveAnchorCache {
|
||||||
|
private final Long2ObjectOpenHashMap<Long2ObjectOpenHashMap<KList<Integer>>> settingCaches;
|
||||||
|
private final Long2ObjectOpenHashMap<byte[]> carvedColumns;
|
||||||
|
private final Long2IntOpenHashMap surfaceHeights;
|
||||||
|
|
||||||
|
private CaveAnchorCache() {
|
||||||
|
this.settingCaches = new Long2ObjectOpenHashMap<>();
|
||||||
|
this.carvedColumns = new Long2ObjectOpenHashMap<>();
|
||||||
|
this.surfaceHeights = new Long2IntOpenHashMap();
|
||||||
|
this.surfaceHeights.defaultReturnValue(Integer.MIN_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KList<Integer> get(MantleWriter writer, IrisCaveAnchorMode anchorMode, int anchorScanStep, int objectMinDepthBelowSurface, int x, int z) {
|
||||||
|
if (anchorScanStep > 65535 || objectMinDepthBelowSurface > 65535) {
|
||||||
|
return scanCaveAnchorColumn(writer, anchorMode, anchorScanStep, objectMinDepthBelowSurface, x, z, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
long settingKey = ((long) anchorMode.ordinal() << 32) | ((long) anchorScanStep << 16) | objectMinDepthBelowSurface;
|
||||||
|
Long2ObjectOpenHashMap<KList<Integer>> columnCache = settingCaches.get(settingKey);
|
||||||
|
if (columnCache == null) {
|
||||||
|
columnCache = new Long2ObjectOpenHashMap<>();
|
||||||
|
settingCaches.put(settingKey, columnCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
long columnKey = Cache.key(x, z);
|
||||||
|
KList<Integer> anchors = columnCache.get(columnKey);
|
||||||
|
if (anchors != null) {
|
||||||
|
return anchors;
|
||||||
|
}
|
||||||
|
|
||||||
|
anchors = scanCaveAnchorColumn(writer, anchorMode, anchorScanStep, objectMinDepthBelowSurface, x, z, this);
|
||||||
|
columnCache.put(columnKey, anchors);
|
||||||
|
return anchors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] getCarvedColumn(MantleWriter writer, int x, int z, int height) {
|
||||||
|
long columnKey = Cache.key(x, z);
|
||||||
|
byte[] carvedColumn = carvedColumns.get(columnKey);
|
||||||
|
if (carvedColumn != null && carvedColumn.length == height) {
|
||||||
|
return carvedColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
carvedColumn = new byte[height];
|
||||||
|
for (int y = 0; y < height; y++) {
|
||||||
|
carvedColumn[y] = (byte) (writer.isCarved(x, y, z) ? 1 : 0);
|
||||||
|
}
|
||||||
|
carvedColumns.put(columnKey, carvedColumn);
|
||||||
|
return carvedColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSurfaceHeight(int x, int z) {
|
||||||
|
long columnKey = Cache.key(x, z);
|
||||||
|
int surfaceY = surfaceHeights.get(columnKey);
|
||||||
|
if (surfaceY != Integer.MIN_VALUE) {
|
||||||
|
return surfaceY;
|
||||||
|
}
|
||||||
|
|
||||||
|
surfaceY = getEngineMantle().getEngine().getHeight(x, z);
|
||||||
|
surfaceHeights.put(columnKey, surfaceY);
|
||||||
|
return surfaceY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class SurfaceHeightLookup {
|
private static final class SurfaceHeightLookup {
|
||||||
private final ChunkContext context;
|
private final ChunkContext context;
|
||||||
private final ProceduralStream<Double> heightStream;
|
private final ProceduralStream<Double> heightStream;
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<PlatformBlockState
|
|||||||
private static final int CAVE_BIOME_BLEND_RADIUS = 3;
|
private static final int CAVE_BIOME_BLEND_RADIUS = 3;
|
||||||
private static final int CAVE_BIOME_BLEND_CENTER_WEIGHT = 4;
|
private static final int CAVE_BIOME_BLEND_CENTER_WEIGHT = 4;
|
||||||
private static final int CAVE_BIOME_BLEND_TOTAL_WEIGHT = 8;
|
private static final int CAVE_BIOME_BLEND_TOTAL_WEIGHT = 8;
|
||||||
|
private static final MatterCavern BASIC_CAVERN = new MatterCavern(true, "", (byte) 0);
|
||||||
private final RNG rng;
|
private final RNG rng;
|
||||||
private final PlatformBlockState AIR = B.getState("CAVE_AIR");
|
private final PlatformBlockState AIR = B.getState("CAVE_AIR");
|
||||||
private final PlatformBlockState LAVA = B.getState("LAVA");
|
private final PlatformBlockState LAVA = B.getState("LAVA");
|
||||||
@@ -128,20 +129,8 @@ public class IrisCarveModifier extends EngineAssignedModifier<PlatformBlockState
|
|||||||
|
|
||||||
columnMasks[columnIndex].add(yy);
|
columnMasks[columnIndex].add(yy);
|
||||||
|
|
||||||
if (rz < 15 && mc.get(xx, yy, zz + 1, MatterCavern.class) == null) {
|
if (!c.getCustomBiome().isEmpty()) {
|
||||||
walls.put(rx, yy, rz + 1, c);
|
scratch.customCaveBiomePresent = true;
|
||||||
}
|
|
||||||
|
|
||||||
if (rx < 15 && mc.get(xx + 1, yy, zz, MatterCavern.class) == null) {
|
|
||||||
walls.put(rx + 1, yy, rz, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rz > 0 && mc.get(xx, yy, zz - 1, MatterCavern.class) == null) {
|
|
||||||
walls.put(rx, yy, rz - 1, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rx > 0 && mc.get(xx - 1, yy, zz, MatterCavern.class) == null) {
|
|
||||||
walls.put(rx - 1, yy, rz, c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current.isAir()) {
|
if (current.isAir()) {
|
||||||
@@ -160,6 +149,11 @@ public class IrisCarveModifier extends EngineAssignedModifier<PlatformBlockState
|
|||||||
output.setRaw(rx, yy, rz, AIR);
|
output.setRaw(rx, yy, rz, AIR);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
if (scratch.customCaveBiomePresent) {
|
||||||
|
addInternalWallsFromMantle(mc, walls, columnMasks);
|
||||||
|
} else {
|
||||||
|
addInternalWallsFromMasks(walls, columnMasks);
|
||||||
|
}
|
||||||
addCrossChunkBoundaryWalls(mantle, mc, walls, boundaryMasks, boundaryCaverns, x, z, surfaceHeights);
|
addCrossChunkBoundaryWalls(mantle, mc, walls, boundaryMasks, boundaryCaverns, x, z, surfaceHeights);
|
||||||
getEngine().getMetrics().getCarveResolve().put(resolveStopwatch.getMilliseconds());
|
getEngine().getMetrics().getCarveResolve().put(resolveStopwatch.getMilliseconds());
|
||||||
|
|
||||||
@@ -207,6 +201,65 @@ public class IrisCarveModifier extends EngineAssignedModifier<PlatformBlockState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addInternalWallsFromMasks(PackedWallBuffer walls, ColumnMask[] columnMasks) {
|
||||||
|
for (int columnIndex = 0; columnIndex < 256; columnIndex++) {
|
||||||
|
ColumnMask columnMask = columnMasks[columnIndex];
|
||||||
|
if (columnMask.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rx = columnIndex >> 4;
|
||||||
|
int rz = columnIndex & 15;
|
||||||
|
int yy = columnMask.nextSetBit(0);
|
||||||
|
while (yy >= 0) {
|
||||||
|
if (rz < 15 && !columnMasks[columnIndex + 1].contains(yy)) {
|
||||||
|
walls.put(rx, yy, rz + 1, BASIC_CAVERN);
|
||||||
|
}
|
||||||
|
if (rx < 15 && !columnMasks[columnIndex + 16].contains(yy)) {
|
||||||
|
walls.put(rx + 1, yy, rz, BASIC_CAVERN);
|
||||||
|
}
|
||||||
|
if (rz > 0 && !columnMasks[columnIndex - 1].contains(yy)) {
|
||||||
|
walls.put(rx, yy, rz - 1, BASIC_CAVERN);
|
||||||
|
}
|
||||||
|
if (rx > 0 && !columnMasks[columnIndex - 16].contains(yy)) {
|
||||||
|
walls.put(rx - 1, yy, rz, BASIC_CAVERN);
|
||||||
|
}
|
||||||
|
yy = columnMask.nextSetBit(yy + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInternalWallsFromMantle(MantleChunk<Matter> mc, PackedWallBuffer walls, ColumnMask[] columnMasks) {
|
||||||
|
for (int columnIndex = 0; columnIndex < 256; columnIndex++) {
|
||||||
|
ColumnMask columnMask = columnMasks[columnIndex];
|
||||||
|
if (columnMask.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rx = columnIndex >> 4;
|
||||||
|
int rz = columnIndex & 15;
|
||||||
|
int yy = columnMask.nextSetBit(0);
|
||||||
|
while (yy >= 0) {
|
||||||
|
MatterCavern cavern = mc.get(rx, yy, rz, MatterCavern.class);
|
||||||
|
if (cavern != null) {
|
||||||
|
if (rz < 15 && mc.get(rx, yy, rz + 1, MatterCavern.class) == null) {
|
||||||
|
walls.put(rx, yy, rz + 1, cavern);
|
||||||
|
}
|
||||||
|
if (rx < 15 && mc.get(rx + 1, yy, rz, MatterCavern.class) == null) {
|
||||||
|
walls.put(rx + 1, yy, rz, cavern);
|
||||||
|
}
|
||||||
|
if (rz > 0 && mc.get(rx, yy, rz - 1, MatterCavern.class) == null) {
|
||||||
|
walls.put(rx, yy, rz - 1, cavern);
|
||||||
|
}
|
||||||
|
if (rx > 0 && mc.get(rx - 1, yy, rz, MatterCavern.class) == null) {
|
||||||
|
walls.put(rx - 1, yy, rz, cavern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yy = columnMask.nextSetBit(yy + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addCrossChunkBoundaryWalls(
|
private void addCrossChunkBoundaryWalls(
|
||||||
Mantle<Matter> mantle,
|
Mantle<Matter> mantle,
|
||||||
MantleChunk<Matter> mc,
|
MantleChunk<Matter> mc,
|
||||||
@@ -748,6 +801,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<PlatformBlockState
|
|||||||
private final PackedWallBuffer walls = new PackedWallBuffer(512);
|
private final PackedWallBuffer walls = new PackedWallBuffer(512);
|
||||||
private final Map<String, IrisBiome> customBiomeCache = new HashMap<>();
|
private final Map<String, IrisBiome> customBiomeCache = new HashMap<>();
|
||||||
private int[] upperSurfaceHeights;
|
private int[] upperSurfaceHeights;
|
||||||
|
private boolean customCaveBiomePresent;
|
||||||
|
|
||||||
private CarveScratch() {
|
private CarveScratch() {
|
||||||
for (int index = 0; index < columnMasks.length; index++) {
|
for (int index = 0; index < columnMasks.length; index++) {
|
||||||
@@ -771,6 +825,7 @@ public class IrisCarveModifier extends EngineAssignedModifier<PlatformBlockState
|
|||||||
}
|
}
|
||||||
walls.clear();
|
walls.clear();
|
||||||
customBiomeCache.clear();
|
customBiomeCache.clear();
|
||||||
|
customCaveBiomePresent = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -823,6 +878,19 @@ public class IrisCarveModifier extends EngineAssignedModifier<PlatformBlockState
|
|||||||
return maxWord < 0;
|
return maxWord < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean contains(int y) {
|
||||||
|
if (y < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int wordIndex = y >> 6;
|
||||||
|
if (wordIndex > maxWord) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (words[wordIndex] & (1L << (y & 63))) != 0L;
|
||||||
|
}
|
||||||
|
|
||||||
private void clear() {
|
private void clear() {
|
||||||
if (maxWord < 0) {
|
if (maxWord < 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -364,17 +364,21 @@ public final class FloatingIslandSample {
|
|||||||
boolean useProfileCarve = carvingProfileSampler != null;
|
boolean useProfileCarve = carvingProfileSampler != null;
|
||||||
boolean useCarve = directCarveEnabled(entry, carvingProfileSampler, carve, carveThreshold);
|
boolean useCarve = directCarveEnabled(entry, carvingProfileSampler, carve, carveThreshold);
|
||||||
|
|
||||||
for (int k = 0; k < thickness; k++) {
|
if (useWarp) {
|
||||||
int wy = botY + k;
|
for (int k = 0; k < thickness; k++) {
|
||||||
boolean layerSolid = layerFootprintSolid(footprintCng, wallWarp, useWarp, warpAmp, wx, wy, wz, signedCut);
|
int wy = botY + k;
|
||||||
NeighborSupport layerSupport = layerNeighborSupport(footprintCng, wallWarp, useWarp, warpAmp, wx, wy, wz, signedCut);
|
boolean layerSolid = layerFootprintSolid(footprintCng, wallWarp, true, warpAmp, wx, wy, wz, signedCut);
|
||||||
if (layerSolid && !layerSupport.hasSolidSupport()) {
|
NeighborSupport layerSupport = layerNeighborSupport(footprintCng, wallWarp, true, warpAmp, wx, wy, wz, signedCut);
|
||||||
continue;
|
if (layerSolid && !layerSupport.hasSolidSupport()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!layerSolid && !layerSupport.canFillPinhole()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
solidMask[k] = true;
|
||||||
}
|
}
|
||||||
if (!layerSolid && !layerSupport.canFillPinhole()) {
|
} else {
|
||||||
continue;
|
Arrays.fill(solidMask, true);
|
||||||
}
|
|
||||||
solidMask[k] = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int solidCount = solidifyUncarvedInterior(solidMask);
|
int solidCount = solidifyUncarvedInterior(solidMask);
|
||||||
|
|||||||
@@ -79,6 +79,8 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
|||||||
private final transient AtomicCache<KList<CNG>> layerSeaHeightGenerators = new AtomicCache<>();
|
private final transient AtomicCache<KList<CNG>> layerSeaHeightGenerators = new AtomicCache<>();
|
||||||
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
|
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
|
||||||
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
|
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
|
||||||
|
private final transient AtomicCache<IrisOreGeneratorBounds> surfaceOreBoundsCache = new AtomicCache<>();
|
||||||
|
private final transient AtomicCache<IrisOreGeneratorBounds> undergroundOreBoundsCache = new AtomicCache<>();
|
||||||
private final transient AtomicCache<EnumMap<IrisDecorationPart, IrisDecorator[]>> decoratorBuckets = new AtomicCache<>();
|
private final transient AtomicCache<EnumMap<IrisDecorationPart, IrisDecorator[]>> decoratorBuckets = new AtomicCache<>();
|
||||||
private final transient AtomicCache<Biome> derivativeResolved = new AtomicCache<>();
|
private final transient AtomicCache<Biome> derivativeResolved = new AtomicCache<>();
|
||||||
private final transient AtomicCache<Biome> vanillaDerivativeResolved = new AtomicCache<>();
|
private final transient AtomicCache<Biome> vanillaDerivativeResolved = new AtomicCache<>();
|
||||||
@@ -235,6 +237,8 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
|||||||
this.ores = ores == null ? new KList<>() : ores;
|
this.ores = ores == null ? new KList<>() : ores;
|
||||||
surfaceOreCache.reset();
|
surfaceOreCache.reset();
|
||||||
undergroundOreCache.reset();
|
undergroundOreCache.reset();
|
||||||
|
surfaceOreBoundsCache.reset();
|
||||||
|
undergroundOreBoundsCache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
|
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
|
||||||
@@ -245,6 +249,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
|
|||||||
return getOres(false);
|
return getOres(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IrisOreGeneratorBounds getSurfaceOreGeneratorBounds() {
|
||||||
|
return surfaceOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getSurfaceOres()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisOreGeneratorBounds getUndergroundOreGeneratorBounds() {
|
||||||
|
return undergroundOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getUndergroundOres()));
|
||||||
|
}
|
||||||
|
|
||||||
private KList<IrisOreGenerator> getSurfaceOres() {
|
private KList<IrisOreGenerator> getSurfaceOres() {
|
||||||
return getOres(true);
|
return getOres(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
private final transient AtomicCache<Map<String, IrisDimensionCarvingEntry>> carvingEntryIndex = new AtomicCache<>();
|
private final transient AtomicCache<Map<String, IrisDimensionCarvingEntry>> carvingEntryIndex = new AtomicCache<>();
|
||||||
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
|
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
|
||||||
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
|
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
|
||||||
|
private final transient AtomicCache<IrisOreGeneratorBounds> surfaceOreBoundsCache = new AtomicCache<>();
|
||||||
|
private final transient AtomicCache<IrisOreGeneratorBounds> undergroundOreBoundsCache = new AtomicCache<>();
|
||||||
@MinNumber(2)
|
@MinNumber(2)
|
||||||
@Required
|
@Required
|
||||||
@Desc("The human readable name of this dimension")
|
@Desc("The human readable name of this dimension")
|
||||||
@@ -340,6 +342,8 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
this.ores = ores == null ? new KList<>() : ores;
|
this.ores = ores == null ? new KList<>() : ores;
|
||||||
surfaceOreCache.reset();
|
surfaceOreCache.reset();
|
||||||
undergroundOreCache.reset();
|
undergroundOreCache.reset();
|
||||||
|
surfaceOreBoundsCache.reset();
|
||||||
|
undergroundOreBoundsCache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
|
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
|
||||||
@@ -350,6 +354,14 @@ public class IrisDimension extends IrisRegistrant {
|
|||||||
return getOres(false);
|
return getOres(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IrisOreGeneratorBounds getSurfaceOreGeneratorBounds() {
|
||||||
|
return surfaceOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getSurfaceOres()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisOreGeneratorBounds getUndergroundOreGeneratorBounds() {
|
||||||
|
return undergroundOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getUndergroundOres()));
|
||||||
|
}
|
||||||
|
|
||||||
private KList<IrisOreGenerator> getSurfaceOres() {
|
private KList<IrisOreGenerator> getSurfaceOres() {
|
||||||
return getOres(true);
|
return getOres(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,18 +53,20 @@ public class IrisMaterialPalette {
|
|||||||
private KList<IrisBlockData> palette = new KList<IrisBlockData>().qadd(new IrisBlockData("STONE"));
|
private KList<IrisBlockData> palette = new KList<IrisBlockData>().qadd(new IrisBlockData("STONE"));
|
||||||
|
|
||||||
public PlatformBlockState get(RNG rng, double x, double y, double z, IrisData rdata) {
|
public PlatformBlockState get(RNG rng, double x, double y, double z, IrisData rdata) {
|
||||||
if (getBlockData(rdata).isEmpty()) {
|
KList<PlatformBlockState> localBlockData = getBlockData(rdata);
|
||||||
|
int blockDataSize = localBlockData.size();
|
||||||
|
if (blockDataSize == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getBlockData(rdata).size() == 1) {
|
if (blockDataSize == 1) {
|
||||||
return getBlockData(rdata).get(0);
|
return localBlockData.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
double scaledX = x / zoom;
|
double scaledX = x / zoom;
|
||||||
double scaledY = y / zoom;
|
double scaledY = y / zoom;
|
||||||
double scaledZ = z / zoom;
|
double scaledZ = z / zoom;
|
||||||
return getLayerGenerator(rng, rdata).fit(getBlockData(rdata), scaledX, scaledY, scaledZ);
|
return getLayerGenerator(rng, rdata).fit(localBlockData, scaledX, scaledY, scaledZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<TileData> getTile(RNG rng, double x, double y, double z, IrisData rdata) {
|
public Optional<TileData> getTile(RNG rng, double x, double y, double z, IrisData rdata) {
|
||||||
|
|||||||
@@ -32,13 +32,18 @@ import lombok.NoArgsConstructor;
|
|||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
import org.bukkit.Axis;
|
import org.bukkit.Axis;
|
||||||
import org.bukkit.block.BlockFace;
|
import org.bukkit.block.BlockFace;
|
||||||
import org.bukkit.block.data.*;
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.block.data.Directional;
|
||||||
|
import org.bukkit.block.data.MultipleFacing;
|
||||||
|
import org.bukkit.block.data.Orientable;
|
||||||
|
import org.bukkit.block.data.Rotatable;
|
||||||
import org.bukkit.block.data.type.RedstoneWire;
|
import org.bukkit.block.data.type.RedstoneWire;
|
||||||
import org.bukkit.block.data.type.Wall;
|
import org.bukkit.block.data.type.Wall;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
@Snippet("object-rotator")
|
@Snippet("object-rotator")
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@@ -291,11 +296,25 @@ public class IrisObjectRotation {
|
|||||||
return rotator == null ? state : rotator.rotate(this, state, spinx, spiny, spinz);
|
return rotator == null ? state : rotator.rotate(this, state, spinx, spiny, spinz);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockData raw = ((BlockData) state.nativeHandle()).clone();
|
BlockData original = (BlockData) state.nativeHandle();
|
||||||
|
if (!canRotate() || !canRotateBlockData(original)) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockData raw = original.clone();
|
||||||
BlockData rotated = rotate(raw, spinx, spiny, spinz);
|
BlockData rotated = rotate(raw, spinx, spiny, spinz);
|
||||||
return rotated == null ? null : BukkitBlockState.of(rotated);
|
return rotated == null ? null : BukkitBlockState.of(rotated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean canRotateBlockData(BlockData data) {
|
||||||
|
return data instanceof Directional
|
||||||
|
|| data instanceof Rotatable
|
||||||
|
|| data instanceof Orientable
|
||||||
|
|| data instanceof MultipleFacing
|
||||||
|
|| data instanceof Wall
|
||||||
|
|| data instanceof RedstoneWire;
|
||||||
|
}
|
||||||
|
|
||||||
public BlockData rotate(BlockData dd, int spinxx, int spinyy, int spinzz) {
|
public BlockData rotate(BlockData dd, int spinxx, int spinyy, int spinzz) {
|
||||||
BlockData d = dd;
|
BlockData d = dd;
|
||||||
try {
|
try {
|
||||||
@@ -375,7 +394,7 @@ public class IrisObjectRotation {
|
|||||||
} else if (d instanceof RedstoneWire wire) {
|
} else if (d instanceof RedstoneWire wire) {
|
||||||
Map<BlockFace, RedstoneWire.Connection> faces = new HashMap<>();
|
Map<BlockFace, RedstoneWire.Connection> faces = new HashMap<>();
|
||||||
|
|
||||||
var allowed = wire.getAllowedFaces();
|
Set<BlockFace> allowed = wire.getAllowedFaces();
|
||||||
for (BlockFace i : allowed) {
|
for (BlockFace i : allowed) {
|
||||||
RedstoneWire.Connection connection = wire.getFace(i);
|
RedstoneWire.Connection connection = wire.getFace(i);
|
||||||
IrisBlockVector bv = new IrisBlockVector(i.getModX(), i.getModY(), i.getModZ());
|
IrisBlockVector bv = new IrisBlockVector(i.getModX(), i.getModY(), i.getModZ());
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ import lombok.experimental.Accessors;
|
|||||||
@Desc("Scale objects")
|
@Desc("Scale objects")
|
||||||
@Data
|
@Data
|
||||||
public class IrisObjectScale {
|
public class IrisObjectScale {
|
||||||
|
private static final int CACHE_INITIAL_CAPACITY = 256;
|
||||||
|
private static final int CACHE_MAX_WEIGHT = 8192;
|
||||||
private static final ConcurrentLinkedHashMap<CacheKey, KList<IrisObject>> cache
|
private static final ConcurrentLinkedHashMap<CacheKey, KList<IrisObject>> cache
|
||||||
= new ConcurrentLinkedHashMap.Builder<CacheKey, KList<IrisObject>>()
|
= new ConcurrentLinkedHashMap.Builder<CacheKey, KList<IrisObject>>()
|
||||||
.initialCapacity(64)
|
.initialCapacity(CACHE_INITIAL_CAPACITY)
|
||||||
.maximumWeightedCapacity(1024)
|
.maximumWeightedCapacity(CACHE_MAX_WEIGHT)
|
||||||
.concurrencyLevel(32)
|
.concurrencyLevel(32)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ public class IrisOreGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PlatformBlockState generate(int x, int y, int z, RNG rng, IrisData data) {
|
public PlatformBlockState generate(int x, int y, int z, RNG rng, IrisData data) {
|
||||||
if (palette.getPalette().isEmpty()) {
|
if (!range.contains(y)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!range.contains(y)) {
|
if (palette.getPalette().isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package art.arcane.iris.engine.object;
|
||||||
|
|
||||||
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
|
|
||||||
|
public final class IrisOreGeneratorBounds {
|
||||||
|
public static final IrisOreGeneratorBounds EMPTY = new IrisOreGeneratorBounds(false, 0D, 0D);
|
||||||
|
|
||||||
|
private final boolean hasOres;
|
||||||
|
private final double min;
|
||||||
|
private final double max;
|
||||||
|
|
||||||
|
private IrisOreGeneratorBounds(boolean hasOres, double min, double max) {
|
||||||
|
this.hasOres = hasOres;
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IrisOreGeneratorBounds of(KList<IrisOreGenerator> ores) {
|
||||||
|
if (ores == null || ores.isEmpty()) {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
double min = Double.POSITIVE_INFINITY;
|
||||||
|
double max = Double.NEGATIVE_INFINITY;
|
||||||
|
int oreCount = ores.size();
|
||||||
|
for (int oreIndex = 0; oreIndex < oreCount; oreIndex++) {
|
||||||
|
IrisOreGenerator oreGenerator = ores.get(oreIndex);
|
||||||
|
IrisRange range = oreGenerator.getRange();
|
||||||
|
min = Math.min(min, range.getMin());
|
||||||
|
max = Math.max(max, range.getMax());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new IrisOreGeneratorBounds(true, min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasOres() {
|
||||||
|
return hasOres;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(int y) {
|
||||||
|
return hasOres && y >= min && y <= max;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,6 +67,8 @@ public class IrisRegion extends IrisRegistrant implements IRare {
|
|||||||
private final transient AtomicCache<Color> cacheColor = new AtomicCache<>();
|
private final transient AtomicCache<Color> cacheColor = new AtomicCache<>();
|
||||||
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
|
private final transient AtomicCache<KList<IrisOreGenerator>> surfaceOreCache = new AtomicCache<>();
|
||||||
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
|
private final transient AtomicCache<KList<IrisOreGenerator>> undergroundOreCache = new AtomicCache<>();
|
||||||
|
private final transient AtomicCache<IrisOreGeneratorBounds> surfaceOreBoundsCache = new AtomicCache<>();
|
||||||
|
private final transient AtomicCache<IrisOreGeneratorBounds> undergroundOreBoundsCache = new AtomicCache<>();
|
||||||
@MinNumber(2)
|
@MinNumber(2)
|
||||||
@Required
|
@Required
|
||||||
@Desc("The name of the region")
|
@Desc("The name of the region")
|
||||||
@@ -195,6 +197,8 @@ public class IrisRegion extends IrisRegistrant implements IRare {
|
|||||||
this.ores = ores == null ? new KList<>() : ores;
|
this.ores = ores == null ? new KList<>() : ores;
|
||||||
surfaceOreCache.reset();
|
surfaceOreCache.reset();
|
||||||
undergroundOreCache.reset();
|
undergroundOreCache.reset();
|
||||||
|
surfaceOreBoundsCache.reset();
|
||||||
|
undergroundOreBoundsCache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
|
public KList<IrisOreGenerator> getSurfaceOreGenerators() {
|
||||||
@@ -205,6 +209,14 @@ public class IrisRegion extends IrisRegistrant implements IRare {
|
|||||||
return getOres(false);
|
return getOres(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IrisOreGeneratorBounds getSurfaceOreGeneratorBounds() {
|
||||||
|
return surfaceOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getSurfaceOres()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IrisOreGeneratorBounds getUndergroundOreGeneratorBounds() {
|
||||||
|
return undergroundOreBoundsCache.aquire(() -> IrisOreGeneratorBounds.of(getUndergroundOres()));
|
||||||
|
}
|
||||||
|
|
||||||
private KList<IrisOreGenerator> getSurfaceOres() {
|
private KList<IrisOreGenerator> getSurfaceOres() {
|
||||||
return getOres(true);
|
return getOres(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import art.arcane.iris.core.nms.INMS;
|
|||||||
import art.arcane.iris.spi.PlatformBlockState;
|
import art.arcane.iris.spi.PlatformBlockState;
|
||||||
import art.arcane.iris.util.common.data.IrisCustomData;
|
import art.arcane.iris.util.common.data.IrisCustomData;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -38,65 +37,28 @@ public final class BukkitBlockState implements PlatformBlockState {
|
|||||||
private final BlockData data;
|
private final BlockData data;
|
||||||
private final String key;
|
private final String key;
|
||||||
private final String namespace;
|
private final String namespace;
|
||||||
private final Boolean air;
|
private volatile Boolean air;
|
||||||
private final Boolean solid;
|
private volatile Boolean solid;
|
||||||
private final Boolean occluding;
|
private volatile Boolean occluding;
|
||||||
private final Boolean fluid;
|
private volatile Boolean fluid;
|
||||||
private final Boolean water;
|
private volatile Boolean water;
|
||||||
private final Boolean waterLogged;
|
private volatile Boolean waterLogged;
|
||||||
private final Boolean lit;
|
private volatile Boolean lit;
|
||||||
private final Boolean updatable;
|
private volatile Boolean updatable;
|
||||||
private final Boolean foliage;
|
private volatile Boolean foliage;
|
||||||
private final Boolean foliagePlantable;
|
private volatile Boolean foliagePlantable;
|
||||||
private final Boolean decorant;
|
private volatile Boolean decorant;
|
||||||
private final Boolean storage;
|
private volatile Boolean storage;
|
||||||
private final Boolean storageChest;
|
private volatile Boolean storageChest;
|
||||||
private final Boolean ore;
|
private volatile Boolean ore;
|
||||||
private final Boolean deepSlate;
|
private volatile Boolean deepSlate;
|
||||||
private final Boolean vineBlock;
|
private volatile Boolean vineBlock;
|
||||||
private volatile Boolean tileEntity;
|
private volatile Boolean tileEntity;
|
||||||
|
|
||||||
private BukkitBlockState(BlockData data, String key) {
|
private BukkitBlockState(BlockData data, String key) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.namespace = parseNamespace(key);
|
this.namespace = parseNamespace(key);
|
||||||
Material material = data.getMaterial();
|
|
||||||
if (material == null) {
|
|
||||||
this.air = null;
|
|
||||||
this.solid = null;
|
|
||||||
this.occluding = null;
|
|
||||||
this.fluid = null;
|
|
||||||
this.water = null;
|
|
||||||
this.waterLogged = null;
|
|
||||||
this.lit = null;
|
|
||||||
this.updatable = null;
|
|
||||||
this.foliage = null;
|
|
||||||
this.foliagePlantable = null;
|
|
||||||
this.decorant = null;
|
|
||||||
this.storage = null;
|
|
||||||
this.storageChest = null;
|
|
||||||
this.ore = null;
|
|
||||||
this.deepSlate = null;
|
|
||||||
this.vineBlock = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.air = BukkitBlockResolution.isAir(data);
|
|
||||||
this.solid = BukkitBlockResolution.isSolid(data);
|
|
||||||
this.occluding = material.isOccluding();
|
|
||||||
this.fluid = BukkitBlockResolution.isFluid(data);
|
|
||||||
this.water = BukkitBlockResolution.isWater(data);
|
|
||||||
this.waterLogged = BukkitBlockResolution.isWaterLogged(data);
|
|
||||||
this.lit = BukkitBlockResolution.isLit(data);
|
|
||||||
this.updatable = BukkitBlockResolution.isUpdatable(data);
|
|
||||||
this.foliage = BukkitBlockResolution.isFoliage(data);
|
|
||||||
this.foliagePlantable = BukkitBlockResolution.isFoliagePlantable(data);
|
|
||||||
this.decorant = BukkitBlockResolution.isDecorant(data);
|
|
||||||
this.storage = BukkitBlockResolution.isStorage(data);
|
|
||||||
this.storageChest = BukkitBlockResolution.isStorageChest(data);
|
|
||||||
this.ore = BukkitBlockResolution.isOre(data);
|
|
||||||
this.deepSlate = BukkitBlockResolution.isDeepSlate(data);
|
|
||||||
this.vineBlock = BukkitBlockResolution.isVineBlock(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BukkitBlockState of(BlockData data) {
|
public static BukkitBlockState of(BlockData data) {
|
||||||
@@ -173,17 +135,32 @@ public final class BukkitBlockState implements PlatformBlockState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAir() {
|
public boolean isAir() {
|
||||||
return air != null ? air : BukkitBlockResolution.isAir(data);
|
Boolean cached = air;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isAir(data);
|
||||||
|
air = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSolid() {
|
public boolean isSolid() {
|
||||||
return solid != null ? solid : BukkitBlockResolution.isSolid(data);
|
Boolean cached = solid;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isSolid(data);
|
||||||
|
solid = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOccluding() {
|
public boolean isOccluding() {
|
||||||
return occluding != null ? occluding : data.getMaterial().isOccluding();
|
Boolean cached = occluding;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = data.getMaterial().isOccluding();
|
||||||
|
occluding = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -193,67 +170,132 @@ public final class BukkitBlockState implements PlatformBlockState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFluid() {
|
public boolean isFluid() {
|
||||||
return fluid != null ? fluid : BukkitBlockResolution.isFluid(data);
|
Boolean cached = fluid;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isFluid(data);
|
||||||
|
fluid = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWater() {
|
public boolean isWater() {
|
||||||
return water != null ? water : BukkitBlockResolution.isWater(data);
|
Boolean cached = water;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isWater(data);
|
||||||
|
water = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWaterLogged() {
|
public boolean isWaterLogged() {
|
||||||
return waterLogged != null ? waterLogged : BukkitBlockResolution.isWaterLogged(data);
|
Boolean cached = waterLogged;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isWaterLogged(data);
|
||||||
|
waterLogged = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLit() {
|
public boolean isLit() {
|
||||||
return lit != null ? lit : BukkitBlockResolution.isLit(data);
|
Boolean cached = lit;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isLit(data);
|
||||||
|
lit = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isUpdatable() {
|
public boolean isUpdatable() {
|
||||||
return updatable != null ? updatable : BukkitBlockResolution.isUpdatable(data);
|
Boolean cached = updatable;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isUpdatable(data);
|
||||||
|
updatable = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFoliage() {
|
public boolean isFoliage() {
|
||||||
return foliage != null ? foliage : BukkitBlockResolution.isFoliage(data);
|
Boolean cached = foliage;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isFoliage(data);
|
||||||
|
foliage = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFoliagePlantable() {
|
public boolean isFoliagePlantable() {
|
||||||
return foliagePlantable != null ? foliagePlantable : BukkitBlockResolution.isFoliagePlantable(data);
|
Boolean cached = foliagePlantable;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isFoliagePlantable(data);
|
||||||
|
foliagePlantable = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDecorant() {
|
public boolean isDecorant() {
|
||||||
return decorant != null ? decorant : BukkitBlockResolution.isDecorant(data);
|
Boolean cached = decorant;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isDecorant(data);
|
||||||
|
decorant = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isStorage() {
|
public boolean isStorage() {
|
||||||
return storage != null ? storage : BukkitBlockResolution.isStorage(data);
|
Boolean cached = storage;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isStorage(data);
|
||||||
|
storage = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isStorageChest() {
|
public boolean isStorageChest() {
|
||||||
return storageChest != null ? storageChest : BukkitBlockResolution.isStorageChest(data);
|
Boolean cached = storageChest;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isStorageChest(data);
|
||||||
|
storageChest = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isOre() {
|
public boolean isOre() {
|
||||||
return ore != null ? ore : BukkitBlockResolution.isOre(data);
|
Boolean cached = ore;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isOre(data);
|
||||||
|
ore = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDeepSlate() {
|
public boolean isDeepSlate() {
|
||||||
return deepSlate != null ? deepSlate : BukkitBlockResolution.isDeepSlate(data);
|
Boolean cached = deepSlate;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isDeepSlate(data);
|
||||||
|
deepSlate = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVineBlock() {
|
public boolean isVineBlock() {
|
||||||
return vineBlock != null ? vineBlock : BukkitBlockResolution.isVineBlock(data);
|
Boolean cached = vineBlock;
|
||||||
|
if (cached == null) {
|
||||||
|
cached = BukkitBlockResolution.isVineBlock(data);
|
||||||
|
vineBlock = cached;
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -278,10 +320,7 @@ public final class BukkitBlockState implements PlatformBlockState {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAirOrFluid() {
|
public boolean isAirOrFluid() {
|
||||||
if (air != null && fluid != null) {
|
return isAir() || isFluid();
|
||||||
return air || fluid;
|
|
||||||
}
|
|
||||||
return PlatformBlockState.super.isAirOrFluid();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package art.arcane.iris.util.project.hunk.view;
|
package art.arcane.iris.util.project.hunk.view;
|
||||||
|
|
||||||
|
import art.arcane.iris.core.nms.INMS;
|
||||||
import art.arcane.iris.spi.PlatformBlockState;
|
import art.arcane.iris.spi.PlatformBlockState;
|
||||||
import art.arcane.iris.util.common.data.B;
|
import art.arcane.iris.util.common.data.B;
|
||||||
import art.arcane.iris.util.project.hunk.storage.AtomicHunk;
|
import art.arcane.iris.util.project.hunk.storage.AtomicHunk;
|
||||||
@@ -59,7 +60,15 @@ public class ChunkDataHunkHolder extends AtomicHunk<PlatformBlockState> {
|
|||||||
return b != null ? b : States.AIR;
|
return b != null ? b : States.AIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlatformBlockState getStoredRaw(int x, int y, int z) {
|
||||||
|
return super.getRaw(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
public void apply() {
|
public void apply() {
|
||||||
|
if (INMS.get().applyChunkDataBlocks(chunk, this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int height = getHeight();
|
int height = getHeight();
|
||||||
for (int x = 0; x < getWidth(); x++) {
|
for (int x = 0; x < getWidth(); x++) {
|
||||||
for (int z = 0; z < getDepth(); z++) {
|
for (int z = 0; z < getDepth(); z++) {
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ public class CNG {
|
|||||||
private transient double effectiveScale;
|
private transient double effectiveScale;
|
||||||
private transient double signedFractureScale;
|
private transient double signedFractureScale;
|
||||||
private transient boolean fastPathStateDirty = true;
|
private transient boolean fastPathStateDirty = true;
|
||||||
|
private transient int coordCacheSalt;
|
||||||
|
|
||||||
public CNG(RNG random) {
|
public CNG(RNG random) {
|
||||||
this(random, 1);
|
this(random, 1);
|
||||||
@@ -114,6 +115,7 @@ public class CNG {
|
|||||||
this.opacity = opacity;
|
this.opacity = opacity;
|
||||||
this.injector = ADD;
|
this.injector = ADD;
|
||||||
this.injectorMode = InjectorMode.ADD;
|
this.injectorMode = InjectorMode.ADD;
|
||||||
|
coordCacheSalt = createCoordCacheSalt();
|
||||||
|
|
||||||
if (generator instanceof OctaveNoise) {
|
if (generator instanceof OctaveNoise) {
|
||||||
((OctaveNoise) generator).setOctaves(octaves);
|
((OctaveNoise) generator).setOctaves(octaves);
|
||||||
@@ -962,7 +964,7 @@ public class CNG {
|
|||||||
long kx = Double.doubleToRawLongBits(x);
|
long kx = Double.doubleToRawLongBits(x);
|
||||||
long kz = Double.doubleToRawLongBits(z);
|
long kz = Double.doubleToRawLongBits(z);
|
||||||
CoordCache cc = COORD_CACHE_2D.get();
|
CoordCache cc = COORD_CACHE_2D.get();
|
||||||
int slot = coordSlot(kx, kz, System.identityHashCode(this));
|
int slot = coordSlot(kx, kz, coordCacheSalt());
|
||||||
if (cc.owner[slot] == this && cc.kx[slot] == kx && cc.kz[slot] == kz) {
|
if (cc.owner[slot] == this && cc.kx[slot] == kx && cc.kz[slot] == kz) {
|
||||||
return cc.value[slot];
|
return cc.value[slot];
|
||||||
}
|
}
|
||||||
@@ -1013,7 +1015,7 @@ public class CNG {
|
|||||||
long ky = Double.doubleToRawLongBits(y);
|
long ky = Double.doubleToRawLongBits(y);
|
||||||
long kz = Double.doubleToRawLongBits(z);
|
long kz = Double.doubleToRawLongBits(z);
|
||||||
CoordCache3D cc = COORD_CACHE_3D.get();
|
CoordCache3D cc = COORD_CACHE_3D.get();
|
||||||
int slot = coordSlot(kx ^ Long.rotateLeft(ky, 21), kz, System.identityHashCode(this));
|
int slot = coordSlot(kx ^ Long.rotateLeft(ky, 21), kz, coordCacheSalt());
|
||||||
if (cc.owner[slot] == this && cc.kx[slot] == kx && cc.ky[slot] == ky && cc.kz[slot] == kz) {
|
if (cc.owner[slot] == this && cc.kx[slot] == kx && cc.ky[slot] == ky && cc.kz[slot] == kz) {
|
||||||
return cc.value[slot];
|
return cc.value[slot];
|
||||||
}
|
}
|
||||||
@@ -1065,6 +1067,19 @@ public class CNG {
|
|||||||
fastPathStateDirty = true;
|
fastPathStateDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int coordCacheSalt() {
|
||||||
|
if (coordCacheSalt == 0) {
|
||||||
|
coordCacheSalt = createCoordCacheSalt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return coordCacheSalt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int createCoordCacheSalt() {
|
||||||
|
int salt = System.identityHashCode(this);
|
||||||
|
return salt == 0 ? 1 : salt;
|
||||||
|
}
|
||||||
|
|
||||||
private void refreshFastPathState() {
|
private void refreshFastPathState() {
|
||||||
effectiveScale = noscale ? 1D : bakedScale * scale;
|
effectiveScale = noscale ? 1D : bakedScale * scale;
|
||||||
identityPostFastPath = power == 1D
|
identityPostFastPath = power == 1D
|
||||||
|
|||||||
Reference in new issue
Block a user