mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2025-12-19 15:09:18 +00:00
@@ -105,7 +105,7 @@ nmsBindings.forEach { key, value ->
|
|||||||
pluginJars(tasks.jar.flatMap { it.archiveFile })
|
pluginJars(tasks.jar.flatMap { it.archiveFile })
|
||||||
javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(jvmVersion.getOrDefault(key, 21))}
|
javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(jvmVersion.getOrDefault(key, 21))}
|
||||||
runDirectory.convention(layout.buildDirectory.dir("run/$key"))
|
runDirectory.convention(layout.buildDirectory.dir("run/$key"))
|
||||||
systemProperty("disable.watchdog", "")
|
systemProperty("disable.watchdog", "true")
|
||||||
systemProperty("net.kyori.ansi.colorLevel", color)
|
systemProperty("net.kyori.ansi.colorLevel", color)
|
||||||
systemProperty("com.mojang.eula.agree", true)
|
systemProperty("com.mojang.eula.agree", true)
|
||||||
systemProperty("iris.suppressReporting", !errorReporting)
|
systemProperty("iris.suppressReporting", !errorReporting)
|
||||||
|
|||||||
@@ -96,6 +96,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
public static IrisCompat compat;
|
public static IrisCompat compat;
|
||||||
public static FileWatcher configWatcher;
|
public static FileWatcher configWatcher;
|
||||||
private static VolmitSender sender;
|
private static VolmitSender sender;
|
||||||
|
private static Thread shutdownHook;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
@@ -453,6 +454,7 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
configWatcher = new FileWatcher(getDataFile("settings.json"));
|
||||||
services.values().forEach(IrisService::onEnable);
|
services.values().forEach(IrisService::onEnable);
|
||||||
services.values().forEach(this::registerListener);
|
services.values().forEach(this::registerListener);
|
||||||
|
addShutdownHook();
|
||||||
J.s(() -> {
|
J.s(() -> {
|
||||||
J.a(IrisSafeguard::suggestPaper);
|
J.a(IrisSafeguard::suggestPaper);
|
||||||
J.a(() -> IO.delete(getTemp()));
|
J.a(() -> IO.delete(getTemp()));
|
||||||
@@ -471,6 +473,23 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addShutdownHook() {
|
||||||
|
if (shutdownHook != null) {
|
||||||
|
Runtime.getRuntime().removeShutdownHook(shutdownHook);
|
||||||
|
}
|
||||||
|
shutdownHook = new Thread(() -> {
|
||||||
|
Bukkit.getWorlds()
|
||||||
|
.stream()
|
||||||
|
.map(IrisToolbelt::access)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(PlatformChunkGenerator::close);
|
||||||
|
|
||||||
|
MultiBurst.burst.close();
|
||||||
|
services.clear();
|
||||||
|
});
|
||||||
|
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||||
|
}
|
||||||
|
|
||||||
public void checkForBukkitWorlds(Predicate<String> filter) {
|
public void checkForBukkitWorlds(Predicate<String> filter) {
|
||||||
try {
|
try {
|
||||||
IrisWorlds.readBukkitWorlds().forEach((s, generator) -> {
|
IrisWorlds.readBukkitWorlds().forEach((s, generator) -> {
|
||||||
@@ -548,16 +567,6 @@ public class Iris extends VolmitPlugin implements Listener {
|
|||||||
super.onDisable();
|
super.onDisable();
|
||||||
|
|
||||||
J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan);
|
J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan);
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
|
||||||
Bukkit.getWorlds()
|
|
||||||
.stream()
|
|
||||||
.map(IrisToolbelt::access)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.forEach(PlatformChunkGenerator::close);
|
|
||||||
|
|
||||||
MultiBurst.burst.close();
|
|
||||||
services.clear();
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupPapi() {
|
private void setupPapi() {
|
||||||
|
|||||||
@@ -176,13 +176,13 @@ public class IrisSettings {
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public static class IrisSettingsUpdater {
|
public static class IrisSettingsUpdater {
|
||||||
public double threadMultiplier = 2;
|
public int maxConcurrency = 256;
|
||||||
public double chunkLoadSensitivity = 0.7;
|
public double chunkLoadSensitivity = 0.7;
|
||||||
public MsRange emptyMsRange = new MsRange(80, 100);
|
public MsRange emptyMsRange = new MsRange(80, 100);
|
||||||
public MsRange defaultMsRange = new MsRange(20, 40);
|
public MsRange defaultMsRange = new MsRange(20, 40);
|
||||||
|
|
||||||
public double getThreadMultiplier() {
|
public int getMaxConcurrency() {
|
||||||
return Math.min(Math.abs(threadMultiplier), 0.1);
|
return Math.max(Math.abs(maxConcurrency), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getChunkLoadSensitivity() {
|
public double getChunkLoadSensitivity() {
|
||||||
@@ -242,6 +242,7 @@ public class IrisSettings {
|
|||||||
public String defaultWorldType = "overworld";
|
public String defaultWorldType = "overworld";
|
||||||
public int maxBiomeChildDepth = 4;
|
public int maxBiomeChildDepth = 4;
|
||||||
public boolean preventLeafDecay = true;
|
public boolean preventLeafDecay = true;
|
||||||
|
public boolean useMulticore = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@@ -255,6 +256,7 @@ public class IrisSettings {
|
|||||||
@Data
|
@Data
|
||||||
public static class IrisSettingsEngineSVC {
|
public static class IrisSettingsEngineSVC {
|
||||||
public boolean useVirtualThreads = true;
|
public boolean useVirtualThreads = true;
|
||||||
|
public boolean forceMulticoreWrite = false;
|
||||||
public int priority = Thread.NORM_PRIORITY;
|
public int priority = Thread.NORM_PRIORITY;
|
||||||
|
|
||||||
public int getPriority() {
|
public int getPriority() {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import com.volmit.iris.util.io.CountingDataInputStream;
|
|||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.util.io.IO;
|
||||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||||
import com.volmit.iris.util.math.M;
|
import com.volmit.iris.util.math.M;
|
||||||
|
import com.volmit.iris.util.matter.Matter;
|
||||||
import com.volmit.iris.util.nbt.mca.MCAFile;
|
import com.volmit.iris.util.nbt.mca.MCAFile;
|
||||||
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
import com.volmit.iris.util.nbt.mca.MCAUtil;
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
@@ -53,6 +54,7 @@ import org.bukkit.World;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.NetworkInterface;
|
import java.net.NetworkInterface;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
@@ -64,7 +66,6 @@ import java.util.zip.GZIPOutputStream;
|
|||||||
public class CommandDeveloper implements DecreeExecutor {
|
public class CommandDeveloper implements DecreeExecutor {
|
||||||
private CommandTurboPregen turboPregen;
|
private CommandTurboPregen turboPregen;
|
||||||
private CommandLazyPregen lazyPregen;
|
private CommandLazyPregen lazyPregen;
|
||||||
private CommandUpdater updater;
|
|
||||||
|
|
||||||
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
|
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
|
||||||
public void EngineStatus() {
|
public void EngineStatus() {
|
||||||
@@ -79,6 +80,33 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
Iris.reportError(new Exception("This is a test"));
|
Iris.reportError(new Exception("This is a test"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Decree(description = "Test")
|
||||||
|
public void mantle(@Param(defaultValue = "false") boolean plate, @Param(defaultValue = "21474836474") String name) throws Throwable {
|
||||||
|
var base = Iris.instance.getDataFile("dump", "pv." + name + ".ttp.lz4b.bin");
|
||||||
|
var section = Iris.instance.getDataFile("dump", "pv." + name + ".section.bin");
|
||||||
|
|
||||||
|
//extractSection(base, section, 5604930, 4397);
|
||||||
|
|
||||||
|
if (plate) {
|
||||||
|
try (var in = CountingDataInputStream.wrap(new BufferedInputStream(new FileInputStream(base)))) {
|
||||||
|
new TectonicPlate(1088, in, true);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
} else Matter.read(section);
|
||||||
|
if (!TectonicPlate.hasError())
|
||||||
|
Iris.info("Read " + (plate ? base : section).length() + " bytes from " + (plate ? base : section).getAbsolutePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractSection(File source, File target, long offset, int length) throws IOException {
|
||||||
|
var raf = new RandomAccessFile(source, "r");
|
||||||
|
var bytes = new byte[length];
|
||||||
|
raf.seek(offset);
|
||||||
|
raf.readFully(bytes);
|
||||||
|
raf.close();
|
||||||
|
Files.write(target.toPath(), bytes);
|
||||||
|
}
|
||||||
|
|
||||||
@Decree(description = "Test")
|
@Decree(description = "Test")
|
||||||
public void dumpThreads() {
|
public void dumpThreads() {
|
||||||
try {
|
try {
|
||||||
@@ -115,27 +143,6 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Decree(description = "Test")
|
|
||||||
public void benchmarkMantle(
|
|
||||||
@Param(description = "The world to bench", aliases = {"world"})
|
|
||||||
World world
|
|
||||||
) throws IOException, ClassNotFoundException {
|
|
||||||
Engine engine = IrisToolbelt.access(world).getEngine();
|
|
||||||
int maxHeight = engine.getTarget().getHeight();
|
|
||||||
File folder = new File(Bukkit.getWorldContainer(), world.getName());
|
|
||||||
int c = 0;
|
|
||||||
//MCAUtil.read()
|
|
||||||
|
|
||||||
File tectonicplates = new File(folder, "mantle");
|
|
||||||
for (File i : Objects.requireNonNull(tectonicplates.listFiles())) {
|
|
||||||
TectonicPlate.read(maxHeight, i, true);
|
|
||||||
c++;
|
|
||||||
Iris.info("Loaded count: " + c );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Decree(description = "Test")
|
@Decree(description = "Test")
|
||||||
public void packBenchmark(
|
public void packBenchmark(
|
||||||
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
|
@Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld")
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ package com.volmit.iris.core.commands;
|
|||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.core.IrisSettings;
|
import com.volmit.iris.core.IrisSettings;
|
||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
import com.volmit.iris.core.pregenerator.ChunkUpdater;
|
|
||||||
import com.volmit.iris.core.service.StudioSVC;
|
import com.volmit.iris.core.service.StudioSVC;
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
@@ -33,7 +32,6 @@ import com.volmit.iris.util.decree.annotations.Decree;
|
|||||||
import com.volmit.iris.util.decree.annotations.Param;
|
import com.volmit.iris.util.decree.annotations.Param;
|
||||||
import com.volmit.iris.util.decree.specialhandlers.NullablePlayerHandler;
|
import com.volmit.iris.util.decree.specialhandlers.NullablePlayerHandler;
|
||||||
import com.volmit.iris.util.format.C;
|
import com.volmit.iris.util.format.C;
|
||||||
import com.volmit.iris.util.format.Form;
|
|
||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.util.io.IO;
|
||||||
import com.volmit.iris.util.misc.ServerProperties;
|
import com.volmit.iris.util.misc.ServerProperties;
|
||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
@@ -56,6 +54,7 @@ import static org.bukkit.Bukkit.getServer;
|
|||||||
|
|
||||||
@Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command")
|
@Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command")
|
||||||
public class CommandIris implements DecreeExecutor {
|
public class CommandIris implements DecreeExecutor {
|
||||||
|
private CommandUpdater updater;
|
||||||
private CommandStudio studio;
|
private CommandStudio studio;
|
||||||
private CommandPregen pregen;
|
private CommandPregen pregen;
|
||||||
private CommandSettings settings;
|
private CommandSettings settings;
|
||||||
@@ -318,24 +317,6 @@ public class CommandIris implements DecreeExecutor {
|
|||||||
return dir.delete();
|
return dir.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Decree(description = "Updates all chunk in the specified world")
|
|
||||||
public void updater(
|
|
||||||
@Param(description = "World to update chunks at")
|
|
||||||
World world
|
|
||||||
) {
|
|
||||||
if (!IrisToolbelt.isIrisWorld(world)) {
|
|
||||||
sender().sendMessage(C.GOLD + "This is not an Iris world");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ChunkUpdater updater = new ChunkUpdater(world);
|
|
||||||
if (sender().isPlayer()) {
|
|
||||||
sender().sendMessage(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks()));
|
|
||||||
} else {
|
|
||||||
Iris.info(C.GREEN + "Updating " + world.getName() + " Total chunks: " + Form.f(updater.getChunks()));
|
|
||||||
}
|
|
||||||
updater.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Decree(description = "Set aura spins")
|
@Decree(description = "Set aura spins")
|
||||||
public void aura(
|
public void aura(
|
||||||
@Param(description = "The h color value", defaultValue = "-20")
|
@Param(description = "The h color value", defaultValue = "-20")
|
||||||
|
|||||||
@@ -182,17 +182,32 @@ public class CommandStudio implements DecreeExecutor {
|
|||||||
int rad = engine.getMantle().getRadius();
|
int rad = engine.getMantle().getRadius();
|
||||||
var mantle = engine.getMantle().getMantle();
|
var mantle = engine.getMantle().getMantle();
|
||||||
var chunkMap = new KMap<Position2, MantleChunk>();
|
var chunkMap = new KMap<Position2, MantleChunk>();
|
||||||
for (int i = -(radius + rad); i <= radius + rad; i++) {
|
ParallelQueueJob<Position2> prep = new ParallelQueueJob<>() {
|
||||||
for (int j = -(radius + rad); j <= radius + rad; j++) {
|
@Override
|
||||||
int xx = i + x, zz = j + z;
|
public void execute(Position2 pos) {
|
||||||
if (Math.abs(i) <= radius && Math.abs(j) <= radius) {
|
var cpos = pos.add(x, z);
|
||||||
mantle.deleteChunk(xx, zz);
|
if (Math.abs(pos.getX()) <= radius && Math.abs(pos.getZ()) <= radius) {
|
||||||
continue;
|
mantle.deleteChunk(cpos.getX(), cpos.getZ());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
chunkMap.put(new Position2(xx, zz), mantle.getChunk(xx, zz));
|
chunkMap.put(cpos, mantle.getChunk(cpos.getX(), cpos.getZ()));
|
||||||
mantle.deleteChunk(xx, zz);
|
mantle.deleteChunk(cpos.getX(), cpos.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Preparing Mantle";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (int xx = -(radius + rad); xx <= radius + rad; xx++) {
|
||||||
|
for (int zz = -(radius + rad); zz <= radius + rad; zz++) {
|
||||||
|
prep.queue(new Position2(xx, zz));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
CountDownLatch pLatch = new CountDownLatch(1);
|
||||||
|
prep.execute(sender(), pLatch::countDown);
|
||||||
|
pLatch.await();
|
||||||
|
|
||||||
|
|
||||||
ParallelQueueJob<Position2> job = new ParallelQueueJob<>() {
|
ParallelQueueJob<Position2> job = new ParallelQueueJob<>() {
|
||||||
@Override
|
@Override
|
||||||
@@ -210,14 +225,14 @@ public class CommandStudio implements DecreeExecutor {
|
|||||||
job.queue(new Position2(i + x, j + z));
|
job.queue(new Position2(i + x, j + z));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
job.execute(sender(), latch::countDown);
|
job.execute(sender(), latch::countDown);
|
||||||
latch.await();
|
latch.await();
|
||||||
|
|
||||||
int sections = mantle.getWorldHeight() >> 4;
|
int sections = mantle.getWorldHeight() >> 4;
|
||||||
chunkMap.forEach((pos, chunk) -> {
|
chunkMap.forEach((pos, chunk) -> {
|
||||||
var c = mantle.getChunk(pos.getX(), pos.getZ());
|
var c = mantle.getChunk(pos.getX(), pos.getZ()).use();
|
||||||
|
try {
|
||||||
for (MantleFlag flag : MantleFlag.values()) {
|
for (MantleFlag flag : MantleFlag.values()) {
|
||||||
c.flag(flag, chunk.isFlagged(flag));
|
c.flag(flag, chunk.isFlagged(flag));
|
||||||
}
|
}
|
||||||
@@ -228,6 +243,9 @@ public class CommandStudio implements DecreeExecutor {
|
|||||||
var s = c.getOrCreate(y);
|
var s = c.getOrCreate(y);
|
||||||
slice.getSliceMap().forEach(s::putSlice);
|
slice.getSliceMap().forEach(s::putSlice);
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
c.release();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
sender().sendMessage("Error while regenerating chunks");
|
sender().sendMessage("Error while regenerating chunks");
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package com.volmit.iris.core.commands;
|
package com.volmit.iris.core.commands;
|
||||||
|
|
||||||
|
import lombok.Synchronized;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
@@ -32,7 +33,8 @@ import com.volmit.iris.util.format.Form;
|
|||||||
|
|
||||||
@Decree(name = "updater", origin = DecreeOrigin.BOTH, description = "Iris World Updater")
|
@Decree(name = "updater", origin = DecreeOrigin.BOTH, description = "Iris World Updater")
|
||||||
public class CommandUpdater implements DecreeExecutor {
|
public class CommandUpdater implements DecreeExecutor {
|
||||||
private ChunkUpdater chunkUpdater;
|
private final Object lock = new Object();
|
||||||
|
private transient ChunkUpdater chunkUpdater;
|
||||||
|
|
||||||
@Decree(description = "Updates all chunk in the specified world")
|
@Decree(description = "Updates all chunk in the specified world")
|
||||||
public void start(
|
public void start(
|
||||||
@@ -43,6 +45,7 @@ public class CommandUpdater implements DecreeExecutor {
|
|||||||
sender().sendMessage(C.GOLD + "This is not an Iris world");
|
sender().sendMessage(C.GOLD + "This is not an Iris world");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
synchronized (lock) {
|
||||||
if (chunkUpdater != null) {
|
if (chunkUpdater != null) {
|
||||||
chunkUpdater.stop();
|
chunkUpdater.stop();
|
||||||
}
|
}
|
||||||
@@ -55,7 +58,9 @@ public class CommandUpdater implements DecreeExecutor {
|
|||||||
}
|
}
|
||||||
chunkUpdater.start();
|
chunkUpdater.start();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized("lock")
|
||||||
@Decree(description = "Pause the updater")
|
@Decree(description = "Pause the updater")
|
||||||
public void pause( ) {
|
public void pause( ) {
|
||||||
if (chunkUpdater == null) {
|
if (chunkUpdater == null) {
|
||||||
@@ -78,6 +83,7 @@ public class CommandUpdater implements DecreeExecutor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized("lock")
|
||||||
@Decree(description = "Stops the updater")
|
@Decree(description = "Stops the updater")
|
||||||
public void stop() {
|
public void stop() {
|
||||||
if (chunkUpdater == null) {
|
if (chunkUpdater == null) {
|
||||||
|
|||||||
@@ -7,11 +7,12 @@ import com.volmit.iris.engine.framework.Engine;
|
|||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
import com.volmit.iris.util.data.IrisCustomData;
|
import com.volmit.iris.util.data.IrisCustomData;
|
||||||
import com.volmit.iris.util.scheduling.J;
|
|
||||||
import dev.lone.itemsadder.api.CustomBlock;
|
import dev.lone.itemsadder.api.CustomBlock;
|
||||||
import dev.lone.itemsadder.api.CustomStack;
|
import dev.lone.itemsadder.api.CustomStack;
|
||||||
|
import dev.lone.itemsadder.api.Events.ItemsAdderLoadDataEvent;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@@ -31,12 +32,12 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
try {
|
|
||||||
updateNamespaces();
|
updateNamespaces();
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.warn("Failed to update ItemAdder namespaces: " + e.getMessage());
|
|
||||||
J.s(this::updateNamespaces, 20);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onLoadData(ItemsAdderLoadDataEvent event) {
|
||||||
|
updateNamespaces();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -68,33 +69,36 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
|
|||||||
public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) {
|
public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) {
|
||||||
return switch (dataType) {
|
return switch (dataType) {
|
||||||
case ENTITY -> List.of();
|
case ENTITY -> List.of();
|
||||||
case ITEM -> updateNamespaces(dataType, CustomStack.getNamespacedIdsInRegistry()
|
case ITEM -> CustomStack.getNamespacedIdsInRegistry()
|
||||||
.stream()
|
.stream()
|
||||||
.map(Identifier::fromString)
|
.map(Identifier::fromString)
|
||||||
.toList());
|
.toList();
|
||||||
case BLOCK -> updateNamespaces(dataType, CustomBlock.getNamespacedIdsInRegistry()
|
case BLOCK -> CustomBlock.getNamespacedIdsInRegistry()
|
||||||
.stream()
|
.stream()
|
||||||
.map(Identifier::fromString)
|
.map(Identifier::fromString)
|
||||||
.toList());
|
.toList();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateNamespaces() {
|
private void updateNamespaces() {
|
||||||
getTypes(DataType.ITEM);
|
try {
|
||||||
getTypes(DataType.BLOCK);
|
updateNamespaces(DataType.ITEM);
|
||||||
|
updateNamespaces(DataType.BLOCK);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.warn("Failed to update ItemAdder namespaces: " + e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<Identifier> updateNamespaces(DataType dataType, Collection<Identifier> ids) {
|
private void updateNamespaces(DataType dataType) {
|
||||||
var namespaces = ids.stream().map(Identifier::namespace).collect(Collectors.toSet());
|
var namespaces = getTypes(dataType).stream().map(Identifier::namespace).collect(Collectors.toSet());
|
||||||
var currentNamespaces = dataType == DataType.ITEM ? itemNamespaces : blockNamespaces;
|
var currentNamespaces = dataType == DataType.ITEM ? itemNamespaces : blockNamespaces;
|
||||||
currentNamespaces.removeIf(n -> !namespaces.contains(n));
|
currentNamespaces.removeIf(n -> !namespaces.contains(n));
|
||||||
currentNamespaces.addAll(namespaces);
|
currentNamespaces.addAll(namespaces);
|
||||||
return ids;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValidProvider(@NotNull Identifier id, DataType dataType) {
|
public boolean isValidProvider(@NotNull Identifier id, DataType dataType) {
|
||||||
if (dataType == DataType.ENTITY) return false;
|
if (dataType == DataType.ENTITY) return false;
|
||||||
return dataType == DataType.ITEM ? this.itemNamespaces.contains(id.namespace()) : this.blockNamespaces.contains(id.namespace());
|
return dataType == DataType.ITEM ? itemNamespaces.contains(id.namespace()) : blockNamespaces.contains(id.namespace());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,8 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.MissingResourceException;
|
import java.util.MissingResourceException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
public class NexoDataProvider extends ExternalDataProvider {
|
public class NexoDataProvider extends ExternalDataProvider {
|
||||||
private final AtomicBoolean failed = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
public NexoDataProvider() {
|
public NexoDataProvider() {
|
||||||
super("Nexo");
|
super("Nexo");
|
||||||
}
|
}
|
||||||
@@ -125,9 +122,4 @@ public class NexoDataProvider extends ExternalDataProvider {
|
|||||||
if (dataType == DataType.ENTITY) return false;
|
if (dataType == DataType.ENTITY) return false;
|
||||||
return "nexo".equalsIgnoreCase(id.namespace());
|
return "nexo".equalsIgnoreCase(id.namespace());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isReady() {
|
|
||||||
return super.isReady() && !failed.get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.volmit.iris.Iris;
|
|||||||
import com.volmit.iris.core.IrisSettings;
|
import com.volmit.iris.core.IrisSettings;
|
||||||
import com.volmit.iris.core.project.SchemaBuilder;
|
import com.volmit.iris.core.project.SchemaBuilder;
|
||||||
import com.volmit.iris.core.service.PreservationSVC;
|
import com.volmit.iris.core.service.PreservationSVC;
|
||||||
|
import com.volmit.iris.engine.data.cache.AtomicCache;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.framework.MeteredCache;
|
import com.volmit.iris.engine.framework.MeteredCache;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
@@ -45,9 +46,7 @@ import lombok.ToString;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -60,7 +59,7 @@ import java.util.zip.GZIPOutputStream;
|
|||||||
public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
||||||
public static final AtomicDouble tlt = new AtomicDouble(0);
|
public static final AtomicDouble tlt = new AtomicDouble(0);
|
||||||
private static final int CACHE_SIZE = 100000;
|
private static final int CACHE_SIZE = 100000;
|
||||||
protected final AtomicReference<KList<File>> folderCache;
|
protected final AtomicCache<KList<File>> folderCache;
|
||||||
protected KSet<String> firstAccess;
|
protected KSet<String> firstAccess;
|
||||||
protected File root;
|
protected File root;
|
||||||
protected String folderName;
|
protected String folderName;
|
||||||
@@ -76,7 +75,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
|||||||
public ResourceLoader(File root, IrisData manager, String folderName, String resourceTypeName, Class<? extends T> objectClass) {
|
public ResourceLoader(File root, IrisData manager, String folderName, String resourceTypeName, Class<? extends T> objectClass) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
firstAccess = new KSet<>();
|
firstAccess = new KSet<>();
|
||||||
folderCache = new AtomicReference<>();
|
folderCache = new AtomicCache<>();
|
||||||
sec = new ChronoLatch(5000);
|
sec = new ChronoLatch(5000);
|
||||||
loads = new AtomicInteger();
|
loads = new AtomicInteger();
|
||||||
this.objectClass = objectClass;
|
this.objectClass = objectClass;
|
||||||
@@ -361,8 +360,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public KList<File> getFolders() {
|
public KList<File> getFolders() {
|
||||||
synchronized (folderCache) {
|
return folderCache.aquire(() -> {
|
||||||
if (folderCache.get() == null) {
|
|
||||||
KList<File> fc = new KList<>();
|
KList<File> fc = new KList<>();
|
||||||
|
|
||||||
File[] files = root.listFiles();
|
File[] files = root.listFiles();
|
||||||
@@ -378,12 +376,8 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return fc;
|
||||||
folderCache.set(fc);
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return folderCache.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public KList<File> getFolders(String rc) {
|
public KList<File> getFolders(String rc) {
|
||||||
@@ -403,7 +397,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
|||||||
public void clearCache() {
|
public void clearCache() {
|
||||||
possibleKeys = null;
|
possibleKeys = null;
|
||||||
loadCache.invalidate();
|
loadCache.invalidate();
|
||||||
folderCache.set(null);
|
folderCache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public File fileFor(T b) {
|
public File fileFor(T b) {
|
||||||
@@ -429,7 +423,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void clearList() {
|
public void clearList() {
|
||||||
folderCache.set(null);
|
folderCache.reset();
|
||||||
possibleKeys = null;
|
possibleKeys = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -39,14 +39,14 @@ public class ChunkUpdater {
|
|||||||
private final AtomicInteger chunksUpdated = new AtomicInteger();
|
private final AtomicInteger chunksUpdated = new AtomicInteger();
|
||||||
private final AtomicBoolean serverEmpty = new AtomicBoolean(true);
|
private final AtomicBoolean serverEmpty = new AtomicBoolean(true);
|
||||||
private final AtomicLong lastCpsTime = new AtomicLong(M.ms());
|
private final AtomicLong lastCpsTime = new AtomicLong(M.ms());
|
||||||
private final int coreLimit = (int) Math.max(Runtime.getRuntime().availableProcessors() * IrisSettings.get().getUpdater().getThreadMultiplier(), 1);
|
private final int maxConcurrency = IrisSettings.get().getUpdater().getMaxConcurrency();
|
||||||
private final Semaphore semaphore = new Semaphore(256);
|
private final Semaphore semaphore = new Semaphore(maxConcurrency);
|
||||||
private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, 256, IrisSettings.get().getUpdater().emptyMsRange);
|
private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, maxConcurrency, IrisSettings.get().getUpdater().emptyMsRange);
|
||||||
private final AtomicLong startTime = new AtomicLong();
|
private final AtomicLong startTime = new AtomicLong();
|
||||||
private final Dimensions dimensions;
|
private final Dimensions dimensions;
|
||||||
private final PregenTask task;
|
private final PregenTask task;
|
||||||
private final ExecutorService executor = Executors.newFixedThreadPool(coreLimit);
|
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
|
||||||
private final ExecutorService chunkExecutor = Executors.newFixedThreadPool(coreLimit);
|
private final ExecutorService chunkExecutor = Executors.newVirtualThreadPerTaskExecutor();
|
||||||
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
|
||||||
private final CountDownLatch latch;
|
private final CountDownLatch latch;
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
@@ -138,10 +138,10 @@ public class ChunkUpdater {
|
|||||||
loadBalancer.close();
|
loadBalancer.close();
|
||||||
semaphore.acquire(256);
|
semaphore.acquire(256);
|
||||||
|
|
||||||
executor.shutdown();
|
|
||||||
executor.awaitTermination(5, TimeUnit.SECONDS);
|
|
||||||
chunkExecutor.shutdown();
|
chunkExecutor.shutdown();
|
||||||
chunkExecutor.awaitTermination(5, TimeUnit.SECONDS);
|
chunkExecutor.awaitTermination(5, TimeUnit.SECONDS);
|
||||||
|
executor.shutdown();
|
||||||
|
executor.awaitTermination(5, TimeUnit.SECONDS);
|
||||||
scheduler.shutdownNow();
|
scheduler.shutdownNow();
|
||||||
unloadAndSaveAllChunks();
|
unloadAndSaveAllChunks();
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
|
|||||||
@@ -69,8 +69,8 @@ public class ExternalDataSVC implements IrisService {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPluginEnable(PluginEnableEvent e) {
|
public void onPluginEnable(PluginEnableEvent e) {
|
||||||
if (activeProviders.stream().noneMatch(p -> p.getPlugin().equals(e.getPlugin()))) {
|
if (activeProviders.stream().noneMatch(p -> e.getPlugin().equals(p.getPlugin()))) {
|
||||||
providers.stream().filter(p -> p.isReady() && p.getPlugin().equals(e.getPlugin())).findFirst().ifPresent(edp -> {
|
providers.stream().filter(p -> p.isReady() && e.getPlugin().equals(p.getPlugin())).findFirst().ifPresent(edp -> {
|
||||||
activeProviders.add(edp);
|
activeProviders.add(edp);
|
||||||
edp.init();
|
edp.init();
|
||||||
Iris.instance.registerListener(edp);
|
Iris.instance.registerListener(edp);
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ public class IrisEngineSVC implements IrisService {
|
|||||||
private final class Registered {
|
private final class Registered {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final PlatformChunkGenerator access;
|
private final PlatformChunkGenerator access;
|
||||||
|
private final int offset = RNG.r.nextInt(1000);
|
||||||
private transient ScheduledFuture<?> trimmer;
|
private transient ScheduledFuture<?> trimmer;
|
||||||
private transient ScheduledFuture<?> unloader;
|
private transient ScheduledFuture<?> unloader;
|
||||||
private transient boolean closed;
|
private transient boolean closed;
|
||||||
@@ -193,7 +194,7 @@ public class IrisEngineSVC implements IrisService {
|
|||||||
Iris.error("EngineSVC: Failed to trim for " + name);
|
Iris.error("EngineSVC: Failed to trim for " + name);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
|
}, offset, 2000, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
|
if (unloader == null || unloader.isDone() || unloader.isCancelled()) {
|
||||||
@@ -204,7 +205,7 @@ public class IrisEngineSVC implements IrisService {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
long unloadStart = System.currentTimeMillis();
|
long unloadStart = System.currentTimeMillis();
|
||||||
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit());
|
int count = engine.getMantle().unloadTectonicPlate(IrisSettings.get().getPerformance().getEngineSVC().forceMulticoreWrite ? 0 : tectonicLimit());
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
||||||
}
|
}
|
||||||
@@ -213,7 +214,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();
|
||||||
}
|
}
|
||||||
}, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS);
|
}, offset + 1000, 2000, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -296,67 +296,63 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
try {
|
try {
|
||||||
Semaphore semaphore = new Semaphore(3);
|
Semaphore semaphore = new Semaphore(3);
|
||||||
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
|
chunk.raiseFlag(MantleFlag.ETCHED, () -> {
|
||||||
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> {
|
chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> {
|
||||||
mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> {
|
chunk.iterate(TileWrapper.class, (x, y, z, v) -> {
|
||||||
int betterY = y + getWorld().minHeight();
|
Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15);
|
||||||
if (!TileData.setTileState(c.getBlock(x, betterY, z), v.getData()))
|
if (!TileData.setTileState(block, v.getData()))
|
||||||
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", x, betterY, z, c.getBlock(x, betterY, z).getBlockData().getMaterial().getKey(), v.getData().getMaterial().name());
|
Iris.warn("Failed to set tile entity data at [%d %d %d | %s] for tile %s!", block.getX(), block.getY(), block.getZ(), block.getType().getKey(), v.getData().getMaterial().getKey());
|
||||||
});
|
});
|
||||||
})));
|
}, 0));
|
||||||
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> {
|
chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> {
|
||||||
mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> {
|
chunk.iterate(Identifier.class, (x, y, z, v) -> {
|
||||||
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
Iris.service(ExternalDataSVC.class).processUpdate(this, c.getBlock(x & 15, y + getWorld().minHeight(), z & 15), v);
|
||||||
});
|
});
|
||||||
})));
|
}, 0));
|
||||||
|
|
||||||
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> J.s(() -> {
|
chunk.raiseFlag(MantleFlag.UPDATE, run(semaphore, () -> {
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
KMap<Long, Integer> updates = new KMap<>();
|
int[][] grid = new int[16][16];
|
||||||
RNG r = new RNG(Cache.key(c.getX(), c.getZ()));
|
for (int x = 0; x < 16; x++) {
|
||||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> {
|
for (int z = 0; z < 16; z++) {
|
||||||
|
grid[x][z] = Integer.MIN_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RNG rng = new RNG(Cache.key(c.getX(), c.getZ()));
|
||||||
|
chunk.iterate(MatterCavern.class, (x, yf, z, v) -> {
|
||||||
int y = yf + getWorld().minHeight();
|
int y = yf + getWorld().minHeight();
|
||||||
if (!B.isFluid(c.getBlock(x & 15, y, z & 15).getBlockData())) {
|
x &= 15;
|
||||||
|
z &= 15;
|
||||||
|
Block block = c.getBlock(x, y, z);
|
||||||
|
if (!B.isFluid(block.getBlockData())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean u = false;
|
boolean u = B.isAir(block.getRelative(BlockFace.DOWN).getBlockData())
|
||||||
if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) {
|
|| B.isAir(block.getRelative(BlockFace.WEST).getBlockData())
|
||||||
u = true;
|
|| B.isAir(block.getRelative(BlockFace.EAST).getBlockData())
|
||||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) {
|
|| B.isAir(block.getRelative(BlockFace.SOUTH).getBlockData())
|
||||||
u = true;
|
|| B.isAir(block.getRelative(BlockFace.NORTH).getBlockData());
|
||||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.EAST).getBlockData())) {
|
|
||||||
u = true;
|
|
||||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.SOUTH).getBlockData())) {
|
|
||||||
u = true;
|
|
||||||
} else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.NORTH).getBlockData())) {
|
|
||||||
u = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (u) {
|
if (u) grid[x][z] = Math.max(grid[x][z], y);
|
||||||
updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> {
|
|
||||||
if (vv != null) {
|
|
||||||
return Math.max(vv, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
return y;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r));
|
for (int x = 0; x < 16; x++) {
|
||||||
mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> {
|
for (int z = 0; z < 16; z++) {
|
||||||
|
if (grid[x][z] == Integer.MIN_VALUE)
|
||||||
|
continue;
|
||||||
|
update(x, grid[x][z], z, c, rng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.iterate(MatterUpdate.class, (x, yf, z, v) -> {
|
||||||
int y = yf + getWorld().minHeight();
|
int y = yf + getWorld().minHeight();
|
||||||
if (v != null && v.isUpdate()) {
|
if (v != null && v.isUpdate()) {
|
||||||
int vx = x & 15;
|
update(x, y, z, c, rng);
|
||||||
int vz = z & 15;
|
|
||||||
update(x, y, z, c, new RNG(Cache.key(c.getX(), c.getZ())));
|
|
||||||
if (vx > 0 && vx < 15 && vz > 0 && vz < 15) {
|
|
||||||
updateLighting(x, y, z, c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class);
|
chunk.deleteSlices(MatterUpdate.class);
|
||||||
getMetrics().getUpdates().put(p.getMilliseconds());
|
getMetrics().getUpdates().put(p.getMilliseconds());
|
||||||
}, RNG.r.i(0, 20))));
|
}, RNG.r.i(1, 20))); //Why is there a random delay here?
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -367,33 +363,21 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Runnable run(Semaphore semaphore, Runnable runnable) {
|
private static Runnable run(Semaphore semaphore, Runnable runnable, int delay) {
|
||||||
return () -> {
|
return () -> {
|
||||||
if (!semaphore.tryAcquire())
|
if (!semaphore.tryAcquire())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
J.s(() -> {
|
||||||
try {
|
try {
|
||||||
runnable.run();
|
runnable.run();
|
||||||
} finally {
|
} finally {
|
||||||
semaphore.release();
|
semaphore.release();
|
||||||
}
|
}
|
||||||
|
}, delay);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@BlockCoordinates
|
|
||||||
default void updateLighting(int x, int y, int z, Chunk c) {
|
|
||||||
Block block = c.getBlock(x, y, z);
|
|
||||||
BlockData data = block.getBlockData();
|
|
||||||
|
|
||||||
if (B.isLit(data)) {
|
|
||||||
try {
|
|
||||||
block.setType(Material.AIR, false);
|
|
||||||
block.setBlockData(data, true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@BlockCoordinates
|
@BlockCoordinates
|
||||||
@Override
|
@Override
|
||||||
|
|
||||||
|
|||||||
@@ -26,31 +26,30 @@ import com.volmit.iris.engine.framework.Engine;
|
|||||||
import com.volmit.iris.engine.framework.EngineTarget;
|
import com.volmit.iris.engine.framework.EngineTarget;
|
||||||
import com.volmit.iris.engine.mantle.components.MantleJigsawComponent;
|
import com.volmit.iris.engine.mantle.components.MantleJigsawComponent;
|
||||||
import com.volmit.iris.engine.mantle.components.MantleObjectComponent;
|
import com.volmit.iris.engine.mantle.components.MantleObjectComponent;
|
||||||
import com.volmit.iris.engine.object.IObjectPlacer;
|
|
||||||
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.engine.object.TileData;
|
|
||||||
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.ChunkContext;
|
||||||
import com.volmit.iris.util.context.IrisContext;
|
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.data.IrisCustomData;
|
|
||||||
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;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
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.MantleFlag;
|
import com.volmit.iris.util.mantle.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.BurstExecutor;
|
|
||||||
import com.volmit.iris.util.parallel.MultiBurst;
|
import com.volmit.iris.util.parallel.MultiBurst;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
// TODO: MOVE PLACER OUT OF MATTER INTO ITS OWN THING
|
import static com.volmit.iris.util.parallel.StreamUtils.forEach;
|
||||||
public interface EngineMantle extends IObjectPlacer {
|
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();
|
||||||
@@ -87,12 +86,10 @@ public interface EngineMantle extends IObjectPlacer {
|
|||||||
return getHighest(x, z, getData(), ignoreFluid);
|
return getHighest(x, z, getData(), ignoreFluid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
default int getHighest(int x, int z, IrisData data) {
|
default int getHighest(int x, int z, IrisData data) {
|
||||||
return getHighest(x, z, data, false);
|
return getHighest(x, z, data, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
default int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
|
default int getHighest(int x, int z, IrisData data, boolean ignoreFluid) {
|
||||||
return ignoreFluid ? trueHeight(x, z) : Math.max(trueHeight(x, z), getEngine().getDimension().getFluidHeight());
|
return ignoreFluid ? trueHeight(x, z) : Math.max(trueHeight(x, z), getEngine().getDimension().getFluidHeight());
|
||||||
}
|
}
|
||||||
@@ -101,24 +98,12 @@ public interface EngineMantle extends IObjectPlacer {
|
|||||||
return getComplex().getRoundedHeighteightStream().get(x, z);
|
return getComplex().getRoundedHeighteightStream().get(x, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
default boolean isCarved(int x, int h, int z) {
|
default boolean isCarved(int x, int h, int z) {
|
||||||
return getMantle().get(x, h, z, MatterCavern.class) != null;
|
return getMantle().get(x, h, z, MatterCavern.class) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Deprecated(forRemoval = true)
|
||||||
default void set(int x, int y, int z, BlockData d) {
|
|
||||||
if (d instanceof IrisCustomData data) {
|
|
||||||
getMantle().set(x, y, z, data.getBase());
|
|
||||||
getMantle().set(x, y, z, data.getCustom());
|
|
||||||
} else getMantle().set(x, y, z, d == null ? AIR : d);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default void setTile(int x, int y, int z, TileData d) {
|
|
||||||
getMantle().set(x, y, z, new TileWrapper(d));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default BlockData get(int x, int y, int z) {
|
default BlockData get(int x, int y, int z) {
|
||||||
BlockData block = getMantle().get(x, y, z, BlockData.class);
|
BlockData block = getMantle().get(x, y, z, BlockData.class);
|
||||||
if (block == null)
|
if (block == null)
|
||||||
@@ -126,27 +111,18 @@ public interface EngineMantle extends IObjectPlacer {
|
|||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
default boolean isPreventingDecay() {
|
default boolean isPreventingDecay() {
|
||||||
return getEngine().getDimension().isPreventLeafDecay();
|
return getEngine().getDimension().isPreventLeafDecay();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
default boolean isSolid(int x, int y, int z) {
|
|
||||||
return B.isSolid(get(x, y, z));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
default boolean isUnderwater(int x, int z) {
|
default boolean isUnderwater(int x, int z) {
|
||||||
return getHighest(x, z, true) <= getFluidHeight();
|
return getHighest(x, z, true) <= getFluidHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
default int getFluidHeight() {
|
default int getFluidHeight() {
|
||||||
return getEngine().getDimension().getFluidHeight();
|
return getEngine().getDimension().getFluidHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
default boolean isDebugSmartBore() {
|
default boolean isDebugSmartBore() {
|
||||||
return getEngine().getDimension().isDebugSmartBore();
|
return getEngine().getDimension().isDebugSmartBore();
|
||||||
}
|
}
|
||||||
@@ -196,7 +172,7 @@ public interface EngineMantle extends IObjectPlacer {
|
|||||||
|
|
||||||
@ChunkCoordinates
|
@ChunkCoordinates
|
||||||
default void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
|
default void generateMatter(int x, int z, boolean multicore, ChunkContext context) {
|
||||||
if (!getEngine().getDimension().isUseMantle()) {
|
if (!getEngine().getDimension().isUseMantle() || getMantle().hasFlag(x, z, MantleFlag.PLANNED)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,30 +182,32 @@ public interface EngineMantle extends IObjectPlacer {
|
|||||||
var pair = iterator.next();
|
var pair = iterator.next();
|
||||||
int radius = pair.getB();
|
int radius = pair.getB();
|
||||||
boolean last = !iterator.hasNext();
|
boolean last = !iterator.hasNext();
|
||||||
BurstExecutor burst = burst().burst(radius * 2 + 1);
|
forEach(streamRadius(x, z, radius),
|
||||||
burst.setMulticore(multicore);
|
pos -> pair.getA()
|
||||||
|
.stream()
|
||||||
|
.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
|
||||||
|
);
|
||||||
|
|
||||||
for (int i = -radius; i <= radius; i++) {
|
if (!last) continue;
|
||||||
for (int j = -radius; j <= radius; j++) {
|
forEach(streamRadius(x, z, radius),
|
||||||
int xx = x + i;
|
p -> writer.acquireChunk(x, z).flag(MantleFlag.PLANNED, true),
|
||||||
int zz = z + j;
|
multicore ? burst() : null
|
||||||
MantleChunk mc = getMantle().getChunk(xx, zz);
|
);
|
||||||
|
|
||||||
burst.queue(() -> {
|
|
||||||
IrisContext.touch(getEngine().getContext());
|
|
||||||
pair.getA().forEach(k -> generateMantleComponent(writer, xx, zz, k, mc, context));
|
|
||||||
if (last) mc.flag(MantleFlag.PLANNED, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
burst.complete();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) {
|
default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) {
|
||||||
mc.raiseFlag(c.getFlag(), () -> {
|
mc.raiseFlag(MantleFlag.PLANNED, c.getFlag(), () -> {
|
||||||
if (c.isEnabled()) c.generateLayer(writer, x, z, context);
|
if (c.isEnabled()) c.generateLayer(writer, x, z, context);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -240,7 +218,12 @@ public interface EngineMantle extends IObjectPlacer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
getMantle().iterateChunk(x, z, t, blocks::set);
|
var chunk = getMantle().getChunk(x, z).use();
|
||||||
|
try {
|
||||||
|
chunk.iterate(t, blocks::set);
|
||||||
|
} finally {
|
||||||
|
chunk.release();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@BlockCoordinates
|
@BlockCoordinates
|
||||||
|
|||||||
@@ -29,12 +29,16 @@ import com.volmit.iris.engine.object.IrisPosition;
|
|||||||
import com.volmit.iris.engine.object.TileData;
|
import com.volmit.iris.engine.object.TileData;
|
||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.collection.KSet;
|
import com.volmit.iris.util.collection.KSet;
|
||||||
|
import com.volmit.iris.util.data.B;
|
||||||
import com.volmit.iris.util.data.IrisCustomData;
|
import com.volmit.iris.util.data.IrisCustomData;
|
||||||
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
import com.volmit.iris.util.function.Function3;
|
import com.volmit.iris.util.function.Function3;
|
||||||
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.math.RNG;
|
import com.volmit.iris.util.math.RNG;
|
||||||
import com.volmit.iris.util.matter.Matter;
|
import com.volmit.iris.util.matter.Matter;
|
||||||
|
import com.volmit.iris.util.matter.MatterCavern;
|
||||||
|
import com.volmit.iris.util.matter.TileWrapper;
|
||||||
import com.volmit.iris.util.noise.CNG;
|
import com.volmit.iris.util.noise.CNG;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.bukkit.block.data.BlockData;
|
import org.bukkit.block.data.BlockData;
|
||||||
@@ -44,6 +48,8 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static com.volmit.iris.engine.mantle.EngineMantle.AIR;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
||||||
private final EngineMantle engineMantle;
|
private final EngineMantle engineMantle;
|
||||||
@@ -144,20 +150,41 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cx >= this.x - radius && cx <= this.x + radius
|
MantleChunk chunk = acquireChunk(cx, cz);
|
||||||
&& cz >= this.z - radius && cz <= this.z + radius) {
|
if (chunk == null) return;
|
||||||
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
|
|
||||||
|
|
||||||
if (chunk == null) {
|
|
||||||
Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Matter matter = chunk.getOrCreate(y >> 4);
|
Matter matter = chunk.getOrCreate(y >> 4);
|
||||||
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t);
|
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t);
|
||||||
} else {
|
|
||||||
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> T getData(int x, int y, int z, Class<T> type) {
|
||||||
|
int cx = x >> 4;
|
||||||
|
int cz = z >> 4;
|
||||||
|
|
||||||
|
if (y < 0 || y >= mantle.getWorldHeight()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MantleChunk chunk = acquireChunk(cx, cz);
|
||||||
|
if (chunk == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk.getOrCreate(y >> 4)
|
||||||
|
.<T>slice(type)
|
||||||
|
.get(x & 15, y & 15, z & 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ChunkCoordinates
|
||||||
|
public MantleChunk acquireChunk(int cx, int cz) {
|
||||||
|
if (cx < this.x - radius || cx > this.x + radius
|
||||||
|
|| cz < this.z - radius || cz > this.z + radius) {
|
||||||
|
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use());
|
||||||
|
if (chunk == null) Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)");
|
||||||
|
return chunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -180,7 +207,10 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockData get(int x, int y, int z) {
|
public BlockData get(int x, int y, int z) {
|
||||||
return getEngineMantle().get(x, y, z);
|
BlockData block = getData(x, y, z, BlockData.class);
|
||||||
|
if (block == null)
|
||||||
|
return AIR;
|
||||||
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -190,12 +220,12 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isCarved(int x, int y, int z) {
|
public boolean isCarved(int x, int y, int z) {
|
||||||
return getEngineMantle().isCarved(x, y, z);
|
return getData(x, y, z, MatterCavern.class) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSolid(int x, int y, int z) {
|
public boolean isSolid(int x, int y, int z) {
|
||||||
return getEngineMantle().isSolid(x, y, z);
|
return B.isSolid(get(x, y, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -215,7 +245,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTile(int xx, int yy, int zz, TileData tile) {
|
public void setTile(int xx, int yy, int zz, TileData tile) {
|
||||||
getEngineMantle().setTile(xx, yy, zz, tile);
|
setData(xx, yy, zz, new TileWrapper(tile));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
|||||||
private final CNG cng;
|
private final CNG cng;
|
||||||
|
|
||||||
public MantleJigsawComponent(EngineMantle engineMantle) {
|
public MantleJigsawComponent(EngineMantle engineMantle) {
|
||||||
super(engineMantle, MantleFlag.JIGSAW, 1);
|
super(engineMantle, MantleFlag.JIGSAW, 2);
|
||||||
cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
|
cng = NoiseStyle.STATIC.create(new RNG(jigsaw()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,6 +180,10 @@ public class MantleJigsawComponent extends IrisMantleComponent {
|
|||||||
var dimension = getDimension();
|
var dimension = getDimension();
|
||||||
|
|
||||||
KSet<String> structures = new KSet<>();
|
KSet<String> structures = new KSet<>();
|
||||||
|
if (dimension.getStronghold() != null) {
|
||||||
|
structures.add(dimension.getStronghold());
|
||||||
|
}
|
||||||
|
|
||||||
for (var placement : dimension.getJigsawStructures()) {
|
for (var placement : dimension.getJigsawStructures()) {
|
||||||
structures.add(placement.getStructure());
|
structures.add(placement.getStructure());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import com.volmit.iris.util.collection.KList;
|
|||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.context.ChunkContext;
|
import com.volmit.iris.util.context.ChunkContext;
|
||||||
import com.volmit.iris.util.data.B;
|
import com.volmit.iris.util.data.B;
|
||||||
|
import com.volmit.iris.util.documentation.ChunkCoordinates;
|
||||||
import com.volmit.iris.util.function.Consumer4;
|
import com.volmit.iris.util.function.Consumer4;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
import com.volmit.iris.util.mantle.Mantle;
|
import com.volmit.iris.util.mantle.Mantle;
|
||||||
@@ -53,10 +54,11 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ChunkCoordinates
|
||||||
public void onModify(int x, int z, Hunk<BlockData> output, boolean multicore, ChunkContext context) {
|
public void onModify(int x, int z, Hunk<BlockData> output, boolean multicore, ChunkContext context) {
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
Mantle mantle = getEngine().getMantle().getMantle();
|
Mantle mantle = getEngine().getMantle().getMantle();
|
||||||
MantleChunk mc = getEngine().getMantle().getMantle().getChunk(x, z).use();
|
MantleChunk mc = mantle.getChunk(x, z).use();
|
||||||
KMap<Long, KList<Integer>> positions = new KMap<>();
|
KMap<Long, KList<Integer>> positions = new KMap<>();
|
||||||
KMap<IrisPosition, MatterCavern> walls = new KMap<>();
|
KMap<IrisPosition, MatterCavern> walls = new KMap<>();
|
||||||
Consumer4<Integer, Integer, Integer, MatterCavern> iterator = (xx, yy, zz, c) -> {
|
Consumer4<Integer, Integer, Integer, MatterCavern> iterator = (xx, yy, zz, c) -> {
|
||||||
@@ -81,19 +83,19 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
|
|||||||
|
|
||||||
//todo: Fix chunk decoration not working on chunk's border
|
//todo: Fix chunk decoration not working on chunk's border
|
||||||
|
|
||||||
if (rz < 15 && mantle.get(xx, yy, zz + 1, MatterCavern.class) == null) {
|
if (rz < 15 && mc.get(xx, yy, zz + 1, MatterCavern.class) == null) {
|
||||||
walls.put(new IrisPosition(rx, yy, rz + 1), c);
|
walls.put(new IrisPosition(rx, yy, rz + 1), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rx < 15 && mantle.get(xx + 1, yy, zz, MatterCavern.class) == null) {
|
if (rx < 15 && mc.get(xx + 1, yy, zz, MatterCavern.class) == null) {
|
||||||
walls.put(new IrisPosition(rx + 1, yy, rz), c);
|
walls.put(new IrisPosition(rx + 1, yy, rz), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rz > 0 && mantle.get(xx, yy, zz - 1, MatterCavern.class) == null) {
|
if (rz > 0 && mc.get(xx, yy, zz - 1, MatterCavern.class) == null) {
|
||||||
walls.put(new IrisPosition(rx, yy, rz - 1), c);
|
walls.put(new IrisPosition(rx, yy, rz - 1), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rx > 0 && mantle.get(xx - 1, yy, zz, MatterCavern.class) == null) {
|
if (rx > 0 && mc.get(xx - 1, yy, zz, MatterCavern.class) == null) {
|
||||||
walls.put(new IrisPosition(rx - 1, yy, rz), c);
|
walls.put(new IrisPosition(rx - 1, yy, rz), c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -514,9 +514,9 @@ public class IrisObject extends IrisRegistrant {
|
|||||||
max.setZ(Math.max(max.getZ(), i.getZ()));
|
max.setZ(Math.max(max.getZ(), i.getZ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
w = max.getBlockX() - min.getBlockX() + (min.getBlockX() <= 0 && max.getBlockX() >= 0 && min.getBlockX() != max.getBlockX() ? 1 : 0);
|
w = max.getBlockX() - min.getBlockX() + 1;
|
||||||
h = max.getBlockY() - min.getBlockY() + (min.getBlockY() <= 0 && max.getBlockY() >= 0 && min.getBlockY() != max.getBlockY() ? 1 : 0);
|
h = max.getBlockY() - min.getBlockY() + 1;
|
||||||
d = max.getBlockZ() - min.getBlockZ() + (min.getBlockZ() <= 0 && max.getBlockZ() >= 0 && min.getBlockZ() != max.getBlockZ() ? 1 : 0);
|
d = max.getBlockZ() - min.getBlockZ() + 1;
|
||||||
center = new BlockVector(w / 2, h / 2, d / 2);
|
center = new BlockVector(w / 2, h / 2, d / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
package com.volmit.iris.engine.platform;
|
package com.volmit.iris.engine.platform;
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
import com.volmit.iris.core.IrisWorlds;
|
import com.volmit.iris.core.IrisWorlds;
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.nms.INMS;
|
import com.volmit.iris.core.nms.INMS;
|
||||||
@@ -34,7 +35,6 @@ import com.volmit.iris.engine.object.StudioMode;
|
|||||||
import com.volmit.iris.engine.platform.studio.StudioGenerator;
|
import com.volmit.iris.engine.platform.studio.StudioGenerator;
|
||||||
import com.volmit.iris.util.collection.KList;
|
import com.volmit.iris.util.collection.KList;
|
||||||
import com.volmit.iris.util.data.IrisBiomeStorage;
|
import com.volmit.iris.util.data.IrisBiomeStorage;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
|
||||||
import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder;
|
import com.volmit.iris.util.hunk.view.BiomeGridHunkHolder;
|
||||||
import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder;
|
import com.volmit.iris.util.hunk.view.ChunkDataHunkHolder;
|
||||||
import com.volmit.iris.util.io.ReactiveFolder;
|
import com.volmit.iris.util.io.ReactiveFolder;
|
||||||
@@ -46,8 +46,6 @@ import lombok.Data;
|
|||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.bukkit.*;
|
import org.bukkit.*;
|
||||||
import org.bukkit.block.Biome;
|
|
||||||
import org.bukkit.block.data.BlockData;
|
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Entity;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
@@ -206,7 +204,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
IrisBiomeStorage st = new IrisBiomeStorage();
|
IrisBiomeStorage st = new IrisBiomeStorage();
|
||||||
TerrainChunk tc = TerrainChunk.createUnsafe(world, st);
|
TerrainChunk tc = TerrainChunk.createUnsafe(world, st);
|
||||||
this.world.bind(world);
|
this.world.bind(world);
|
||||||
getEngine().generate(x << 4, z << 4, tc, false);
|
getEngine().generate(x << 4, z << 4, tc, IrisSettings.get().getGenerator().useMulticore);
|
||||||
|
|
||||||
Chunk c = PaperLib.getChunkAtAsync(world, x, z)
|
Chunk c = PaperLib.getChunkAtAsync(world, x, z)
|
||||||
.thenApply(d -> {
|
.thenApply(d -> {
|
||||||
@@ -367,7 +365,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun
|
|||||||
} else {
|
} else {
|
||||||
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc);
|
ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc);
|
||||||
BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight());
|
BiomeGridHunkHolder biomes = new BiomeGridHunkHolder(tc, tc.getMinHeight(), tc.getMaxHeight());
|
||||||
getEngine().generate(x << 4, z << 4, blocks, biomes, false);
|
getEngine().generate(x << 4, z << 4, blocks, biomes, IrisSettings.get().getGenerator().useMulticore);
|
||||||
blocks.apply();
|
blocks.apply();
|
||||||
biomes.apply();
|
biomes.apply();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import lombok.Data;
|
|||||||
@Data
|
@Data
|
||||||
public class IrisContext {
|
public class IrisContext {
|
||||||
private static final KMap<Thread, IrisContext> context = new KMap<>();
|
private static final KMap<Thread, IrisContext> context = new KMap<>();
|
||||||
private static ChronoLatch cl = new ChronoLatch(60000);
|
private static final ChronoLatch cl = new ChronoLatch(60000);
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private ChunkContext chunkContext;
|
private ChunkContext chunkContext;
|
||||||
|
|
||||||
@@ -53,9 +53,10 @@ public class IrisContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void touch(IrisContext c) {
|
public static void touch(IrisContext c) {
|
||||||
synchronized (context) {
|
|
||||||
context.put(Thread.currentThread(), c);
|
context.put(Thread.currentThread(), c);
|
||||||
|
|
||||||
|
if (!cl.couldFlip()) return;
|
||||||
|
synchronized (cl) {
|
||||||
if (cl.flip()) {
|
if (cl.flip()) {
|
||||||
dereference();
|
dereference();
|
||||||
}
|
}
|
||||||
@@ -63,18 +64,16 @@ public class IrisContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void dereference() {
|
public static void dereference() {
|
||||||
synchronized (context) {
|
|
||||||
for (Thread i : context.k()) {
|
for (Thread i : context.k()) {
|
||||||
if (!i.isAlive() || context.get(i).engine.isClosed()) {
|
if (!i.isAlive() || context.get(i).engine.isClosed()) {
|
||||||
if (context.get(i).engine.isClosed()) {
|
if (context.get(i).engine.isClosed()) {
|
||||||
Iris.debug("Dereferenced Context<Engine> " + i.getName() + " " + i.getId());
|
Iris.debug("Dereferenced Context<Engine> " + i.getName() + " " + i.threadId());
|
||||||
}
|
}
|
||||||
|
|
||||||
context.remove(i);
|
context.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void touch() {
|
public void touch() {
|
||||||
IrisContext.touch(this);
|
IrisContext.touch(this);
|
||||||
|
|||||||
@@ -610,8 +610,7 @@ public class B {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isUpdatable(BlockData mat) {
|
public static boolean isUpdatable(BlockData mat) {
|
||||||
return isLit(mat)
|
return isStorage(mat)
|
||||||
|| isStorage(mat)
|
|
||||||
|| (mat instanceof PointedDripstone
|
|| (mat instanceof PointedDripstone
|
||||||
&& ((PointedDripstone) mat).getThickness().equals(PointedDripstone.Thickness.TIP));
|
&& ((PointedDripstone) mat).getThickness().equals(PointedDripstone.Thickness.TIP));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,12 @@
|
|||||||
package com.volmit.iris.util.hunk.bits;
|
package com.volmit.iris.util.hunk.bits;
|
||||||
|
|
||||||
import com.volmit.iris.util.data.Varint;
|
import com.volmit.iris.util.data.Varint;
|
||||||
|
import lombok.Getter;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicLongArray;
|
import java.util.concurrent.atomic.AtomicLongArray;
|
||||||
import java.util.function.IntConsumer;
|
import java.util.function.IntConsumer;
|
||||||
|
|
||||||
@@ -52,8 +52,10 @@ public class DataBits {
|
|||||||
0, 5};
|
0, 5};
|
||||||
|
|
||||||
private final AtomicLongArray data;
|
private final AtomicLongArray data;
|
||||||
|
@Getter
|
||||||
private final int bits;
|
private final int bits;
|
||||||
private final long mask;
|
private final long mask;
|
||||||
|
@Getter
|
||||||
private final int size;
|
private final int size;
|
||||||
private final int valuesPerLong;
|
private final int valuesPerLong;
|
||||||
private final int divideMul;
|
private final int divideMul;
|
||||||
@@ -149,18 +151,9 @@ public class DataBits {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getBits() {
|
|
||||||
return bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataBits setBits(int newBits) {
|
public DataBits setBits(int newBits) {
|
||||||
if (bits != newBits) {
|
if (bits != newBits) {
|
||||||
DataBits newData = new DataBits(newBits, size);
|
DataBits newData = new DataBits(newBits, size);
|
||||||
AtomicInteger c = new AtomicInteger(0);
|
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
newData.set(i, get(i));
|
newData.set(i, get(i));
|
||||||
|
|||||||
@@ -19,78 +19,32 @@
|
|||||||
package com.volmit.iris.util.hunk.bits;
|
package com.volmit.iris.util.hunk.bits;
|
||||||
|
|
||||||
import com.volmit.iris.util.data.Varint;
|
import com.volmit.iris.util.data.Varint;
|
||||||
|
import lombok.Synchronized;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
public class DataContainer<T> {
|
public class DataContainer<T> {
|
||||||
protected static final int INITIAL_BITS = 3;
|
protected static final int INITIAL_BITS = 3;
|
||||||
protected static final int LINEAR_BITS_LIMIT = 4;
|
protected static final int LINEAR_BITS_LIMIT = 4;
|
||||||
protected static final int LINEAR_INITIAL_LENGTH = (int) Math.pow(2, LINEAR_BITS_LIMIT) + 1;
|
protected static final int LINEAR_INITIAL_LENGTH = (int) Math.pow(2, LINEAR_BITS_LIMIT) + 1;
|
||||||
protected static final int[] BIT = computeBitLimits();
|
protected static final int[] BIT = computeBitLimits();
|
||||||
private final AtomicReference<Palette<T>> palette;
|
private volatile Palette<T> palette;
|
||||||
private final AtomicReference<DataBits> data;
|
private volatile DataBits data;
|
||||||
private final AtomicInteger bits;
|
|
||||||
private final int length;
|
private final int length;
|
||||||
private final Writable<T> writer;
|
private final Writable<T> writer;
|
||||||
|
|
||||||
public DataContainer(Writable<T> writer, int length) {
|
public DataContainer(Writable<T> writer, int length) {
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.bits = new AtomicInteger(INITIAL_BITS);
|
this.data = new DataBits(INITIAL_BITS, length);
|
||||||
this.data = new AtomicReference<>(new DataBits(INITIAL_BITS, length));
|
this.palette = newPalette(INITIAL_BITS);
|
||||||
this.palette = new AtomicReference<>(newPalette(INITIAL_BITS));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataContainer(DataInputStream din, Writable<T> writer) throws IOException {
|
public DataContainer(DataInputStream din, Writable<T> writer) throws IOException {
|
||||||
this.writer = writer;
|
this.writer = writer;
|
||||||
this.length = Varint.readUnsignedVarInt(din);
|
this.length = Varint.readUnsignedVarInt(din);
|
||||||
this.palette = new AtomicReference<>(newPalette(din));
|
this.palette = newPalette(din);
|
||||||
this.data = new AtomicReference<>(new DataBits(palette.get().bits(), length, din));
|
this.data = new DataBits(palette.bits(), length, din);
|
||||||
this.bits = new AtomicInteger(palette.get().bits());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String readBitString(DataInputStream din) throws IOException {
|
|
||||||
DataContainer<Character> c = new DataContainer<>(din, new Writable<Character>() {
|
|
||||||
@Override
|
|
||||||
public Character readNodeData(DataInputStream din) throws IOException {
|
|
||||||
return din.readChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeNodeData(DataOutputStream dos, Character character) throws IOException {
|
|
||||||
dos.writeChar(character);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
for (int i = c.size() - 1; i >= 0; i--) {
|
|
||||||
sb.setCharAt(i, c.get(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void writeBitString(String s, DataOutputStream dos) throws IOException {
|
|
||||||
DataContainer<Character> c = new DataContainer<>(new Writable<Character>() {
|
|
||||||
@Override
|
|
||||||
public Character readNodeData(DataInputStream din) throws IOException {
|
|
||||||
return din.readChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeNodeData(DataOutputStream dos, Character character) throws IOException {
|
|
||||||
dos.writeChar(character);
|
|
||||||
}
|
|
||||||
}, s.length());
|
|
||||||
|
|
||||||
for (int i = 0; i < s.length(); i++) {
|
|
||||||
c.set(i, s.charAt(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
c.writeDos(dos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] computeBitLimits() {
|
private static int[] computeBitLimits() {
|
||||||
@@ -117,17 +71,9 @@ public class DataContainer<T> {
|
|||||||
return DataContainer.BIT.length - 1;
|
return DataContainer.BIT.length - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataBits getData() {
|
|
||||||
return data.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Palette<T> getPalette() {
|
|
||||||
return palette.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DataContainer <" + length + " x " + bits + " bits> -> Palette<" + palette.get().getClass().getSimpleName().replaceAll("\\QPalette\\E", "") + ">: " + palette.get().size() +
|
return "DataContainer <" + length + " x " + data.getBits() + " bits> -> Palette<" + palette.getClass().getSimpleName().replaceAll("\\QPalette\\E", "") + ">: " + palette.size() +
|
||||||
" " + data.get().toString() + " PalBit: " + palette.get().bits();
|
" " + data.toString() + " PalBit: " + palette.bits();
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] write() throws IOException {
|
public byte[] write() throws IOException {
|
||||||
@@ -140,11 +86,12 @@ public class DataContainer<T> {
|
|||||||
writeDos(new DataOutputStream(out));
|
writeDos(new DataOutputStream(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
public void writeDos(DataOutputStream dos) throws IOException {
|
public void writeDos(DataOutputStream dos) throws IOException {
|
||||||
Varint.writeUnsignedVarInt(length, dos);
|
Varint.writeUnsignedVarInt(length, dos);
|
||||||
Varint.writeUnsignedVarInt(palette.get().size(), dos);
|
Varint.writeUnsignedVarInt(palette.size(), dos);
|
||||||
palette.get().iterateIO((data, __) -> writer.writeNodeData(dos, data));
|
palette.iterateIO((data, __) -> writer.writeNodeData(dos, data));
|
||||||
data.get().write(dos);
|
data.write(dos);
|
||||||
dos.flush();
|
dos.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,55 +110,44 @@ public class DataContainer<T> {
|
|||||||
return new HashPalette<>();
|
return new HashPalette<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ensurePaletted(T t) {
|
@Synchronized
|
||||||
if (palette.get().id(t) == -1) {
|
|
||||||
expandOne();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void set(int position, T t) {
|
public void set(int position, T t) {
|
||||||
synchronized (this) {
|
int id = palette.id(t);
|
||||||
int id = palette.get().id(t);
|
|
||||||
|
|
||||||
if (id == -1) {
|
if (id == -1) {
|
||||||
expandOne();
|
id = palette.add(t);
|
||||||
id = palette.get().add(t);
|
updateBits();
|
||||||
}
|
}
|
||||||
|
|
||||||
data.get().set(position, id);
|
data.set(position, id);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expandOne() {
|
@Synchronized
|
||||||
if (palette.get().size() + 1 >= BIT[bits.get()]) {
|
@SuppressWarnings("NonAtomicOperationOnVolatileField")
|
||||||
setBits(bits.get() + 1);
|
private void updateBits() {
|
||||||
}
|
if (palette.bits() == data.getBits())
|
||||||
|
return;
|
||||||
|
|
||||||
|
int bits = palette.bits();
|
||||||
|
if (data.getBits() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) {
|
||||||
|
palette = newPalette(bits).from(palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data = data.setBits(bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
public T get(int position) {
|
public T get(int position) {
|
||||||
synchronized (this) {
|
int id = data.get(position);
|
||||||
int id = data.get().get(position) + 1;
|
|
||||||
|
|
||||||
if (id <= 0) {
|
if (id <= 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return palette.get().get(id - 1);
|
return palette.get(id);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBits(int bits) {
|
|
||||||
if (this.bits.get() != bits) {
|
|
||||||
if (this.bits.get() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) {
|
|
||||||
palette.set(newPalette(bits).from(palette.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bits.set(bits);
|
|
||||||
data.set(data.get().setBits(bits));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return getData().getSize();
|
return data.getSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,26 +21,25 @@ package com.volmit.iris.util.hunk.bits;
|
|||||||
import com.volmit.iris.util.collection.KMap;
|
import com.volmit.iris.util.collection.KMap;
|
||||||
import com.volmit.iris.util.function.Consumer2;
|
import com.volmit.iris.util.function.Consumer2;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
|
||||||
public class HashPalette<T> implements Palette<T> {
|
public class HashPalette<T> implements Palette<T> {
|
||||||
private final ReentrantLock lock = new ReentrantLock();
|
|
||||||
private final LinkedHashMap<T, Integer> palette;
|
private final LinkedHashMap<T, Integer> palette;
|
||||||
private final KMap<Integer, T> lookup;
|
private final KMap<Integer, T> lookup;
|
||||||
private final AtomicInteger size;
|
private final AtomicInteger size;
|
||||||
|
|
||||||
public HashPalette() {
|
public HashPalette() {
|
||||||
this.size = new AtomicInteger(0);
|
this.size = new AtomicInteger(1);
|
||||||
this.palette = new LinkedHashMap<>();
|
this.palette = new LinkedHashMap<>();
|
||||||
this.lookup = new KMap<>();
|
this.lookup = new KMap<>();
|
||||||
add(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T get(int id) {
|
public T get(int id) {
|
||||||
if (id < 0 || id >= size.get()) {
|
if (id <= 0 || id >= size.get()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,17 +48,16 @@ public class HashPalette<T> implements Palette<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int add(T t) {
|
public int add(T t) {
|
||||||
lock.lock();
|
if (t == null) {
|
||||||
try {
|
return 0;
|
||||||
int index = size.getAndIncrement();
|
|
||||||
palette.put(t, index);
|
|
||||||
|
|
||||||
if (t != null) {
|
|
||||||
lookup.put(index, t);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized (palette) {
|
||||||
|
return palette.computeIfAbsent(t, $ -> {
|
||||||
|
int index = size.getAndIncrement();
|
||||||
|
lookup.put(index, t);
|
||||||
return index;
|
return index;
|
||||||
} finally {
|
});
|
||||||
lock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,17 +78,33 @@ public class HashPalette<T> implements Palette<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void iterate(Consumer2<T, Integer> c) {
|
public void iterate(Consumer2<T, Integer> c) {
|
||||||
lock.lock();
|
synchronized (palette) {
|
||||||
try {
|
for (int i = 1; i < size.get(); i++) {
|
||||||
for (T i : palette.keySet()) {
|
c.accept(lookup.get(i), i);
|
||||||
if (i == null) {
|
}
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.accept(i, id(i));
|
@Override
|
||||||
|
public Palette<T> from(Palette<T> oldPalette) {
|
||||||
|
oldPalette.iterate((t, i) -> {
|
||||||
|
if (t == null) throw new NullPointerException("Null palette entries are not allowed!");
|
||||||
|
lookup.put(i, t);
|
||||||
|
palette.put(t, i);
|
||||||
|
});
|
||||||
|
size.set(oldPalette.size() + 1);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
lock.unlock();
|
@Override
|
||||||
|
public Palette<T> from(int size, Writable<T> writable, DataInputStream in) throws IOException {
|
||||||
|
for (int i = 1; i <= size; i++) {
|
||||||
|
T t = writable.readNodeData(in);
|
||||||
|
if (t == null) throw new NullPointerException("Null palette entries are not allowed!");
|
||||||
|
lookup.put(i, t);
|
||||||
|
palette.put(t, i);
|
||||||
}
|
}
|
||||||
|
this.size.set(size + 1);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,19 +19,19 @@
|
|||||||
package com.volmit.iris.util.hunk.bits;
|
package com.volmit.iris.util.hunk.bits;
|
||||||
|
|
||||||
import com.volmit.iris.util.function.Consumer2;
|
import com.volmit.iris.util.function.Consumer2;
|
||||||
|
import lombok.Synchronized;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||||
|
|
||||||
public class LinearPalette<T> implements Palette<T> {
|
public class LinearPalette<T> implements Palette<T> {
|
||||||
private final AtomicReference<AtomicReferenceArray<T>> palette;
|
private volatile AtomicReferenceArray<T> palette;
|
||||||
private final AtomicInteger size;
|
private final AtomicInteger size;
|
||||||
|
|
||||||
public LinearPalette(int initialSize) {
|
public LinearPalette(int initialSize) {
|
||||||
this.size = new AtomicInteger(0);
|
this.size = new AtomicInteger(1);
|
||||||
this.palette = new AtomicReference<>(new AtomicReferenceArray<>(initialSize));
|
this.palette = new AtomicReferenceArray<>(initialSize);
|
||||||
palette.get().set(size.getAndIncrement(), null);
|
palette.set(0, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -40,26 +40,29 @@ public class LinearPalette<T> implements Palette<T> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return palette.get().get(id);
|
return palette.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int add(T t) {
|
public int add(T t) {
|
||||||
|
if (t == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int index = size.getAndIncrement();
|
int index = size.getAndIncrement();
|
||||||
grow(index + 1);
|
grow(index + 1);
|
||||||
palette.get().set(index, t);
|
palette.set(index, t);
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void grow(int newLength) {
|
private synchronized void grow(int newLength) {
|
||||||
if (newLength > palette.get().length()) {
|
if (newLength > palette.length()) {
|
||||||
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(newLength + size.get());
|
AtomicReferenceArray<T> a = new AtomicReferenceArray<>(newLength);
|
||||||
|
|
||||||
for (int i = 0; i < palette.get().length(); i++) {
|
for (int i = 0; i < palette.length(); i++) {
|
||||||
a.set(i, palette.get().get(i));
|
a.set(i, palette.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
palette.set(a);
|
palette = a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,8 +72,8 @@ public class LinearPalette<T> implements Palette<T> {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 1; i < size() + 1; i++) {
|
for (int i = 1; i < size.get(); i++) {
|
||||||
if (t.equals(palette.get().get(i))) {
|
if (t.equals(palette.get(i))) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,8 +88,8 @@ public class LinearPalette<T> implements Palette<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void iterate(Consumer2<T, Integer> c) {
|
public void iterate(Consumer2<T, Integer> c) {
|
||||||
for (int i = 1; i < size() + 1; i++) {
|
for (int i = 1; i <= size(); i++) {
|
||||||
c.accept(palette.get().get(i), i);
|
c.accept(palette.get(i), i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,14 +44,14 @@ public class CountingDataInputStream extends DataInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(@NotNull byte[] b, int off, int len) throws IOException {
|
public int read(byte @NotNull [] b, int off, int len) throws IOException {
|
||||||
int i = in.read(b, off, len);
|
int i = in.read(b, off, len);
|
||||||
count(i);
|
if (i != -1) count(i);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void count(int i) {
|
private void count(int i) {
|
||||||
count += i;
|
count = Math.addExact(count, i);
|
||||||
if (mark == -1)
|
if (mark == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -69,6 +69,12 @@ public class CountingDataInputStream extends DataInputStream {
|
|||||||
public synchronized void mark(int readlimit) {
|
public synchronized void mark(int readlimit) {
|
||||||
if (!in.markSupported()) return;
|
if (!in.markSupported()) return;
|
||||||
in.mark(readlimit);
|
in.mark(readlimit);
|
||||||
|
if (readlimit <= 0) {
|
||||||
|
mark = -1;
|
||||||
|
markLimit = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mark = count;
|
mark = count;
|
||||||
markLimit = readlimit;
|
markLimit = readlimit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,14 +24,20 @@ import com.google.gson.JsonObject;
|
|||||||
import com.google.gson.JsonParser;
|
import com.google.gson.JsonParser;
|
||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
|
import com.volmit.iris.util.scheduling.J;
|
||||||
import org.apache.commons.io.function.IOConsumer;
|
import org.apache.commons.io.function.IOConsumer;
|
||||||
import org.apache.commons.io.function.IOFunction;
|
import org.apache.commons.io.function.IOFunction;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.channels.FileLock;
|
||||||
|
import java.nio.channels.OverlappingFileLockException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.security.DigestInputStream;
|
import java.security.DigestInputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
@@ -1668,13 +1674,24 @@ public class IO {
|
|||||||
dir.mkdirs();
|
dir.mkdirs();
|
||||||
dir.deleteOnExit();
|
dir.deleteOnExit();
|
||||||
File temp = File.createTempFile("iris",".bin", dir);
|
File temp = File.createTempFile("iris",".bin", dir);
|
||||||
try {
|
try (var target = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.SYNC)) {
|
||||||
|
lock(target);
|
||||||
|
|
||||||
try (var out = builder.apply(new FileOutputStream(temp))) {
|
try (var out = builder.apply(new FileOutputStream(temp))) {
|
||||||
action.accept(out);
|
action.accept(out);
|
||||||
}
|
}
|
||||||
Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
Files.copy(temp.toPath(), Channels.newOutputStream(target));
|
||||||
} finally {
|
} finally {
|
||||||
temp.delete();
|
temp.delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FileLock lock(FileChannel channel) throws IOException {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
return channel.lock();
|
||||||
|
} catch (OverlappingFileLockException e) {}
|
||||||
|
J.sleep(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import com.volmit.iris.util.format.C;
|
|||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
import com.volmit.iris.util.function.Consumer4;
|
import com.volmit.iris.util.function.Consumer4;
|
||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.util.io.IO;
|
||||||
|
import com.volmit.iris.util.mantle.io.IOWorker;
|
||||||
import com.volmit.iris.util.math.M;
|
import com.volmit.iris.util.math.M;
|
||||||
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;
|
||||||
@@ -44,9 +45,7 @@ import com.volmit.iris.util.parallel.MultiBurst;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.Chunk;
|
import org.bukkit.Chunk;
|
||||||
|
|
||||||
import java.io.EOFException;
|
import java.io.*;
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@@ -69,6 +68,7 @@ public class Mantle {
|
|||||||
private final MultiBurst ioBurst;
|
private final MultiBurst ioBurst;
|
||||||
private final Semaphore ioTrim;
|
private final Semaphore ioTrim;
|
||||||
private final Semaphore ioTectonicUnload;
|
private final Semaphore ioTectonicUnload;
|
||||||
|
private final IOWorker worker;
|
||||||
private final AtomicDouble adjustedIdleDuration;
|
private final AtomicDouble adjustedIdleDuration;
|
||||||
private final KSet<Long> toUnload;
|
private final KSet<Long> toUnload;
|
||||||
|
|
||||||
@@ -91,6 +91,7 @@ public class Mantle {
|
|||||||
ioBurst = MultiBurst.burst;
|
ioBurst = MultiBurst.burst;
|
||||||
adjustedIdleDuration = new AtomicDouble(0);
|
adjustedIdleDuration = new AtomicDouble(0);
|
||||||
toUnload = new KSet<>();
|
toUnload = new KSet<>();
|
||||||
|
worker = new IOWorker(dataFolder, worldHeight);
|
||||||
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,7 +380,7 @@ public class Mantle {
|
|||||||
loadedRegions.forEach((i, plate) -> b.queue(() -> {
|
loadedRegions.forEach((i, plate) -> b.queue(() -> {
|
||||||
try {
|
try {
|
||||||
plate.close();
|
plate.close();
|
||||||
plate.write(fileForRegion(dataFolder, i, false));
|
worker.write(fileForRegion(dataFolder, i, false).getName(), plate);
|
||||||
oldFileForRegion(dataFolder, i).delete();
|
oldFileForRegion(dataFolder, i).delete();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i));
|
Iris.error("Failed to write Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i));
|
||||||
@@ -394,6 +395,11 @@ public class Mantle {
|
|||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
worker.close();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.reportError(e);
|
||||||
|
}
|
||||||
|
|
||||||
IO.delete(new File(dataFolder, ".tmp"));
|
IO.delete(new File(dataFolder, ".tmp"));
|
||||||
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath());
|
||||||
@@ -462,8 +468,8 @@ public class Mantle {
|
|||||||
|
|
||||||
ioTectonicUnload.acquireUninterruptibly(LOCK_SIZE);
|
ioTectonicUnload.acquireUninterruptibly(LOCK_SIZE);
|
||||||
try {
|
try {
|
||||||
for (long id : toUnload) {
|
|
||||||
double unloadTime = M.ms() - adjustedIdleDuration.get();
|
double unloadTime = M.ms() - adjustedIdleDuration.get();
|
||||||
|
for (long id : toUnload) {
|
||||||
burst.queue(() -> hyperLock.withLong(id, () -> {
|
burst.queue(() -> hyperLock.withLong(id, () -> {
|
||||||
TectonicPlate m = loadedRegions.get(id);
|
TectonicPlate m = loadedRegions.get(id);
|
||||||
if (m == null) {
|
if (m == null) {
|
||||||
@@ -484,14 +490,15 @@ public class Mantle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
m.write(fileForRegion(dataFolder, id, false));
|
m.close();
|
||||||
|
worker.write(fileForRegion(dataFolder, id, false).getName(), m);
|
||||||
oldFileForRegion(dataFolder, id).delete();
|
oldFileForRegion(dataFolder, id).delete();
|
||||||
loadedRegions.remove(id, m);
|
loadedRegions.remove(id, m);
|
||||||
lastUse.remove(id);
|
lastUse.remove(id);
|
||||||
toUnload.remove(id);
|
toUnload.remove(id);
|
||||||
i.incrementAndGet();
|
i.incrementAndGet();
|
||||||
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||||
} catch (IOException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@@ -523,14 +530,11 @@ public class Mantle {
|
|||||||
try {
|
try {
|
||||||
if (!trim || !unload) {
|
if (!trim || !unload) {
|
||||||
try {
|
try {
|
||||||
return getSafe(x, z).get();
|
return getSafe(x, z);
|
||||||
} catch (InterruptedException e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
|
||||||
Long key = key(x, z);
|
Long key = key(x, z);
|
||||||
TectonicPlate p = loadedRegions.get(key);
|
TectonicPlate p = loadedRegions.get(key);
|
||||||
|
|
||||||
@@ -538,15 +542,19 @@ public class Mantle {
|
|||||||
use(key);
|
use(key);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return getSafe(x, z).get();
|
return getSafe(x, z);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread intterruption (hotload?)");
|
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread intterruption (hotload?)");
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
} catch (ExecutionException e) {
|
} catch (ExecutionException e) {
|
||||||
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread execution exception (engine close?)");
|
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread execution exception (engine close?)");
|
||||||
Iris.reportError(e);
|
Iris.reportError(e);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a unknown exception");
|
||||||
|
Iris.reportError(e);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (trim) ioTrim.release();
|
if (trim) ioTrim.release();
|
||||||
@@ -566,21 +574,22 @@ public class Mantle {
|
|||||||
* @return the future of a tectonic plate.
|
* @return the future of a tectonic plate.
|
||||||
*/
|
*/
|
||||||
@RegionCoordinates
|
@RegionCoordinates
|
||||||
private Future<TectonicPlate> getSafe(int x, int z) {
|
private TectonicPlate getSafe(int x, int z) throws Throwable {
|
||||||
return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> {
|
return hyperLock.withNastyResult(x, z, () -> {
|
||||||
Long k = key(x, z);
|
Long k = key(x, z);
|
||||||
use(k);
|
use(k);
|
||||||
TectonicPlate region = loadedRegions.get(k);
|
TectonicPlate r = loadedRegions.get(k);
|
||||||
|
if (r != null && !r.isClosed()) {
|
||||||
if (region != null && !region.isClosed()) {
|
return r;
|
||||||
return region;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ioBurst.completeValue(() -> {
|
||||||
|
TectonicPlate region;
|
||||||
File file = fileForRegion(dataFolder, x, z);
|
File file = fileForRegion(dataFolder, x, z);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
try {
|
try {
|
||||||
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath());
|
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath());
|
||||||
region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv."));
|
region = worker.read(file.getName());
|
||||||
|
|
||||||
if (region.getX() != x || region.getZ() != z) {
|
if (region.getX() != x || region.getZ() != z) {
|
||||||
Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z);
|
Iris.warn("Loaded Tectonic Plate " + x + "," + z + " but read it as " + region.getX() + "," + region.getZ() + "... Assuming " + x + "," + z);
|
||||||
@@ -609,7 +618,8 @@ public class Mantle {
|
|||||||
Iris.debug("Created new Tectonic Plate " + C.DARK_GREEN + x + " " + z);
|
Iris.debug("Created new Tectonic Plate " + C.DARK_GREEN + x + " " + z);
|
||||||
use(k);
|
use(k);
|
||||||
return region;
|
return region;
|
||||||
}));
|
}).get();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void use(Long key) {
|
private void use(Long key) {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ 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.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.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.matter.IrisMatter;
|
import com.volmit.iris.util.matter.IrisMatter;
|
||||||
@@ -28,6 +29,7 @@ import com.volmit.iris.util.matter.Matter;
|
|||||||
import com.volmit.iris.util.matter.MatterSlice;
|
import com.volmit.iris.util.matter.MatterSlice;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
@@ -47,6 +49,7 @@ public class MantleChunk {
|
|||||||
@Getter
|
@Getter
|
||||||
private final int z;
|
private final int z;
|
||||||
private final AtomicIntegerArray flags;
|
private final AtomicIntegerArray 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);
|
||||||
@@ -60,11 +63,13 @@ public class MantleChunk {
|
|||||||
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 AtomicIntegerArray(MantleFlag.values().length);
|
flags = new AtomicIntegerArray(MantleFlag.values().length);
|
||||||
|
flagLocks = new Object[MantleFlag.values().length];
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.z = z;
|
this.z = z;
|
||||||
|
|
||||||
for (int i = 0; i < flags.length(); i++) {
|
for (int i = 0; i < flags.length(); i++) {
|
||||||
flags.set(i, 0);
|
flags.set(i, 0);
|
||||||
|
flagLocks[i] = new Object();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +114,9 @@ public class MantleChunk {
|
|||||||
din.skipTo(end);
|
din.skipTo(end);
|
||||||
TectonicPlate.addError();
|
TectonicPlate.addError();
|
||||||
}
|
}
|
||||||
|
if (din.count() != start + size) {
|
||||||
|
throw new IOException("Chunk section read size mismatch!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,12 +151,18 @@ public class MantleChunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void raiseFlag(MantleFlag flag, Runnable r) {
|
public void raiseFlag(MantleFlag flag, Runnable r) {
|
||||||
synchronized (this) {
|
raiseFlag(null, flag, r);
|
||||||
if (!isFlagged(flag)) flag(flag, true);
|
|
||||||
else return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.getAndSet(flag.ordinal(), 1) == 0) {
|
||||||
r.run();
|
r.run();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isFlagged(MantleFlag flag) {
|
public boolean isFlagged(MantleFlag flag) {
|
||||||
return flags.get(flag.ordinal()) == 1;
|
return flags.get(flag.ordinal()) == 1;
|
||||||
@@ -176,6 +190,15 @@ public class MantleChunk {
|
|||||||
return sections.get(section);
|
return sections.get(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@ChunkRelativeBlockCoordinates
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(int x, int y, int z, Class<T> type) {
|
||||||
|
return (T) getOrCreate(y >> 4)
|
||||||
|
.slice(type)
|
||||||
|
.get(x & 15, y & 15, z & 15);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all matter from this chunk
|
* Clear all matter from this chunk
|
||||||
*/
|
*/
|
||||||
@@ -207,7 +230,9 @@ public class MantleChunk {
|
|||||||
|
|
||||||
if (matter == null) {
|
if (matter == null) {
|
||||||
matter = new IrisMatter(16, 16, 16);
|
matter = new IrisMatter(16, 16, 16);
|
||||||
sections.set(section, matter);
|
if (!sections.compareAndSet(section, null, matter)) {
|
||||||
|
matter = get(section);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return matter;
|
return matter;
|
||||||
|
|||||||
@@ -19,26 +19,14 @@
|
|||||||
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.core.IrisSettings;
|
|
||||||
import com.volmit.iris.engine.EnginePanic;
|
import com.volmit.iris.engine.EnginePanic;
|
||||||
import com.volmit.iris.engine.data.cache.Cache;
|
import com.volmit.iris.engine.data.cache.Cache;
|
||||||
import com.volmit.iris.util.data.Varint;
|
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.format.C;
|
|
||||||
import com.volmit.iris.util.format.Form;
|
|
||||||
import com.volmit.iris.util.io.CountingDataInputStream;
|
import com.volmit.iris.util.io.CountingDataInputStream;
|
||||||
import com.volmit.iris.util.io.IO;
|
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.jpountz.lz4.LZ4BlockInputStream;
|
|
||||||
import net.jpountz.lz4.LZ4BlockOutputStream;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.channels.Channels;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.StandardCopyOption;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||||
|
|
||||||
@@ -111,31 +99,6 @@ public class TectonicPlate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TectonicPlate read(int worldHeight, File file, boolean versioned) throws IOException {
|
|
||||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
|
||||||
fc.lock();
|
|
||||||
|
|
||||||
InputStream fin = Channels.newInputStream(fc);
|
|
||||||
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
|
|
||||||
BufferedInputStream bis = new BufferedInputStream(lz4);
|
|
||||||
try (CountingDataInputStream din = CountingDataInputStream.wrap(bis)) {
|
|
||||||
return new TectonicPlate(worldHeight, din, versioned);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (IrisSettings.get().getGeneral().isDumpMantleOnError() && errors.get()) {
|
|
||||||
File dump = Iris.instance.getDataFolder("dump", file.getName() + ".bin");
|
|
||||||
try (FileChannel fc = FileChannel.open(file.toPath(), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.SYNC)) {
|
|
||||||
fc.lock();
|
|
||||||
|
|
||||||
InputStream fin = Channels.newInputStream(fc);
|
|
||||||
LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin);
|
|
||||||
Files.copy(lz4, dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
errors.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean inUse() {
|
public boolean inUse() {
|
||||||
for (int i = 0; i < chunks.length(); i++) {
|
for (int i = 0; i < chunks.length(); i++) {
|
||||||
MantleChunk chunk = chunks.get(i);
|
MantleChunk chunk = chunks.get(i);
|
||||||
@@ -223,18 +186,6 @@ public class TectonicPlate {
|
|||||||
return Cache.to1D(x, z, 0, 32, 32);
|
return Cache.to1D(x, z, 0, 32, 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Write this tectonic plate to file
|
|
||||||
*
|
|
||||||
* @param file the file to writeNodeData it to
|
|
||||||
* @throws IOException shit happens
|
|
||||||
*/
|
|
||||||
public void write(File file) throws IOException {
|
|
||||||
PrecisionStopwatch p = PrecisionStopwatch.start();
|
|
||||||
IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), this::write);
|
|
||||||
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName() + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write this tectonic plate to a data stream
|
* Write this tectonic plate to a data stream
|
||||||
*
|
*
|
||||||
@@ -268,4 +219,12 @@ public class TectonicPlate {
|
|||||||
public static void addError() {
|
public static void addError() {
|
||||||
errors.set(true);
|
errors.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasError() {
|
||||||
|
try {
|
||||||
|
return errors.get();
|
||||||
|
} finally {
|
||||||
|
errors.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.volmit.iris.util.mantle.io;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.channels.Channels;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
|
||||||
|
public class DelegateStream {
|
||||||
|
|
||||||
|
public static InputStream read(FileChannel channel) throws IOException {
|
||||||
|
channel.position(0);
|
||||||
|
return new Input(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OutputStream write(FileChannel channel) throws IOException {
|
||||||
|
channel.position(0);
|
||||||
|
return new Output(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Input extends InputStream {
|
||||||
|
private final InputStream delegate;
|
||||||
|
|
||||||
|
private Input(FileChannel channel) {
|
||||||
|
this.delegate = Channels.newInputStream(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
return delegate.available();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
return delegate.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte @NotNull [] b, int off, int len) throws IOException {
|
||||||
|
return delegate.read(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte @NotNull [] readAllBytes() throws IOException {
|
||||||
|
return delegate.readAllBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readNBytes(byte[] b, int off, int len) throws IOException {
|
||||||
|
return delegate.readNBytes(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte @NotNull [] readNBytes(int len) throws IOException {
|
||||||
|
return delegate.readNBytes(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
return delegate.skip(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void skipNBytes(long n) throws IOException {
|
||||||
|
delegate.skipNBytes(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long transferTo(OutputStream out) throws IOException {
|
||||||
|
return delegate.transferTo(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Output extends OutputStream {
|
||||||
|
private final FileChannel channel;
|
||||||
|
private final OutputStream delegate;
|
||||||
|
|
||||||
|
private Output(FileChannel channel) {
|
||||||
|
this.channel = channel;
|
||||||
|
this.delegate = Channels.newOutputStream(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
delegate.write(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(byte @NotNull [] b, int off, int len) throws IOException {
|
||||||
|
delegate.write(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() throws IOException {
|
||||||
|
channel.truncate(channel.position());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
channel.force(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.volmit.iris.util.mantle.io;
|
||||||
|
|
||||||
|
import com.volmit.iris.util.io.IO;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
class Holder {
|
||||||
|
private final FileChannel channel;
|
||||||
|
private final Semaphore semaphore = new Semaphore(1);
|
||||||
|
private volatile boolean closed;
|
||||||
|
|
||||||
|
Holder(FileChannel channel) throws IOException {
|
||||||
|
this.channel = channel;
|
||||||
|
IO.lock(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizedChannel acquire() {
|
||||||
|
semaphore.acquireUninterruptibly();
|
||||||
|
if (closed) {
|
||||||
|
semaphore.release();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SynchronizedChannel(channel, semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() throws IOException {
|
||||||
|
semaphore.acquireUninterruptibly();
|
||||||
|
try {
|
||||||
|
if (closed) return;
|
||||||
|
closed = true;
|
||||||
|
channel.close();
|
||||||
|
} finally {
|
||||||
|
semaphore.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
130
core/src/main/java/com/volmit/iris/util/mantle/io/IOWorker.java
Normal file
130
core/src/main/java/com/volmit/iris/util/mantle/io/IOWorker.java
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.volmit.iris.util.mantle.io;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.format.Form;
|
||||||
|
import com.volmit.iris.util.io.CountingDataInputStream;
|
||||||
|
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||||
|
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||||
|
import net.jpountz.lz4.LZ4BlockInputStream;
|
||||||
|
import net.jpountz.lz4.LZ4BlockOutputStream;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.file.*;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class IOWorker {
|
||||||
|
private static final Set<OpenOption> OPTIONS = Set.of(StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.SYNC);
|
||||||
|
private static final int MAX_CACHE_SIZE = 128;
|
||||||
|
|
||||||
|
private final Path root;
|
||||||
|
private final File tmp;
|
||||||
|
private final int worldHeight;
|
||||||
|
|
||||||
|
private final Object2ObjectLinkedOpenHashMap<String, Holder> cache = new Object2ObjectLinkedOpenHashMap<>();
|
||||||
|
|
||||||
|
public IOWorker(File root, int worldHeight) {
|
||||||
|
this.root = root.toPath();
|
||||||
|
this.tmp = new File(root, ".tmp");
|
||||||
|
this.worldHeight = worldHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TectonicPlate read(final String name) throws IOException {
|
||||||
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
|
try (var channel = getChannel(name)) {
|
||||||
|
var raw = channel.read();
|
||||||
|
var lz4 = new LZ4BlockInputStream(raw);
|
||||||
|
var buffered = new BufferedInputStream(lz4);
|
||||||
|
try (var in = CountingDataInputStream.wrap(buffered)) {
|
||||||
|
return new TectonicPlate(worldHeight, in, name.startsWith("pv."));
|
||||||
|
} finally {
|
||||||
|
if (TectonicPlate.hasError() && IrisSettings.get().getGeneral().isDumpMantleOnError()) {
|
||||||
|
File dump = Iris.instance.getDataFolder("dump", name + ".bin");
|
||||||
|
Files.copy(new LZ4BlockInputStream(channel.read()), dump.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
|
} else {
|
||||||
|
Iris.debug("Read Tectonic Plate " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(final String name, final TectonicPlate plate) throws IOException {
|
||||||
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
|
try (var channel = getChannel(name)) {
|
||||||
|
tmp.mkdirs();
|
||||||
|
File file = File.createTempFile("iris", ".bin", tmp);
|
||||||
|
try {
|
||||||
|
try (var tmp = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) {
|
||||||
|
plate.write(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
try (var out = channel.write()) {
|
||||||
|
Files.copy(file.toPath(), out);
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
synchronized (cache) {
|
||||||
|
for (Holder h : cache.values()) {
|
||||||
|
h.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SynchronizedChannel getChannel(final String name) throws IOException {
|
||||||
|
PrecisionStopwatch p = PrecisionStopwatch.start();
|
||||||
|
try {
|
||||||
|
synchronized (cache) {
|
||||||
|
Holder holder = cache.getAndMoveToFirst(name);
|
||||||
|
if (holder != null) {
|
||||||
|
var channel = holder.acquire();
|
||||||
|
if (channel != null) {
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache.size() >= MAX_CACHE_SIZE) {
|
||||||
|
var last = cache.removeLast();
|
||||||
|
last.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
holder = new Holder(FileChannel.open(root.resolve(name), OPTIONS));
|
||||||
|
cache.putAndMoveToFirst(name, holder);
|
||||||
|
return Objects.requireNonNull(holder.acquire());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Iris.debug("Acquired Channel for " + C.DARK_GREEN + name + C.RED + " in " + Form.duration(p.getMilliseconds(), 2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||||
|
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.volmit.iris.util.mantle.io;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
|
public class SynchronizedChannel implements Closeable {
|
||||||
|
private final FileChannel channel;
|
||||||
|
private final Semaphore lock;
|
||||||
|
private transient boolean closed;
|
||||||
|
|
||||||
|
SynchronizedChannel(FileChannel channel, Semaphore lock) {
|
||||||
|
this.channel = channel;
|
||||||
|
this.lock = lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream read() throws IOException {
|
||||||
|
if (closed) throw new IOException("Channel is closed!");
|
||||||
|
return DelegateStream.read(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputStream write() throws IOException {
|
||||||
|
if (closed) throw new IOException("Channel is closed!");
|
||||||
|
return DelegateStream.write(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (closed) return;
|
||||||
|
closed = true;
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,6 +25,8 @@ import com.volmit.iris.util.json.JSONObject;
|
|||||||
import com.volmit.iris.util.plugin.VolmitSender;
|
import com.volmit.iris.util.plugin.VolmitSender;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class IrisMatter extends IrisRegistrant implements Matter {
|
public class IrisMatter extends IrisRegistrant implements Matter {
|
||||||
protected static final KMap<Class<?>, MatterSlice<?>> slicers = buildSlicers();
|
protected static final KMap<Class<?>, MatterSlice<?>> slicers = buildSlicers();
|
||||||
|
|
||||||
@@ -65,6 +67,12 @@ public class IrisMatter extends IrisRegistrant implements Matter {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> MatterSlice<T> slice(Class<?> c) {
|
||||||
|
return (MatterSlice<T>) sliceMap.computeIfAbsent(c, $ -> Objects.requireNonNull(createSlice(c, this), "Bad slice " + c.getCanonicalName()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> MatterSlice<T> createSlice(Class<T> type, Matter m) {
|
public <T> MatterSlice<T> createSlice(Class<T> type, Matter m) {
|
||||||
MatterSlice<?> slice = slicers.get(type);
|
MatterSlice<?> slice = slicers.get(type);
|
||||||
|
|||||||
@@ -99,10 +99,9 @@ public interface Matter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Matter read(File f) throws IOException {
|
static Matter read(File f) throws IOException {
|
||||||
FileInputStream in = new FileInputStream(f);
|
try (var in = new FileInputStream(f)) {
|
||||||
Matter m = read(in);
|
return read(in);
|
||||||
in.close();
|
}
|
||||||
return m;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Matter read(InputStream in) throws IOException {
|
static Matter read(InputStream in) throws IOException {
|
||||||
@@ -142,6 +141,7 @@ public interface Matter {
|
|||||||
long size = din.readInt();
|
long size = din.readInt();
|
||||||
if (size == 0) continue;
|
if (size == 0) continue;
|
||||||
long start = din.count();
|
long start = din.count();
|
||||||
|
long end = start + size;
|
||||||
|
|
||||||
Iris.addPanic("read.matter.slice", i + "");
|
Iris.addPanic("read.matter.slice", i + "");
|
||||||
try {
|
try {
|
||||||
@@ -151,9 +151,9 @@ public interface Matter {
|
|||||||
Class<?> type = Class.forName(cn);
|
Class<?> type = Class.forName(cn);
|
||||||
MatterSlice<?> slice = matter.createSlice(type, matter);
|
MatterSlice<?> slice = matter.createSlice(type, matter);
|
||||||
slice.read(din);
|
slice.read(din);
|
||||||
|
if (din.count() < end) throw new IOException("Matter slice read size mismatch!");
|
||||||
matter.putSlice(type, slice);
|
matter.putSlice(type, slice);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
long end = start + size;
|
|
||||||
if (!(e instanceof ClassNotFoundException)) {
|
if (!(e instanceof ClassNotFoundException)) {
|
||||||
Iris.error("Failed to read matter slice, skipping it.");
|
Iris.error("Failed to read matter slice, skipping it.");
|
||||||
Iris.addPanic("read.byte.range", start + " " + end);
|
Iris.addPanic("read.byte.range", start + " " + end);
|
||||||
@@ -165,6 +165,10 @@ public interface Matter {
|
|||||||
}
|
}
|
||||||
din.skipTo(end);
|
din.skipTo(end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (din.count() != end) {
|
||||||
|
throw new IOException("Matter slice read size mismatch!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return matter;
|
return matter;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
|
|||||||
import com.volmit.iris.Iris;
|
import com.volmit.iris.Iris;
|
||||||
import com.volmit.iris.engine.data.cache.Cache;
|
import com.volmit.iris.engine.data.cache.Cache;
|
||||||
import com.volmit.iris.util.function.NastyRunnable;
|
import com.volmit.iris.util.function.NastyRunnable;
|
||||||
|
import com.volmit.iris.util.function.NastySupplier;
|
||||||
import com.volmit.iris.util.io.IORunnable;
|
import com.volmit.iris.util.io.IORunnable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -107,6 +108,15 @@ public class HyperLock {
|
|||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> T withNastyResult(int x, int z, NastySupplier<T> r) throws Throwable {
|
||||||
|
lock(x, z);
|
||||||
|
try {
|
||||||
|
return r.get();
|
||||||
|
} finally {
|
||||||
|
unlock(x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean tryLock(int x, int z) {
|
public boolean tryLock(int x, int z) {
|
||||||
return getLock(x, z).tryLock();
|
return getLock(x, z).tryLock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.volmit.iris.util.parallel;
|
||||||
|
|
||||||
|
import com.volmit.iris.util.math.Position2;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class StreamUtils {
|
||||||
|
|
||||||
|
public static Stream<Position2> streamRadius(int x, int z, int radius) {
|
||||||
|
return streamRadius(x, z, radius, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream<Position2> streamRadius(int x, int z, int radiusX, int radiusZ) {
|
||||||
|
return IntStream.rangeClosed(-radiusX, radiusX)
|
||||||
|
.mapToObj(xx -> IntStream.rangeClosed(-radiusZ, radiusZ)
|
||||||
|
.mapToObj(zz -> new Position2(x + xx, z + zz)))
|
||||||
|
.flatMap(Function.identity());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T, M> void forEach(Stream<T> stream, Function<T, Stream<M>> mapper, Consumer<M> consumer, @Nullable MultiBurst burst) {
|
||||||
|
forEach(stream.flatMap(mapper), consumer, burst);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public static <T> void forEach(Stream<T> stream, Consumer<T> task, @Nullable MultiBurst burst) {
|
||||||
|
if (burst == null) stream.forEach(task);
|
||||||
|
else burst.submit(() -> stream.parallel().forEach(task)).get();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user