From be35e4930205d2799f3391f083602694a60ca105 Mon Sep 17 00:00:00 2001 From: Julian Krings Date: Thu, 30 Oct 2025 16:40:08 +0100 Subject: [PATCH] use coroutines for mantle generation --- .../com/volmit/iris/core/IrisSettings.java | 1 + .../iris/core/service/IrisEngineSVC.java | 2 +- .../volmit/iris/engine/IrisEngineMantle.java | 2 + .../volmit/iris/engine/framework/Engine.java | 2 +- .../iris/engine/mantle/EngineMantle.java | 56 +--------- .../iris/engine/mantle/MantleWriter.java | 3 +- .../components/MantleJigsawComponent.java | 3 +- .../com/volmit/iris/util/collection/KMap.java | 6 +- .../iris/util/context/ChunkContext.java | 51 --------- .../iris/util/context/ChunkedDataCache.java | 60 ----------- .../com/volmit/iris/util/mantle/Mantle.java | 12 --- .../volmit/iris/util/mantle/MantleChunk.java | 88 ++------------- .../volmit/iris/util/parallel/MultiBurst.java | 45 +++++--- .../iris/engine/mantle/MatterGenerator.kt | 68 ++++++++++++ .../volmit/iris/util/context/ChunkContext.kt | 35 ++++++ .../iris/util/context/ChunkedDataCache.kt | 46 ++++++++ .../volmit/iris/util/mantle/FlaggedChunk.kt | 102 ++++++++++++++++++ 17 files changed, 305 insertions(+), 277 deletions(-) delete mode 100644 core/src/main/java/com/volmit/iris/util/context/ChunkContext.java delete mode 100644 core/src/main/java/com/volmit/iris/util/context/ChunkedDataCache.java create mode 100644 core/src/main/kotlin/com/volmit/iris/engine/mantle/MatterGenerator.kt create mode 100644 core/src/main/kotlin/com/volmit/iris/util/context/ChunkContext.kt create mode 100644 core/src/main/kotlin/com/volmit/iris/util/context/ChunkedDataCache.kt create mode 100644 core/src/main/kotlin/com/volmit/iris/util/mantle/FlaggedChunk.kt diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 212b1551f..0ee444d2d 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -243,6 +243,7 @@ public class IrisSettings { public int maxBiomeChildDepth = 4; public boolean preventLeafDecay = true; public boolean useMulticore = false; + public boolean useMulticoreMantle = false; public boolean offsetNoiseTypes = false; public boolean earlyCustomBlocks = false; } diff --git a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java index c91b795ea..37c98291f 100644 --- a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java @@ -216,7 +216,7 @@ public class IrisEngineSVC implements IrisService { Iris.error("EngineSVC: Failed to unload for " + name); e.printStackTrace(); } - }, offset + 1000, TRIM_PERIOD, TimeUnit.MILLISECONDS); + }, offset + TRIM_PERIOD / 2, TRIM_PERIOD, TimeUnit.MILLISECONDS); } } diff --git a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java index b788502e9..71a412290 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java @@ -86,11 +86,13 @@ public class IrisEngineMantle implements EngineMantle { .map(components::get) .map(components -> { int radius = components.stream() + .filter(MantleComponent::isEnabled) .mapToInt(MantleComponent::getRadius) .max() .orElse(0); return new Pair<>(List.copyOf(components), radius); }) + .filter(pair -> !pair.getA().isEmpty()) .toList(); int radius = 0; diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java index 651c25ddc..1d84b01b8 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -295,7 +295,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat var chunk = mantle.getChunk(c).use(); try { Semaphore semaphore = new Semaphore(1024); - chunk.raiseFlag(MantleFlag.ETCHED, () -> { + chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> { chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, () -> { chunk.iterate(TileWrapper.class, (x, y, z, v) -> { Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java index 16157fb6e..00d9c2015 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java @@ -29,8 +29,6 @@ import com.volmit.iris.engine.mantle.components.MantleObjectComponent; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisPosition; import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.context.ChunkContext; -import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.data.B; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; @@ -38,7 +36,6 @@ import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.MantleChunk; import com.volmit.iris.util.mantle.flag.MantleFlag; -import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.matter.*; import com.volmit.iris.util.matter.slices.UpdateMatter; import com.volmit.iris.util.parallel.MultiBurst; @@ -49,10 +46,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; -import static com.volmit.iris.util.parallel.StreamUtils.forEach; -import static com.volmit.iris.util.parallel.StreamUtils.streamRadius; - -public interface EngineMantle { +public interface EngineMantle extends MatterGenerator { BlockData AIR = B.get("AIR"); Mantle getMantle(); @@ -180,49 +174,6 @@ public interface EngineMantle { return getEngine().burst(); } - @ChunkCoordinates - default void generateMatter(int x, int z, boolean multicore, ChunkContext context) { - if (!getEngine().getDimension().isUseMantle() || getMantle().hasFlag(x, z, MantleFlag.PLANNED)) { - return; - } - - try (MantleWriter writer = getMantle().write(this, x, z, getRadius() * 2)) { - var iterator = getComponents().iterator(); - while (iterator.hasNext()) { - var pair = iterator.next(); - int radius = pair.getB(); - boolean last = !iterator.hasNext(); - forEach(streamRadius(x, z, radius), - pos -> pair.getA() - .stream() - .filter(MantleComponent::isEnabled) - .map(c -> new Pair<>(c, pos)), - p -> { - MantleComponent c = p.getA(); - Position2 pos = p.getB(); - int xx = pos.getX(); - int zz = pos.getZ(); - IrisContext.getOr(getEngine()).setChunkContext(context); - generateMantleComponent(writer, xx, zz, c, writer.acquireChunk(xx, zz), context); - }, - multicore ? burst() : null - ); - - if (!last) continue; - forEach(streamRadius(x, z, radius), - p -> writer.acquireChunk(x, z).flag(MantleFlag.PLANNED, true), - multicore ? burst() : null - ); - } - } - } - - default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) { - mc.raiseFlag(MantleFlag.PLANNED, c.getFlag(), () -> { - if (c.isEnabled()) c.generateLayer(writer, x, z, context); - }); - } - @ChunkCoordinates default void insertMatter(int x, int z, Class t, Hunk blocks, boolean multicore) { if (!getEngine().getDimension().isUseMantle()) { @@ -262,9 +213,6 @@ public interface EngineMantle { default int getLoadedRegionCount() { return getMantle().getLoadedRegionCount(); } - default long getLastUseMapMemoryUsage(){ - return getMantle().LastUseMapMemoryUsage(); - } MantleJigsawComponent getJigsawComponent(); @@ -290,7 +238,7 @@ public interface EngineMantle { if (!isCovered(x, z)) return; MantleChunk chunk = getMantle().getChunk(x, z).use(); try { - chunk.raiseFlag(MantleFlag.CLEANED, () -> { + chunk.raiseFlagUnchecked(MantleFlag.CLEANED, () -> { chunk.deleteSlices(BlockData.class); chunk.deleteSlices(String.class); chunk.deleteSlices(MatterCavern.class); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java index 408704925..021ccb67c 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java @@ -62,7 +62,8 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { public MantleWriter(EngineMantle engineMantle, Mantle mantle, int x, int z, int radius) { this.engineMantle = engineMantle; this.mantle = mantle; - this.cachedChunks = new KMap<>(); + int d = radius * 2 + 1; + this.cachedChunks = new KMap<>(d * d); this.radius = radius; this.x = x; this.z = z; diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index 8e167a73e..fe4e45f0a 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -35,7 +35,6 @@ import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer; import com.volmit.iris.util.noise.CNG; -import lombok.Getter; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -45,7 +44,7 @@ public class MantleJigsawComponent extends IrisMantleComponent { private final CNG cng; public MantleJigsawComponent(EngineMantle engineMantle) { - super(engineMantle, ReservedFlag.JIGSAW, 2); + super(engineMantle, ReservedFlag.JIGSAW, 1); cng = NoiseStyle.STATIC.create(new RNG(jigsaw())); } diff --git a/core/src/main/java/com/volmit/iris/util/collection/KMap.java b/core/src/main/java/com/volmit/iris/util/collection/KMap.java index 4a7f938e0..60d8a32f0 100644 --- a/core/src/main/java/com/volmit/iris/util/collection/KMap.java +++ b/core/src/main/java/com/volmit/iris/util/collection/KMap.java @@ -33,7 +33,11 @@ public class KMap extends ConcurrentHashMap { private static final long serialVersionUID = 7288942695300448163L; public KMap() { - super(); + this(16); + } + + public KMap(int initialCapacity) { + super(initialCapacity, 0.75f, Runtime.getRuntime().availableProcessors()); } public KMap(Map gMap) { diff --git a/core/src/main/java/com/volmit/iris/util/context/ChunkContext.java b/core/src/main/java/com/volmit/iris/util/context/ChunkContext.java deleted file mode 100644 index b017d49e1..000000000 --- a/core/src/main/java/com/volmit/iris/util/context/ChunkContext.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.volmit.iris.util.context; - -import com.volmit.iris.engine.IrisComplex; -import com.volmit.iris.engine.object.IrisBiome; -import com.volmit.iris.engine.object.IrisRegion; -import com.volmit.iris.util.documentation.BlockCoordinates; -import com.volmit.iris.util.parallel.BurstExecutor; -import com.volmit.iris.util.parallel.MultiBurst; -import lombok.Data; -import org.bukkit.block.data.BlockData; - -@Data -public class ChunkContext { - private final int x; - private final int z; - private ChunkedDataCache height; - private ChunkedDataCache biome; - private ChunkedDataCache cave; - private ChunkedDataCache rock; - private ChunkedDataCache fluid; - private ChunkedDataCache region; - - @BlockCoordinates - public ChunkContext(int x, int z, IrisComplex c) { - this(x, z, c, true); - } - - @BlockCoordinates - public ChunkContext(int x, int z, IrisComplex c, boolean cache) { - this.x = x; - this.z = z; - - if (cache) { - BurstExecutor b = MultiBurst.burst.burst(); - height = new ChunkedDataCache<>(b, c.getHeightStream(), x, z); - biome = new ChunkedDataCache<>(b, c.getTrueBiomeStream(), x, z); - cave = new ChunkedDataCache<>(b, c.getCaveBiomeStream(), x, z); - rock = new ChunkedDataCache<>(b, c.getRockStream(), x, z); - fluid = new ChunkedDataCache<>(b, c.getFluidStream(), x, z); - region = new ChunkedDataCache<>(b, c.getRegionStream(), x, z); - b.complete(); - } else { - height = new ChunkedDataCache<>(null, c.getHeightStream(), x, z, false); - biome = new ChunkedDataCache<>(null, c.getTrueBiomeStream(), x, z, false); - cave = new ChunkedDataCache<>(null, c.getCaveBiomeStream(), x, z, false); - rock = new ChunkedDataCache<>(null, c.getRockStream(), x, z, false); - fluid = new ChunkedDataCache<>(null, c.getFluidStream(), x, z, false); - region = new ChunkedDataCache<>(null, c.getRegionStream(), x, z, false); - } - } -} diff --git a/core/src/main/java/com/volmit/iris/util/context/ChunkedDataCache.java b/core/src/main/java/com/volmit/iris/util/context/ChunkedDataCache.java deleted file mode 100644 index 8c030c2cc..000000000 --- a/core/src/main/java/com/volmit/iris/util/context/ChunkedDataCache.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.volmit.iris.util.context; - -import com.volmit.iris.util.collection.KSet; -import com.volmit.iris.util.documentation.BlockCoordinates; -import com.volmit.iris.util.parallel.BurstExecutor; -import com.volmit.iris.util.stream.ProceduralStream; -import lombok.Data; - -@Data -public class ChunkedDataCache { - private final int x; - private final int z; - private final KSet uniques; - private final Object[] data; - private final boolean cache; - private final ProceduralStream stream; - - @BlockCoordinates - public ChunkedDataCache(BurstExecutor burst, ProceduralStream stream, int x, int z) { - this(burst, stream, x, z, true); - } - - @BlockCoordinates - public ChunkedDataCache(BurstExecutor burst, ProceduralStream stream, int x, int z, boolean cache) { - this.stream = stream; - this.cache = cache; - this.x = x; - this.z = z; - this.uniques = cache ? new KSet<>() : null; - if (cache) { - data = new Object[256]; - int i, j; - - for (i = 0; i < 16; i++) { - int finalI = i; - for (j = 0; j < 16; j++) { - int finalJ = j; - burst.queue(() -> { - T t = stream.get(x + finalI, z + finalJ); - data[(finalJ * 16) + finalI] = t; - uniques.add(t); - }); - } - } - } else { - data = new Object[0]; - } - } - - @SuppressWarnings("unchecked") - @BlockCoordinates - public T get(int x, int z) { - if (!cache) { - return stream.get(this.x + x, this.z + z); - } - - T t = (T) data[(z * 16) + x]; - return t == null ? stream.get(this.x + x, this.z + z) : t; - } -} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 3867a2fc8..38c1549a8 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -406,18 +406,6 @@ public class Mantle { Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath()); } - /** - * Estimates the memory usage of the lastUse map. - * - * @return Estimated memory usage in bytes. - */ - - public long LastUseMapMemoryUsage() { - long numberOfEntries = lastUse.size(); - long bytesPerEntry = Long.BYTES * 2; - return numberOfEntries * bytesPerEntry; - } - /** * Save & unload regions that have not been used for more than the * specified amount of milliseconds diff --git a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java index c432b93f9..bb6ed868e 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java @@ -19,16 +19,13 @@ package com.volmit.iris.util.mantle; import com.volmit.iris.Iris; -import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates; import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.io.CountingDataInputStream; -import com.volmit.iris.util.mantle.flag.MantleFlag; import com.volmit.iris.util.matter.IrisMatter; import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.MatterSlice; -import com.volmit.iris.util.parallel.AtomicBooleanArray; import lombok.Getter; import lombok.SneakyThrows; import org.jetbrains.annotations.Nullable; @@ -44,13 +41,11 @@ import java.util.concurrent.atomic.AtomicReferenceArray; * Represents a mantle chunk. Mantle chunks contain sections of matter (see matter api) * Mantle Chunks are fully atomic & thread safe */ -public class MantleChunk { +public class MantleChunk extends FlaggedChunk { @Getter private final int x; @Getter private final int z; - private final AtomicBooleanArray flags; - private final Object[] flagLocks; private final AtomicReferenceArray sections; private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true); private final AtomicBoolean closed = new AtomicBoolean(false); @@ -63,14 +58,8 @@ public class MantleChunk { @ChunkCoordinates public MantleChunk(int sectionHeight, int x, int z) { sections = new AtomicReferenceArray<>(sectionHeight); - flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1); - flagLocks = new Object[flags.length()]; this.x = x; this.z = z; - - for (int i = 0; i < flags.length(); i++) { - flagLocks[i] = new Object(); - } } /** @@ -84,20 +73,7 @@ public class MantleChunk { public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException { this(sectionHeight, din.readByte(), din.readByte()); int s = din.readByte(); - int l = version < 0 ? 16 : Varint.readUnsignedVarInt(din); - - if (version >= 1) { - for (int i = 0; i < l;) { - byte f = din.readByte(); - for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) { - flags.set(i, (f & (1 << j)) != 0); - } - } - } else { - for (int i = 0; i < flags.length() && i < l; i++) { - flags.set(i, din.readBoolean()); - } - } + readFlags(version, din); for (int i = 0; i < s; i++) { Iris.addPanic("read.section", "Section[" + i + "]"); @@ -156,52 +132,10 @@ public class MantleChunk { public void copyFlags(MantleChunk chunk) { use(); - for (int i = 0; i < flags.length(); i++) { - flags.set(i, chunk.flags.get(i)); - } + super.copyFlags(chunk); release(); } - public void flag(MantleFlag flag, boolean f) { - if (closed.get()) throw new IllegalStateException("Chunk is closed!"); - flags.set(flag.ordinal(), f); - } - - public void raiseFlag(MantleFlag flag, Runnable r) { - raiseFlag(null, flag, r); - } - - public void raiseFlag(@Nullable MantleFlag guard, MantleFlag flag, Runnable r) { - if (closed.get()) throw new IllegalStateException("Chunk is closed!"); - if (guard != null && isFlagged(guard)) return; - synchronized (flagLocks[flag.ordinal()]) { - if (flags.compareAndSet(flag.ordinal(), false, true)) { - try { - r.run(); - } catch (RuntimeException | Error e) { - flags.set(flag.ordinal(), false); - throw e; - } - } - } - } - - public void raiseFlagUnchecked(MantleFlag flag, Runnable r) { - if (closed.get()) throw new IllegalStateException("Chunk is closed!"); - if (flags.compareAndSet(flag.ordinal(), false, true)) { - try { - r.run(); - } catch (RuntimeException | Error e) { - flags.set(flag.ordinal(), false); - throw e; - } - } - } - - public boolean isFlagged(MantleFlag flag) { - return flags.get(flag.ordinal()); - } - /** * Check if a section exists (same as get(section) != null) * @@ -283,16 +217,7 @@ public class MantleChunk { dos.writeByte(x); dos.writeByte(z); dos.writeByte(sections.length()); - Varint.writeUnsignedVarInt(flags.length(), dos); - - int count = flags.length(); - for (int i = 0; i < count;) { - int f = 0; - for (int j = 0; j < Byte.SIZE && i < flags.length(); j++, i++) { - f |= flags.get(i) ? (1 << j) : 0; - } - dos.write(f); - } + writeFlags(dos); var bytes = new ByteArrayOutputStream(8192); var sub = new DataOutputStream(bytes); @@ -360,4 +285,9 @@ public class MantleChunk { } } } + + @Override + public boolean isClosed() { + return closed.get(); + } } diff --git a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java index 32ab14103..8eb82b31f 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java @@ -23,6 +23,8 @@ import com.volmit.iris.core.IrisSettings; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.math.M; import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import kotlinx.coroutines.CoroutineDispatcher; +import kotlinx.coroutines.ExecutorsKt; import org.jetbrains.annotations.NotNull; import java.util.Collection; @@ -39,7 +41,9 @@ public class MultiBurst implements ExecutorService { private final String name; private final int priority; private final IntSupplier parallelism; - private ExecutorService service; + private final Object lock = new Object(); + private volatile ExecutorService service; + private volatile CoroutineDispatcher dispatcher; public MultiBurst() { this("Iris"); @@ -60,25 +64,36 @@ public class MultiBurst implements ExecutorService { last = new AtomicLong(M.ms()); } - private synchronized ExecutorService getService() { + private ExecutorService getService() { last.set(M.ms()); - if (service == null || service.isShutdown()) { + if (service != null && !service.isShutdown()) + return service; + + synchronized (lock) { + if (service != null && !service.isShutdown()) + return service; + service = new ForkJoinPool(IrisSettings.getThreadCount(parallelism.getAsInt()), - new ForkJoinPool.ForkJoinWorkerThreadFactory() { - int m = 0; + new ForkJoinPool.ForkJoinWorkerThreadFactory() { + int m = 0; - @Override - public ForkJoinWorkerThread newThread(ForkJoinPool pool) { - final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); - worker.setPriority(priority); - worker.setName(name + " " + ++m); - return worker; - } - }, - (t, e) -> e.printStackTrace(), true); + @Override + public ForkJoinWorkerThread newThread(ForkJoinPool pool) { + final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + worker.setPriority(priority); + worker.setName(name + " " + ++m); + return worker; + } + }, + (t, e) -> e.printStackTrace(), true); + dispatcher = ExecutorsKt.from(service); + return service; } + } - return service; + public CoroutineDispatcher getDispatcher() { + getService(); + return dispatcher; } public void burst(Runnable... r) { diff --git a/core/src/main/kotlin/com/volmit/iris/engine/mantle/MatterGenerator.kt b/core/src/main/kotlin/com/volmit/iris/engine/mantle/MatterGenerator.kt new file mode 100644 index 000000000..98340fd43 --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/engine/mantle/MatterGenerator.kt @@ -0,0 +1,68 @@ +package com.volmit.iris.engine.mantle + +import com.volmit.iris.core.IrisSettings +import com.volmit.iris.core.nms.container.Pair +import com.volmit.iris.engine.framework.Engine +import com.volmit.iris.util.context.ChunkContext +import com.volmit.iris.util.documentation.ChunkCoordinates +import com.volmit.iris.util.mantle.Mantle +import com.volmit.iris.util.mantle.flag.MantleFlag +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import java.util.concurrent.Executors +import kotlin.coroutines.EmptyCoroutineContext + +interface MatterGenerator { + val engine: Engine + val mantle: Mantle + val radius: Int + val components: List, Int>> + + @ChunkCoordinates + fun generateMatter(x: Int, z: Int, multicore: Boolean, context: ChunkContext) { + if (!engine.dimension.isUseMantle || mantle.hasFlag(x, z, MantleFlag.PLANNED)) + return + + mantle.write(engine.mantle, x, z, radius * 2).use { writer -> + for (pair in components) { + radius(x, z, pair.b, { x, z -> + for (c in pair.a) { + emit(Triple(x, z, c)) + } + }, { (x, z, c) -> launch { + writer.acquireChunk(x, z) + .raiseFlagSuspend(MantleFlag.PLANNED, c.flag) { + if (c.isEnabled) c.generateLayer(writer, x, z, context) + } + }}) + } + + radius(x, z, radius, { x, z -> + emit(Pair(x, z)) + }, { + writer.acquireChunk(it.a, it.b) + .flag(MantleFlag.PLANNED, true) + }) + } + } + + private fun radius(x: Int, z: Int, radius: Int, collector: suspend FlowCollector.(Int, Int) -> Unit, task: suspend CoroutineScope.(T) -> Unit) = runBlocking { + flow { + for (i in -radius..radius) { + for (j in -radius..radius) { + collector(x + i, z + j) + } + } + }.collect { task(it) } + } + + companion object { + private val dispatcher = Executors.newVirtualThreadPerTaskExecutor().asCoroutineDispatcher() + private fun CoroutineScope.launch(block: suspend CoroutineScope.() -> Unit) = + launch(if (IrisSettings.get().generator.isUseMulticoreMantle) dispatcher else EmptyCoroutineContext, block = block) + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/com/volmit/iris/util/context/ChunkContext.kt b/core/src/main/kotlin/com/volmit/iris/util/context/ChunkContext.kt new file mode 100644 index 000000000..26015204b --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/util/context/ChunkContext.kt @@ -0,0 +1,35 @@ +package com.volmit.iris.util.context + +import com.volmit.iris.engine.IrisComplex +import com.volmit.iris.engine.`object`.IrisBiome +import com.volmit.iris.engine.`object`.IrisRegion +import com.volmit.iris.util.parallel.MultiBurst +import kotlinx.coroutines.* +import org.bukkit.block.data.BlockData + +class ChunkContext @JvmOverloads constructor( + val x: Int, + val z: Int, + c: IrisComplex, + cache: Boolean = true, +) { + val height: ChunkedDataCache = ChunkedDataCache(c.heightStream, x, z, cache) + val biome: ChunkedDataCache = ChunkedDataCache(c.trueBiomeStream, x, z, cache) + val cave: ChunkedDataCache = ChunkedDataCache(c.caveBiomeStream, x, z, cache) + val rock: ChunkedDataCache = ChunkedDataCache(c.rockStream, x, z, cache) + val fluid: ChunkedDataCache = ChunkedDataCache(c.fluidStream, x, z, cache) + val region: ChunkedDataCache = ChunkedDataCache(c.regionStream, x, z, cache) + + init { + if (cache) runBlocking { + val dispatcher = MultiBurst.burst.dispatcher + + launch { height.fill(dispatcher) } + launch { biome.fill(dispatcher) } + launch { cave.fill(dispatcher) } + launch { rock.fill(dispatcher) } + launch { fluid.fill(dispatcher) } + launch { region.fill(dispatcher) } + } + } +} diff --git a/core/src/main/kotlin/com/volmit/iris/util/context/ChunkedDataCache.kt b/core/src/main/kotlin/com/volmit/iris/util/context/ChunkedDataCache.kt new file mode 100644 index 000000000..a730a7aca --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/util/context/ChunkedDataCache.kt @@ -0,0 +1,46 @@ +package com.volmit.iris.util.context + +import com.volmit.iris.util.documentation.BlockCoordinates +import com.volmit.iris.util.stream.ProceduralStream +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.supervisorScope +import kotlin.coroutines.CoroutineContext + +class ChunkedDataCache private constructor( + private val x: Int, + private val z: Int, + private val stream: ProceduralStream, + private val cache: Boolean +) { + private val data = arrayOfNulls(if (cache) 256 else 0) + + @JvmOverloads + @BlockCoordinates + constructor(stream: ProceduralStream, x: Int, z: Int, cache: Boolean = true) : this(x, z, stream, cache) + + suspend fun fill(context: CoroutineContext = Dispatchers.Default) { + if (!cache) return + + supervisorScope { + for (i in 0 until 16) { + for (j in 0 until 16) { + launch(context) { + val t = stream.get((x + i).toDouble(), (z + j).toDouble()) + data[(j * 16) + i] = t + } + } + } + } + } + + @BlockCoordinates + fun get(x: Int, z: Int): T? { + if (!cache) { + return stream.get((this.x + x).toDouble(), (this.z + z).toDouble()) + } + + val t = data[(z * 16) + x] as? T + return t ?: stream.get((this.x + x).toDouble(), (this.z + z).toDouble()) + } +} diff --git a/core/src/main/kotlin/com/volmit/iris/util/mantle/FlaggedChunk.kt b/core/src/main/kotlin/com/volmit/iris/util/mantle/FlaggedChunk.kt new file mode 100644 index 000000000..6fcdf6719 --- /dev/null +++ b/core/src/main/kotlin/com/volmit/iris/util/mantle/FlaggedChunk.kt @@ -0,0 +1,102 @@ +package com.volmit.iris.util.mantle + +import com.volmit.iris.util.data.Varint +import com.volmit.iris.util.mantle.flag.MantleFlag +import com.volmit.iris.util.parallel.AtomicBooleanArray +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import java.io.DataInput +import java.io.DataOutput +import java.io.IOException +import java.lang.Byte +import kotlin.Boolean +import kotlin.IllegalStateException +import kotlin.Int +import kotlin.Throwable +import kotlin.Throws +import kotlin.Unit + +abstract class FlaggedChunk() { + private val flags = AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1) + private val locks = 0.rangeUntil(flags.length()).map { Mutex() }.toTypedArray() + + abstract fun isClosed(): Boolean + + protected fun copyFlags(other: FlaggedChunk) { + for (i in 0 until flags.length()) { + flags.set(i, other.flags.get(i)) + } + } + + fun isFlagged(flag: MantleFlag) = flags.get(flag.ordinal()) + fun flag(flag: MantleFlag, value: Boolean) { + if (isClosed()) throw IllegalStateException("Chunk is closed!") + flags.set(flag.ordinal(), value) + } + + suspend fun raiseFlagSuspend(guard: MantleFlag?, flag: MantleFlag, task: suspend () -> Unit) { + if (isClosed()) throw IllegalStateException("Chunk is closed!") + if (guard != null && isFlagged(guard)) return + + locks[flag.ordinal()].withLock { + if (flags.compareAndSet(flag.ordinal(), false, true)) { + try { + task() + } catch (e: Throwable) { + flags.set(flag.ordinal(), false) + throw e + } + } + } + } + + fun raiseFlagUnchecked(flag: MantleFlag, task: Runnable) { + if (isClosed()) throw IllegalStateException("Chunk is closed!") + if (flags.compareAndSet(flag.ordinal(), false, true)) { + try { + task.run() + } catch (e: Throwable) { + flags.set(flag.ordinal(), false) + throw e + } + } + } + + @Throws(IOException::class) + protected fun readFlags(version: Int, din: DataInput) { + val l = if (version < 0) 16 else Varint.readUnsignedVarInt(din) + + if (version >= 1) { + var i = 0 + while (i < l) { + val f = din.readByte() + var j = 0 + while (j < Byte.SIZE && i < flags.length()) { + flags.set(i, (f.toInt() and (1 shl j)) != 0) + j++ + i++ + } + } + } else { + for (i in 0 until l) { + flags.set(i, din.readBoolean()) + } + } + } + + @Throws(IOException::class) + protected fun writeFlags(dos: DataOutput) { + Varint.writeUnsignedVarInt(flags.length(), dos) + val count = flags.length() + var i = 0 + while (i < count) { + var f = 0 + for (j in 0 until Byte.SIZE) { + if (i >= count) break + f = f or if (flags.get(i)) 1 shl j else 0 + i++ + } + dos.write(f) + } + } +} \ No newline at end of file