mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-12-19 15:09:18 +00:00
use coroutines for mantle generation
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <T> void insertMatter(int x, int z, Class<T> t, Hunk<T> 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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ public class KMap<K, V> extends ConcurrentHashMap<K, V> {
|
||||
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<K, V> gMap) {
|
||||
|
||||
@@ -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<Double> height;
|
||||
private ChunkedDataCache<IrisBiome> biome;
|
||||
private ChunkedDataCache<IrisBiome> cave;
|
||||
private ChunkedDataCache<BlockData> rock;
|
||||
private ChunkedDataCache<BlockData> fluid;
|
||||
private ChunkedDataCache<IrisRegion> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<T> {
|
||||
private final int x;
|
||||
private final int z;
|
||||
private final KSet<T> uniques;
|
||||
private final Object[] data;
|
||||
private final boolean cache;
|
||||
private final ProceduralStream<T> stream;
|
||||
|
||||
@BlockCoordinates
|
||||
public ChunkedDataCache(BurstExecutor burst, ProceduralStream<T> stream, int x, int z) {
|
||||
this(burst, stream, x, z, true);
|
||||
}
|
||||
|
||||
@BlockCoordinates
|
||||
public ChunkedDataCache(BurstExecutor burst, ProceduralStream<T> 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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Matter> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,9 +64,15 @@ 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;
|
||||
@@ -76,9 +86,14 @@ public class MultiBurst implements ExecutorService {
|
||||
}
|
||||
},
|
||||
(t, e) -> e.printStackTrace(), true);
|
||||
dispatcher = ExecutorsKt.from(service);
|
||||
return service;
|
||||
}
|
||||
}
|
||||
|
||||
return service;
|
||||
public CoroutineDispatcher getDispatcher() {
|
||||
getService();
|
||||
return dispatcher;
|
||||
}
|
||||
|
||||
public void burst(Runnable... r) {
|
||||
|
||||
@@ -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<Pair<List<MantleComponent>, 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 <T> radius(x: Int, z: Int, radius: Int, collector: suspend FlowCollector<T>.(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)
|
||||
}
|
||||
}
|
||||
@@ -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<Double> = ChunkedDataCache(c.heightStream, x, z, cache)
|
||||
val biome: ChunkedDataCache<IrisBiome> = ChunkedDataCache(c.trueBiomeStream, x, z, cache)
|
||||
val cave: ChunkedDataCache<IrisBiome> = ChunkedDataCache(c.caveBiomeStream, x, z, cache)
|
||||
val rock: ChunkedDataCache<BlockData> = ChunkedDataCache(c.rockStream, x, z, cache)
|
||||
val fluid: ChunkedDataCache<BlockData> = ChunkedDataCache(c.fluidStream, x, z, cache)
|
||||
val region: ChunkedDataCache<IrisRegion> = 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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<T> private constructor(
|
||||
private val x: Int,
|
||||
private val z: Int,
|
||||
private val stream: ProceduralStream<T?>,
|
||||
private val cache: Boolean
|
||||
) {
|
||||
private val data = arrayOfNulls<Any>(if (cache) 256 else 0)
|
||||
|
||||
@JvmOverloads
|
||||
@BlockCoordinates
|
||||
constructor(stream: ProceduralStream<T?>, 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())
|
||||
}
|
||||
}
|
||||
102
core/src/main/kotlin/com/volmit/iris/util/mantle/FlaggedChunk.kt
Normal file
102
core/src/main/kotlin/com/volmit/iris/util/mantle/FlaggedChunk.kt
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user