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 int maxBiomeChildDepth = 4;
|
||||||
public boolean preventLeafDecay = true;
|
public boolean preventLeafDecay = true;
|
||||||
public boolean useMulticore = false;
|
public boolean useMulticore = false;
|
||||||
|
public boolean useMulticoreMantle = false;
|
||||||
public boolean offsetNoiseTypes = false;
|
public boolean offsetNoiseTypes = false;
|
||||||
public boolean earlyCustomBlocks = false;
|
public boolean earlyCustomBlocks = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ public class IrisEngineSVC implements IrisService {
|
|||||||
Iris.error("EngineSVC: Failed to unload for " + name);
|
Iris.error("EngineSVC: Failed to unload for " + name);
|
||||||
e.printStackTrace();
|
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::get)
|
||||||
.map(components -> {
|
.map(components -> {
|
||||||
int radius = components.stream()
|
int radius = components.stream()
|
||||||
|
.filter(MantleComponent::isEnabled)
|
||||||
.mapToInt(MantleComponent::getRadius)
|
.mapToInt(MantleComponent::getRadius)
|
||||||
.max()
|
.max()
|
||||||
.orElse(0);
|
.orElse(0);
|
||||||
return new Pair<>(List.copyOf(components), radius);
|
return new Pair<>(List.copyOf(components), radius);
|
||||||
})
|
})
|
||||||
|
.filter(pair -> !pair.getA().isEmpty())
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
int radius = 0;
|
int radius = 0;
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
var chunk = mantle.getChunk(c).use();
|
var chunk = mantle.getChunk(c).use();
|
||||||
try {
|
try {
|
||||||
Semaphore semaphore = new Semaphore(1024);
|
Semaphore semaphore = new Semaphore(1024);
|
||||||
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
|
chunk.raiseFlagUnchecked(MantleFlag.ETCHED, () -> {
|
||||||
chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, () -> {
|
chunk.raiseFlagUnchecked(MantleFlag.TILE, run(semaphore, () -> {
|
||||||
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
|
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
|
||||||
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
|
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.IrisDimension;
|
||||||
import com.volmit.iris.engine.object.IrisPosition;
|
import com.volmit.iris.engine.object.IrisPosition;
|
||||||
import com.volmit.iris.util.collection.KList;
|
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.data.B;
|
||||||
import com.volmit.iris.util.documentation.BlockCoordinates;
|
import com.volmit.iris.util.documentation.BlockCoordinates;
|
||||||
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
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.Mantle;
|
||||||
import com.volmit.iris.util.mantle.MantleChunk;
|
import com.volmit.iris.util.mantle.MantleChunk;
|
||||||
import com.volmit.iris.util.mantle.flag.MantleFlag;
|
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.*;
|
||||||
import com.volmit.iris.util.matter.slices.UpdateMatter;
|
import com.volmit.iris.util.matter.slices.UpdateMatter;
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
@@ -49,10 +46,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static com.volmit.iris.util.parallel.StreamUtils.forEach;
|
public interface EngineMantle extends MatterGenerator {
|
||||||
import static com.volmit.iris.util.parallel.StreamUtils.streamRadius;
|
|
||||||
|
|
||||||
public interface EngineMantle {
|
|
||||||
BlockData AIR = B.get("AIR");
|
BlockData AIR = B.get("AIR");
|
||||||
|
|
||||||
Mantle getMantle();
|
Mantle getMantle();
|
||||||
@@ -180,49 +174,6 @@ public interface EngineMantle {
|
|||||||
return getEngine().burst();
|
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
|
@ChunkCoordinates
|
||||||
default <T> void insertMatter(int x, int z, Class<T> t, Hunk<T> blocks, boolean multicore) {
|
default <T> void insertMatter(int x, int z, Class<T> t, Hunk<T> blocks, boolean multicore) {
|
||||||
if (!getEngine().getDimension().isUseMantle()) {
|
if (!getEngine().getDimension().isUseMantle()) {
|
||||||
@@ -262,9 +213,6 @@ public interface EngineMantle {
|
|||||||
default int getLoadedRegionCount() {
|
default int getLoadedRegionCount() {
|
||||||
return getMantle().getLoadedRegionCount();
|
return getMantle().getLoadedRegionCount();
|
||||||
}
|
}
|
||||||
default long getLastUseMapMemoryUsage(){
|
|
||||||
return getMantle().LastUseMapMemoryUsage();
|
|
||||||
}
|
|
||||||
|
|
||||||
MantleJigsawComponent getJigsawComponent();
|
MantleJigsawComponent getJigsawComponent();
|
||||||
|
|
||||||
@@ -290,7 +238,7 @@ public interface EngineMantle {
|
|||||||
if (!isCovered(x, z)) return;
|
if (!isCovered(x, z)) return;
|
||||||
MantleChunk chunk = getMantle().getChunk(x, z).use();
|
MantleChunk chunk = getMantle().getChunk(x, z).use();
|
||||||
try {
|
try {
|
||||||
chunk.raiseFlag(MantleFlag.CLEANED, () -> {
|
chunk.raiseFlagUnchecked(MantleFlag.CLEANED, () -> {
|
||||||
chunk.deleteSlices(BlockData.class);
|
chunk.deleteSlices(BlockData.class);
|
||||||
chunk.deleteSlices(String.class);
|
chunk.deleteSlices(String.class);
|
||||||
chunk.deleteSlices(MatterCavern.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) {
|
public MantleWriter(EngineMantle engineMantle, Mantle mantle, int x, int z, int radius) {
|
||||||
this.engineMantle = engineMantle;
|
this.engineMantle = engineMantle;
|
||||||
this.mantle = mantle;
|
this.mantle = mantle;
|
||||||
this.cachedChunks = new KMap<>();
|
int d = radius * 2 + 1;
|
||||||
|
this.cachedChunks = new KMap<>(d * d);
|
||||||
this.radius = radius;
|
this.radius = radius;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.z = z;
|
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.math.RNG;
|
||||||
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
|
import com.volmit.iris.util.matter.slices.container.JigsawStructuresContainer;
|
||||||
import com.volmit.iris.util.noise.CNG;
|
import com.volmit.iris.util.noise.CNG;
|
||||||
import lombok.Getter;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -45,7 +44,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
|||||||
private final CNG cng;
|
private final CNG cng;
|
||||||
|
|
||||||
public MantleJigsawComponent(EngineMantle engineMantle) {
|
public MantleJigsawComponent(EngineMantle engineMantle) {
|
||||||
super(engineMantle, ReservedFlag.JIGSAW, 2);
|
super(engineMantle, ReservedFlag.JIGSAW, 1);
|
||||||
cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
|
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;
|
private static final long serialVersionUID = 7288942695300448163L;
|
||||||
|
|
||||||
public KMap() {
|
public KMap() {
|
||||||
super();
|
this(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KMap(int initialCapacity) {
|
||||||
|
super(initialCapacity, 0.75f, Runtime.getRuntime().availableProcessors());
|
||||||
}
|
}
|
||||||
|
|
||||||
public KMap(Map<K, V> gMap) {
|
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());
|
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
|
* Save & unload regions that have not been used for more than the
|
||||||
* specified amount of milliseconds
|
* specified amount of milliseconds
|
||||||
|
|||||||
@@ -19,16 +19,13 @@
|
|||||||
package com.volmit.iris.util.mantle;
|
package com.volmit.iris.util.mantle;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
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.ChunkCoordinates;
|
||||||
import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates;
|
import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates;
|
||||||
import com.volmit.iris.util.function.Consumer4;
|
import com.volmit.iris.util.function.Consumer4;
|
||||||
import com.volmit.iris.util.io.CountingDataInputStream;
|
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.IrisMatter;
|
||||||
import com.volmit.iris.util.matter.Matter;
|
import com.volmit.iris.util.matter.Matter;
|
||||||
import com.volmit.iris.util.matter.MatterSlice;
|
import com.volmit.iris.util.matter.MatterSlice;
|
||||||
import com.volmit.iris.util.parallel.AtomicBooleanArray;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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)
|
* Represents a mantle chunk. Mantle chunks contain sections of matter (see matter api)
|
||||||
* Mantle Chunks are fully atomic & thread safe
|
* Mantle Chunks are fully atomic & thread safe
|
||||||
*/
|
*/
|
||||||
public class MantleChunk {
|
public class MantleChunk extends FlaggedChunk {
|
||||||
@Getter
|
@Getter
|
||||||
private final int x;
|
private final int x;
|
||||||
@Getter
|
@Getter
|
||||||
private final int z;
|
private final int z;
|
||||||
private final AtomicBooleanArray flags;
|
|
||||||
private final Object[] flagLocks;
|
|
||||||
private final AtomicReferenceArray<Matter> sections;
|
private final AtomicReferenceArray<Matter> sections;
|
||||||
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
|
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
|
||||||
private final AtomicBoolean closed = new AtomicBoolean(false);
|
private final AtomicBoolean closed = new AtomicBoolean(false);
|
||||||
@@ -63,14 +58,8 @@ public class MantleChunk {
|
|||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
public MantleChunk(int sectionHeight, int x, int z) {
|
public MantleChunk(int sectionHeight, int x, int z) {
|
||||||
sections = new AtomicReferenceArray<>(sectionHeight);
|
sections = new AtomicReferenceArray<>(sectionHeight);
|
||||||
flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1);
|
|
||||||
flagLocks = new Object[flags.length()];
|
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.z = z;
|
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 {
|
public MantleChunk(int version, int sectionHeight, CountingDataInputStream din) throws IOException {
|
||||||
this(sectionHeight, din.readByte(), din.readByte());
|
this(sectionHeight, din.readByte(), din.readByte());
|
||||||
int s = din.readByte();
|
int s = din.readByte();
|
||||||
int l = version < 0 ? 16 : Varint.readUnsignedVarInt(din);
|
readFlags(version, 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < s; i++) {
|
for (int i = 0; i < s; i++) {
|
||||||
Iris.addPanic("read.section", "Section[" + i + "]");
|
Iris.addPanic("read.section", "Section[" + i + "]");
|
||||||
@@ -156,52 +132,10 @@ public class MantleChunk {
|
|||||||
|
|
||||||
public void copyFlags(MantleChunk chunk) {
|
public void copyFlags(MantleChunk chunk) {
|
||||||
use();
|
use();
|
||||||
for (int i = 0; i < flags.length(); i++) {
|
super.copyFlags(chunk);
|
||||||
flags.set(i, chunk.flags.get(i));
|
|
||||||
}
|
|
||||||
release();
|
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)
|
* Check if a section exists (same as get(section) != null)
|
||||||
*
|
*
|
||||||
@@ -283,16 +217,7 @@ public class MantleChunk {
|
|||||||
dos.writeByte(x);
|
dos.writeByte(x);
|
||||||
dos.writeByte(z);
|
dos.writeByte(z);
|
||||||
dos.writeByte(sections.length());
|
dos.writeByte(sections.length());
|
||||||
Varint.writeUnsignedVarInt(flags.length(), dos);
|
writeFlags(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
var bytes = new ByteArrayOutputStream(8192);
|
var bytes = new ByteArrayOutputStream(8192);
|
||||||
var sub = new DataOutputStream(bytes);
|
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.collection.KList;
|
||||||
import com.volmit.iris.util.math.M;
|
import com.volmit.iris.util.math.M;
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher;
|
||||||
|
import kotlinx.coroutines.ExecutorsKt;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -39,7 +41,9 @@ public class MultiBurst implements ExecutorService {
|
|||||||
private final String name;
|
private final String name;
|
||||||
private final int priority;
|
private final int priority;
|
||||||
private final IntSupplier parallelism;
|
private final IntSupplier parallelism;
|
||||||
private ExecutorService service;
|
private final Object lock = new Object();
|
||||||
|
private volatile ExecutorService service;
|
||||||
|
private volatile CoroutineDispatcher dispatcher;
|
||||||
|
|
||||||
public MultiBurst() {
|
public MultiBurst() {
|
||||||
this("Iris");
|
this("Iris");
|
||||||
@@ -60,25 +64,36 @@ public class MultiBurst implements ExecutorService {
|
|||||||
last = new AtomicLong(M.ms());
|
last = new AtomicLong(M.ms());
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized ExecutorService getService() {
|
private ExecutorService getService() {
|
||||||
last.set(M.ms());
|
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()),
|
service = new ForkJoinPool(IrisSettings.getThreadCount(parallelism.getAsInt()),
|
||||||
new ForkJoinPool.ForkJoinWorkerThreadFactory() {
|
new ForkJoinPool.ForkJoinWorkerThreadFactory() {
|
||||||
int m = 0;
|
int m = 0;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
|
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
|
||||||
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
|
final ForkJoinWorkerThread worker = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
|
||||||
worker.setPriority(priority);
|
worker.setPriority(priority);
|
||||||
worker.setName(name + " " + ++m);
|
worker.setName(name + " " + ++m);
|
||||||
return worker;
|
return worker;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(t, e) -> e.printStackTrace(), true);
|
(t, e) -> e.printStackTrace(), true);
|
||||||
|
dispatcher = ExecutorsKt.from(service);
|
||||||
|
return service;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return service;
|
public CoroutineDispatcher getDispatcher() {
|
||||||
|
getService();
|
||||||
|
return dispatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void burst(Runnable... r) {
|
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