9
0
mirror of https://github.com/VolmitSoftware/Iris.git synced 2025-12-19 15:09:18 +00:00

Merge branch 'dev' into feat/kts

# Conflicts:
#	core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java
#	core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java
#	core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java
#	core/src/main/java/com/volmit/iris/util/mantle/Mantle.java
#	core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java
This commit is contained in:
Julian Krings
2025-09-02 12:47:23 +02:00
53 changed files with 1088 additions and 616 deletions

View File

@@ -31,13 +31,12 @@ plugins {
java java
`java-library` `java-library`
alias(libs.plugins.shadow) alias(libs.plugins.shadow)
alias(libs.plugins.sentry)
alias(libs.plugins.download) alias(libs.plugins.download)
alias(libs.plugins.runPaper) alias(libs.plugins.runPaper)
} }
group = "com.volmit" group = "com.volmit"
version = "3.7.0-1.20.1-1.21.7" version = "3.7.2-1.20.1-1.21.8"
apply<ApiGenerator>() apply<ApiGenerator>()
@@ -107,7 +106,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)
@@ -158,12 +157,13 @@ tasks {
group = "io.sentry" group = "io.sentry"
dependsOn("downloadCli") dependsOn("downloadCli")
doLast { doLast {
val url = "http://sentry.volmit.com:8080"
val authToken = project.findProperty("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN") val authToken = project.findProperty("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN")
val org = "volmit-software" val org = "sentry"
val projectName = "iris" val projectName = "iris"
exec(cli, "releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) exec(cli, "--url", url , "--auth-token", authToken, "releases", "new", "-o", org, "-p", projectName, version)
exec(cli, "releases", "set-commits", "--auth-token", authToken, "-o", org, "-p", projectName, version, "--auto", "--ignore-missing") exec(cli, "--url", url , "--auth-token", authToken, "releases", "set-commits", "-o", org, "-p", projectName, version, "--auto", "--ignore-missing")
exec(cli, "releases", "finalize", "--auth-token", authToken, "-o", org, "-p", projectName, version) //exec(cli, "--url", url, "--auth-token", authToken, "releases", "finalize", "-o", org, "-p", projectName, version)
cli.delete() cli.delete()
} }
} }

View File

@@ -114,10 +114,11 @@ java {
} }
sentry { sentry {
url = "http://sentry.volmit.com:8080/"
autoInstallation.enabled = false autoInstallation.enabled = false
includeSourceContext = true includeSourceContext = true
org = "volmit-software" org = "sentry"
projectName = "iris" projectName = "iris"
authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN") authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN")
} }

View File

@@ -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,24 @@ 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();
MultiBurst.ioBurst.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) -> {
@@ -547,17 +567,7 @@ public class Iris extends VolmitPlugin implements Listener {
postShutdown.forEach(Runnable::run); postShutdown.forEach(Runnable::run);
super.onDisable(); super.onDisable();
J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan); J.attempt(new JarScanner(instance.getJarFile(), "", false)::scanAll);
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() {

View File

@@ -49,11 +49,10 @@ public class IrisSettings {
private IrisSettingsSentry sentry = new IrisSettingsSentry(); private IrisSettingsSentry sentry = new IrisSettingsSentry();
public static int getThreadCount(int c) { public static int getThreadCount(int c) {
return switch (c) { return Math.max(switch (c) {
case -1, -2, -4 -> Runtime.getRuntime().availableProcessors() / -c; case -1, -2, -4 -> Runtime.getRuntime().availableProcessors() / -c;
case 0, 1, 2 -> 1;
default -> Math.max(c, 2); default -> Math.max(c, 2);
}; }, 1);
} }
public static IrisSettings get() { public static IrisSettings get() {
@@ -138,6 +137,7 @@ public class IrisSettings {
@Data @Data
public static class IrisSettingsConcurrency { public static class IrisSettingsConcurrency {
public int parallelism = -1; public int parallelism = -1;
public int ioParallelism = -2;
public int worldGenParallelism = -1; public int worldGenParallelism = -1;
public int getWorldGenThreads() { public int getWorldGenThreads() {
@@ -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() {

View File

@@ -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")

View File

@@ -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")

View File

@@ -181,17 +181,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
@@ -209,21 +224,24 @@ 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();
c.copyFlags(chunk); try {
c.clear(); c.copyFlags(chunk);
for (int y = 0; y < sections; y++) { c.clear();
var slice = chunk.get(y); for (int y = 0; y < sections; y++) {
if (slice == null) continue; var slice = chunk.get(y);
var s = c.getOrCreate(y); if (slice == null) continue;
slice.getSliceMap().forEach(s::putSlice); var s = c.getOrCreate(y);
slice.getSliceMap().forEach(s::putSlice);
}
} finally {
c.release();
} }
}); });
} catch (Throwable e) { } catch (Throwable e) {

View File

@@ -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,19 +45,22 @@ 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;
} }
if (chunkUpdater != null) { synchronized (lock) {
chunkUpdater.stop(); if (chunkUpdater != null) {
} chunkUpdater.stop();
}
chunkUpdater = new ChunkUpdater(world); chunkUpdater = new ChunkUpdater(world);
if (sender().isPlayer()) { if (sender().isPlayer()) {
sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks()));
} else { } else {
Iris.info(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); Iris.info(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks()));
}
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) {

View File

@@ -30,7 +30,6 @@ import com.volmit.iris.engine.object.IrisWorld;
import com.volmit.iris.util.collection.KList; 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.collection.KSet; import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.registry.Attributes;
import com.volmit.iris.util.format.Form; import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.math.BlockPosition; import com.volmit.iris.util.math.BlockPosition;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
@@ -40,7 +39,6 @@ import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.O; import com.volmit.iris.util.scheduling.O;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.LivingEntity; import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -415,8 +413,7 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
} }
private double getWorldX(double screenX) { private double getWorldX(double screenX) {
//return (mscale * screenX) + ((oxp / scale) * mscale); return (mscale * screenX) + ((oxp / scale) * mscale);
return (mscale * screenX) + ((oxp / scale));
} }
private double getWorldZ(double screenZ) { private double getWorldZ(double screenZ) {
@@ -486,6 +483,13 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
hz += Math.abs(hz - lz) * 0.36; hz += Math.abs(hz - lz) * 0.36;
} }
if (Math.abs(lx - hx) < 0.5) {
hx = lx;
}
if (Math.abs(lz - hz) < 0.5) {
hz = lz;
}
if (centities.flip()) { if (centities.flip()) {
J.s(() -> { J.s(() -> {
synchronized (lastEntities) { synchronized (lastEntities) {
@@ -510,8 +514,8 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
int iscale = (int) scale; int iscale = (int) scale;
g.setColor(Color.white); g.setColor(Color.white);
g.clearRect(0, 0, w, h); g.clearRect(0, 0, w, h);
int posX = (int) oxp; double offsetX = oxp / scale;
int posZ = (int) ozp; double offsetZ = ozp / scale;
m.set(3); m.set(3);
for (int r = 0; r < Math.max(w, h); r += iscale) { for (int r = 0; r < Math.max(w, h); r += iscale) {
@@ -520,10 +524,14 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
int a = i - (w / 2); int a = i - (w / 2);
int b = j - (h / 2); int b = j - (h / 2);
if (a * a + b * b <= r * r) { if (a * a + b * b <= r * r) {
BufferedImage t = getTile(gg, iscale, Math.floorDiv((posX / iscale) + i, iscale) * iscale, Math.floorDiv((posZ / iscale) + j, iscale) * iscale, m); int tx = (int) (Math.floor((offsetX + i) / iscale) * iscale);
int tz = (int) (Math.floor((offsetZ + j) / iscale) * iscale);
BufferedImage t = getTile(gg, iscale, tx, tz, m);
if (t != null) { if (t != null) {
g.drawImage(t, i - ((posX / iscale) % (iscale)), j - ((posZ / iscale) % (iscale)), iscale, iscale, (img, infoflags, x, y, width, height) -> true); int rx = Math.floorMod((int) Math.floor(offsetX), iscale);
int rz = Math.floorMod((int) Math.floor(offsetZ), iscale);
g.drawImage(t, i - rx, j - rz, iscale, iscale, (img, infoflags, x, y, width, height) -> true);
} }
} }
} }
@@ -651,8 +659,8 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
private void animateTo(double wx, double wz) { private void animateTo(double wx, double wz) {
double cx = getWorldX(getWidth() / 2); double cx = getWorldX(getWidth() / 2);
double cz = getWorldZ(getHeight() / 2); double cz = getWorldZ(getHeight() / 2);
ox += (wx - cx); ox += ((wx - cx) / mscale) * scale;
oz += (wz - cz); oz += ((wz - cz) / mscale) * scale;
} }
private void renderPosition(Graphics2D g, double x, double z) { private void renderPosition(Graphics2D g, double x, double z) {
@@ -807,11 +815,28 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener
return; return;
} }
//Iris.info("Blocks/Pixel: " + (mscale) + ", Blocks Wide: " + (w * mscale)); double m0 = mscale;
double m1 = m0 + ((0.25 * m0) * notches);
m1 = Math.max(m1, 0.00001);
if (m1 == m0) {
return;
}
positions.clear(); positions.clear();
fastpositions.clear(); fastpositions.clear();
mscale = mscale + ((0.25 * mscale) * notches);
mscale = Math.max(mscale, 0.00001); Point p = e.getPoint();
double sx = p.getX();
double sz = p.getY();
double newOxp = scale * ((m0 / m1) * (sx + (oxp / scale)) - sx);
double newOzp = scale * ((m0 / m1) * (sz + (ozp / scale)) - sz);
mscale = m1;
oxp = newOxp;
ozp = newOzp;
ox = oxp;
oz = ozp;
} }
@Override @Override

View File

@@ -4,24 +4,27 @@ import com.volmit.iris.Iris;
import com.volmit.iris.core.link.ExternalDataProvider; import com.volmit.iris.core.link.ExternalDataProvider;
import com.volmit.iris.core.link.Identifier; import com.volmit.iris.core.link.Identifier;
import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.Engine;
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.data.B; import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.data.IrisCustomData; import com.volmit.iris.util.data.IrisCustomData;
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;
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.stream.Collectors;
public class ItemAdderDataProvider extends ExternalDataProvider { public class ItemAdderDataProvider extends ExternalDataProvider {
private KList<String> itemNamespaces, blockNamespaces; private final KSet<String> itemNamespaces = new KSet<>();
private final KSet<String> blockNamespaces = new KSet<>();
public ItemAdderDataProvider() { public ItemAdderDataProvider() {
super("ItemsAdder"); super("ItemsAdder");
@@ -29,16 +32,12 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
@Override @Override
public void init() { public void init() {
this.itemNamespaces = new KList<>(); updateNamespaces();
this.blockNamespaces = new KList<>(); }
for (Identifier i : getTypes(DataType.ITEM)) { @EventHandler
itemNamespaces.addIfMissing(i.namespace()); public void onLoadData(ItemsAdderLoadDataEvent event) {
} updateNamespaces();
for (Identifier i : getTypes(DataType.BLOCK)) {
blockNamespaces.addIfMissing(i.namespace());
Iris.info("Found ItemAdder Block: " + i);
}
} }
@NotNull @NotNull
@@ -48,7 +47,7 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
if (block == null) { if (block == null) {
throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key());
} }
return new IrisCustomData(B.getAir(), blockId); return new IrisCustomData(block.getBaseBlockData(), blockId);
} }
@NotNull @NotNull
@@ -81,9 +80,25 @@ public class ItemAdderDataProvider extends ExternalDataProvider {
}; };
} }
private void updateNamespaces() {
try {
updateNamespaces(DataType.ITEM);
updateNamespaces(DataType.BLOCK);
} catch (Throwable e) {
Iris.warn("Failed to update ItemAdder namespaces: " + e.getMessage());
}
}
private void updateNamespaces(DataType dataType) {
var namespaces = getTypes(dataType).stream().map(Identifier::namespace).collect(Collectors.toSet());
var currentNamespaces = dataType == DataType.ITEM ? itemNamespaces : blockNamespaces;
currentNamespaces.removeIf(n -> !namespaces.contains(n));
currentNamespaces.addAll(namespaces);
}
@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());
} }
} }

View File

@@ -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();
}
} }

View File

@@ -501,7 +501,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
} }
public void savePrefetch(Engine engine) { public void savePrefetch(Engine engine) {
BurstExecutor b = MultiBurst.burst.burst(loaders.size()); BurstExecutor b = MultiBurst.ioBurst.burst(loaders.size());
for (ResourceLoader<?> i : loaders.values()) { for (ResourceLoader<?> i : loaders.values()) {
b.queue(() -> { b.queue(() -> {
@@ -518,7 +518,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory {
} }
public void loadPrefetch(Engine engine) { public void loadPrefetch(Engine engine) {
BurstExecutor b = MultiBurst.burst.burst(loaders.size()); BurstExecutor b = MultiBurst.ioBurst.burst(loaders.size());
for (ResourceLoader<?> i : loaders.values()) { for (ResourceLoader<?> i : loaders.values()) {
b.queue(() -> { b.queue(() -> {

View File

@@ -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;
@@ -47,7 +48,6 @@ import java.io.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
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 +60,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 +76,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;
@@ -240,7 +240,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
public KList<T> loadAllParallel(KList<String> s) { public KList<T> loadAllParallel(KList<String> s) {
KList<T> m = new KList<>(); KList<T> m = new KList<>();
BurstExecutor burst = MultiBurst.burst.burst(s.size()); BurstExecutor burst = MultiBurst.ioBurst.burst(s.size());
for (String i : s) { for (String i : s) {
burst.queue(() -> { burst.queue(() -> {
@@ -365,29 +365,24 @@ 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();
if (files == null) { if (files == null) {
throw new IllegalStateException("Failed to list files in " + root); throw new IllegalStateException("Failed to list files in " + root);
} }
for (File i : files) { for (File i : files) {
if (i.isDirectory()) { if (i.isDirectory()) {
if (i.getName().equals(folderName)) { if (i.getName().equals(folderName)) {
fc.add(i); fc.add(i);
break; break;
}
} }
} }
folderCache.set(fc);
} }
} return fc;
});
return folderCache.get();
} }
public KList<File> getFolders(String rc) { public KList<File> getFolders(String rc) {
@@ -407,7 +402,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) {
@@ -433,7 +428,7 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
} }
public void clearList() { public void clearList() {
folderCache.set(null); folderCache.reset();
possibleKeys = null; possibleKeys = null;
} }

View File

@@ -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) {}

View File

@@ -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);

View File

@@ -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);
} }
} }

View File

@@ -43,10 +43,6 @@ public class PreservationSVC implements IrisService {
threads.add(t); threads.add(t);
} }
public void register(MultiBurst burst) {
}
public void register(ExecutorService service) { public void register(ExecutorService service) {
services.add(service); services.add(service);
} }

View File

@@ -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;
try {
runnable.run(); J.s(() -> {
} finally { try {
semaphore.release(); runnable.run();
} } finally {
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

View File

@@ -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.flag.MantleFlag; import com.volmit.iris.util.mantle.flag.MantleFlag;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.matter.*; import com.volmit.iris.util.matter.*;
import com.volmit.iris.util.matter.slices.UpdateMatter; import com.volmit.iris.util.matter.slices.UpdateMatter;
import com.volmit.iris.util.parallel.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

View File

@@ -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) { Matter matter = chunk.getOrCreate(y >> 4);
Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)"); matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t);
return; }
}
Matter matter = chunk.getOrCreate(y >> 4); public <T> T getData(int x, int y, int z, Class<T> type) {
matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t); int cx = x >> 4;
} else { int cz = z >> 4;
Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz);
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

View File

@@ -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, ReservedFlag.JIGSAW, 1); super(engineMantle, ReservedFlag.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());
} }

View File

@@ -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);
} }
@@ -218,11 +220,15 @@ public class IrisCarveModifier extends EngineAssignedModifier<BlockData> {
break; break;
} }
if (!B.isSolid(output.get(rx, zone.floor - i - 1, rz))) { BlockData b = blocks.get(i);
BlockData down = output.get(rx, zone.ceiling + i - 1, rz);
if (!B.isSolid(down)) {
continue; continue;
} }
if (B.isOre(output.get(rx, zone.floor - i - 1, rz))) { if (B.isOre(down)) {
output.set(rx, zone.ceiling + i - 1, rz, B.toDeepSlateOre(down, b));
continue; continue;
} }

View File

@@ -28,6 +28,7 @@ import com.volmit.iris.util.hunk.Hunk;
import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.RNG;
import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData; import org.bukkit.block.data.BlockData;
import org.bukkit.util.BlockVector; import org.bukkit.util.BlockVector;
@@ -122,6 +123,9 @@ public class IrisDepositModifier extends EngineAssignedModifier<BlockData> {
if (ny > height || nx > 15 || nx < 0 || ny > getEngine().getHeight() || ny < 0 || nz < 0 || nz > 15) { if (ny > height || nx > 15 || nx < 0 || ny > getEngine().getHeight() || ny < 0 || nz < 0 || nz > 15) {
continue; continue;
} }
if (!k.isReplaceBedrock() && data.get(nx, ny, nz).getMaterial() == Material.BEDROCK) {
continue;
}
if (!getEngine().getMantle().isCarved((cx << 4) + nx, ny, (cz << 4) + nz)) { if (!getEngine().getMantle().isCarved((cx << 4) + nx, ny, (cz << 4) + nz)) {
data.set(nx, ny, nz, B.toDeepSlateOre(data.get(nx, ny, nz), clump.getBlocks().get(j))); data.set(nx, ny, nz, B.toDeepSlateOre(data.get(nx, ny, nz), clump.getBlocks().get(j)));

View File

@@ -143,14 +143,14 @@ public class IrisBiome extends IrisRegistrant implements IRare {
@Desc("The default wall if iris decides to place a wall higher than 2 blocks (steep hills or possibly cliffs)") @Desc("The default wall if iris decides to place a wall higher than 2 blocks (steep hills or possibly cliffs)")
private IrisBiomePaletteLayer wall = new IrisBiomePaletteLayer().zero(); private IrisBiomePaletteLayer wall = new IrisBiomePaletteLayer().zero();
@Required @Required
@ArrayType(min = 1, type = IrisBiomePaletteLayer.class) @ArrayType(type = IrisBiomePaletteLayer.class)
@Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.") @Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.")
private KList<IrisBiomePaletteLayer> layers = new KList<IrisBiomePaletteLayer>().qadd(new IrisBiomePaletteLayer()); private KList<IrisBiomePaletteLayer> layers = new KList<IrisBiomePaletteLayer>().qadd(new IrisBiomePaletteLayer());
@Required @Required
@ArrayType(min = 1, type = IrisBiomePaletteLayer.class) @ArrayType(type = IrisBiomePaletteLayer.class)
@Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.") @Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.")
private KList<IrisBiomePaletteLayer> caveCeilingLayers = new KList<IrisBiomePaletteLayer>().qadd(new IrisBiomePaletteLayer()); private KList<IrisBiomePaletteLayer> caveCeilingLayers = new KList<IrisBiomePaletteLayer>().qadd(new IrisBiomePaletteLayer());
@ArrayType(min = 1, type = IrisBiomePaletteLayer.class) @ArrayType(type = IrisBiomePaletteLayer.class)
@Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.") @Desc("This defines the layers of materials in this biome. Each layer has a palette and min/max height and some other properties. Usually a grassy/sandy layer then a dirt layer then a stone layer. Iris will fill in the remaining blocks below your layers with stone.")
private KList<IrisBiomePaletteLayer> seaLayers = new KList<>(); private KList<IrisBiomePaletteLayer> seaLayers = new KList<>();
@ArrayType(min = 1, type = IrisDecorator.class) @ArrayType(min = 1, type = IrisDecorator.class)

View File

@@ -21,6 +21,7 @@ package com.volmit.iris.engine.object;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.engine.object.annotations.ArrayType; import com.volmit.iris.engine.object.annotations.ArrayType;
import com.volmit.iris.engine.object.annotations.Desc; import com.volmit.iris.engine.object.annotations.Desc;
import com.volmit.iris.engine.object.annotations.Required;
import com.volmit.iris.engine.object.annotations.Snippet; import com.volmit.iris.engine.object.annotations.Snippet;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import lombok.Data; import lombok.Data;
@@ -37,6 +38,7 @@ import org.bukkit.World;
@Data @Data
public class IrisCommand { public class IrisCommand {
@Required
@ArrayType(min = 1, type = String.class) @ArrayType(min = 1, type = String.class)
@Desc("List of commands. Iris replaces {x} {y} and {z} with the location of the entity spawn") @Desc("List of commands. Iris replaces {x} {y} and {z} with the location of the entity spawn")
private KList<String> commands = new KList<>(); private KList<String> commands = new KList<>();

View File

@@ -33,6 +33,7 @@ import org.bukkit.entity.Player;
@Desc("Represents a casting location for a command") @Desc("Represents a casting location for a command")
@Data @Data
public class IrisCommandRegistry { public class IrisCommandRegistry {
@Required
@ArrayType(min = 1, type = IrisCommand.class) @ArrayType(min = 1, type = IrisCommand.class)
@Desc("Run commands, at the exact location of the player") @Desc("Run commands, at the exact location of the player")
private KList<IrisCommand> rawCommands = new KList<>(); private KList<IrisCommand> rawCommands = new KList<>();

View File

@@ -87,6 +87,8 @@ public class IrisDepositGenerator {
@MaxNumber(64) @MaxNumber(64)
@Desc("Ore varience is how many different objects clumps iris will create") @Desc("Ore varience is how many different objects clumps iris will create")
private int varience = 3; private int varience = 3;
@Desc("If set to true, this deposit will replace bedrock")
private boolean replaceBedrock = false;
public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) { public IrisObject getClump(Engine engine, RNG rng, IrisData rdata) {
KList<IrisObject> objects = this.objects.aquire(() -> KList<IrisObject> objects = this.objects.aquire(() ->

View File

@@ -146,7 +146,6 @@ public class IrisEffect {
@MinNumber(1) @MinNumber(1)
@Desc("The chance is 1 in CHANCE per interval") @Desc("The chance is 1 in CHANCE per interval")
private int chance = 50; private int chance = 50;
@ArrayType(min = 1, type = IrisCommandRegistry.class)
@Desc("Run commands, with configurable location parameters") @Desc("Run commands, with configurable location parameters")
private IrisCommandRegistry commandRegistry = null; private IrisCommandRegistry commandRegistry = null;

View File

@@ -50,8 +50,7 @@ public class IrisJigsawPiece extends IrisRegistrant {
@Desc("The object this piece represents") @Desc("The object this piece represents")
private String object = ""; private String object = "";
@Required @ArrayType(type = IrisJigsawPieceConnector.class)
@ArrayType(type = IrisJigsawPieceConnector.class, min = 1)
@Desc("The connectors this object contains") @Desc("The connectors this object contains")
private KList<IrisJigsawPieceConnector> connectors = new KList<>(); private KList<IrisJigsawPieceConnector> connectors = new KList<>();

View File

@@ -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);
} }

View File

@@ -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();
} }

View File

@@ -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,15 +64,13 @@ 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.threadId());
Iris.debug("Dereferenced Context<Engine> " + i.getName() + " " + i.getId());
}
context.remove(i);
} }
context.remove(i);
} }
} }
} }

View File

@@ -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));
} }

View File

@@ -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));

View File

@@ -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);
} }
}
public void setBits(int bits) { return palette.get(id);
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();
} }
} }

View File

@@ -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) { synchronized (palette) {
return palette.computeIfAbsent(t, $ -> {
int index = size.getAndIncrement();
lookup.put(index, t); 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));
} }
} finally {
lock.unlock();
} }
} }
@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;
}
@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;
}
} }

View File

@@ -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);
} }
} }
} }

View File

@@ -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;
} }

View File

@@ -24,6 +24,7 @@ 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 lombok.SneakyThrows; import lombok.SneakyThrows;
@@ -34,10 +35,15 @@ import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter; import org.dom4j.io.XMLWriter;
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;
@@ -1692,13 +1698,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);
}
}
} }

View File

@@ -83,6 +83,28 @@ public class JarScanner {
zip.close(); zip.close();
} }
public void scanAll() throws IOException {
classes.clear();
FileInputStream fin = new FileInputStream(jar);
ZipInputStream zip = new ZipInputStream(fin);
for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
if (!entry.isDirectory() && entry.getName().endsWith(".class")) {
String c = entry.getName().replaceAll("/", ".").replace(".class", "");
if (c.startsWith(superPackage)) {
try {
Class<?> clazz = Class.forName(c);
classes.add(clazz);
} catch (Throwable e) {
if (!report) continue;
Iris.reportError(e);
e.printStackTrace();
}
}
}
}
}
/** /**
* Get the scanned clases * Get the scanned clases
* *

View File

@@ -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.mantle.flag.MantleFlag; import com.volmit.iris.util.mantle.flag.MantleFlag;
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;
@@ -45,9 +46,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;
@@ -70,6 +69,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;
@@ -89,9 +89,10 @@ public class Mantle {
this.ioTectonicUnload = new Semaphore(LOCK_SIZE, true); this.ioTectonicUnload = new Semaphore(LOCK_SIZE, true);
loadedRegions = new KMap<>(); loadedRegions = new KMap<>();
lastUse = new KMap<>(); lastUse = new KMap<>();
ioBurst = MultiBurst.burst; ioBurst = MultiBurst.ioBurst;
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());
} }
@@ -380,7 +381,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));
@@ -395,6 +396,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());
@@ -463,8 +469,8 @@ public class Mantle {
ioTectonicUnload.acquireUninterruptibly(LOCK_SIZE); ioTectonicUnload.acquireUninterruptibly(LOCK_SIZE);
try { try {
double unloadTime = M.ms() - adjustedIdleDuration.get();
for (long id : toUnload) { for (long id : toUnload) {
double unloadTime = M.ms() - adjustedIdleDuration.get();
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) {
@@ -485,14 +491,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);
} }
})); }));
@@ -524,30 +531,31 @@ 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);
TectonicPlate p = loadedRegions.get(key);
if (p != null && !p.isClosed()) {
use(key);
return p;
}
} }
Long key = key(x, z);
TectonicPlate p = loadedRegions.get(key);
if (p != null && !p.isClosed()) {
use(key);
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();
@@ -567,50 +575,52 @@ 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;
} }
File file = fileForRegion(dataFolder, x, z); return ioBurst.completeValue(() -> {
if (file.exists()) { TectonicPlate region;
try { File file = fileForRegion(dataFolder, x, z);
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath()); if (file.exists()) {
region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv.")); try {
Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath());
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);
}
loadedRegions.put(k, region);
Iris.debug("Loaded Tectonic Plate " + C.DARK_GREEN + x + " " + z + C.DARK_AQUA + " " + file.getName());
} catch (Throwable e) {
Iris.error("Failed to read Tectonic Plate " + file.getAbsolutePath() + " creating a new chunk instead.");
Iris.reportError(e);
if (!(e instanceof EOFException)) {
e.printStackTrace();
}
Iris.panic();
region = new TectonicPlate(worldHeight, x, z);
loadedRegions.put(k, region);
Iris.debug("Created new Tectonic Plate (Due to Load Failure) " + C.DARK_GREEN + x + " " + z);
} }
loadedRegions.put(k, region); use(k);
Iris.debug("Loaded Tectonic Plate " + C.DARK_GREEN + x + " " + z + C.DARK_AQUA + " " + file.getName()); return region;
} catch (Throwable e) {
Iris.error("Failed to read Tectonic Plate " + file.getAbsolutePath() + " creating a new chunk instead.");
Iris.reportError(e);
if (!(e instanceof EOFException)) {
e.printStackTrace();
}
Iris.panic();
region = new TectonicPlate(worldHeight, x, z);
loadedRegions.put(k, region);
Iris.debug("Created new Tectonic Plate (Due to Load Failure) " + C.DARK_GREEN + x + " " + z);
} }
region = new TectonicPlate(worldHeight, x, z);
loadedRegions.put(k, region);
Iris.debug("Created new Tectonic Plate " + C.DARK_GREEN + x + " " + z);
use(k); use(k);
return region; return region;
} }).get();
});
region = new TectonicPlate(worldHeight, x, z);
loadedRegions.put(k, region);
Iris.debug("Created new Tectonic Plate " + C.DARK_GREEN + x + " " + z);
use(k);
return region;
}));
} }
private void use(Long key) { private void use(Long key) {

View File

@@ -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.mantle.flag.MantleFlag; import com.volmit.iris.util.mantle.flag.MantleFlag;
@@ -30,6 +31,7 @@ import com.volmit.iris.util.matter.MatterSlice;
import com.volmit.iris.util.parallel.AtomicBooleanArray; import com.volmit.iris.util.parallel.AtomicBooleanArray;
import lombok.Getter; import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.jetbrains.annotations.Nullable;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -48,6 +50,7 @@ public class MantleChunk {
@Getter @Getter
private final int z; private final int z;
private final AtomicBooleanArray flags; private final AtomicBooleanArray flags;
private final Object[] flagLocks;
private final AtomicReferenceArray<Matter> sections; private final AtomicReferenceArray<Matter> sections;
private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true); private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true);
private final AtomicBoolean closed = new AtomicBoolean(false); private final AtomicBoolean closed = new AtomicBoolean(false);
@@ -61,8 +64,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 AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1); flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1);
flagLocks = new Object[flags.length()];
this.x = x; this.x = x;
this.z = z; this.z = z;
for (int i = 0; i < flags.length(); i++) {
flagLocks[i] = new Object();
}
} }
/** /**
@@ -116,6 +124,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!");
}
} }
} }
@@ -158,11 +169,17 @@ 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.compareAndSet(flag.ordinal(), false, true)) {
r.run();
}
} }
r.run();
} }
public boolean isFlagged(MantleFlag flag) { public boolean isFlagged(MantleFlag flag) {
@@ -191,6 +208,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
*/ */
@@ -222,7 +248,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;

View File

@@ -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();
}
}
} }

View File

@@ -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);
}
}
}

View File

@@ -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();
}
}
}

View 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));
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -51,7 +51,7 @@ public class Bindings {
YamlConfiguration desc = resource != null ? YamlConfiguration.loadConfiguration(new InputStreamReader(resource)) : new YamlConfiguration(); YamlConfiguration desc = resource != null ? YamlConfiguration.loadConfiguration(new InputStreamReader(resource)) : new YamlConfiguration();
Sentry.init(options -> { Sentry.init(options -> {
options.setDsn("https://b16ecc222e9c1e0c48faecacb906fd89@o4509451052646400.ingest.de.sentry.io/4509452722765904"); options.setDsn("http://4cdbb9ac953306529947f4ca1e8e6b26@sentry.volmit.com:8080/2");
if (settings.debug) { if (settings.debug) {
options.setLogger(new IrisLogger()); options.setLogger(new IrisLogger());
options.setDebug(true); options.setDebug(true);
@@ -79,7 +79,6 @@ public class Bindings {
scope.setTag("server.api", Bukkit.getBukkitVersion()); scope.setTag("server.api", Bukkit.getBukkitVersion());
scope.setTag("iris.commit", desc.getString("commit", "unknown")); scope.setTag("iris.commit", desc.getString("commit", "unknown"));
}); });
Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close));
} }
private static boolean suppress(Throwable e) { private static boolean suppress(Throwable e) {

View File

@@ -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();
} }

View File

@@ -20,7 +20,6 @@ package com.volmit.iris.util.parallel;
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.service.PreservationSVC;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.M;
import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.PrecisionStopwatch;
@@ -30,30 +29,37 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.IntSupplier;
public class MultiBurst implements ExecutorService { public class MultiBurst implements ExecutorService {
private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000); private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000);
public static final MultiBurst burst = new MultiBurst(); public static final MultiBurst burst = new MultiBurst();
public static final MultiBurst ioBurst = new MultiBurst("Iris IO", () -> IrisSettings.get().getConcurrency().getIoParallelism());
private final AtomicLong last; private final AtomicLong last;
private final String name; private final String name;
private final int priority; private final int priority;
private final IntSupplier parallelism;
private ExecutorService service; private ExecutorService service;
public MultiBurst() { public MultiBurst() {
this("Iris", Thread.MIN_PRIORITY); this("Iris", Thread.MIN_PRIORITY, () -> IrisSettings.get().getConcurrency().getParallelism());
} }
public MultiBurst(String name, int priority) { public MultiBurst(String name, IntSupplier parallelism) {
this(name, Thread.MIN_PRIORITY, parallelism);
}
public MultiBurst(String name, int priority, IntSupplier parallelism) {
this.name = name; this.name = name;
this.priority = priority; this.priority = priority;
this.parallelism = parallelism;
last = new AtomicLong(M.ms()); last = new AtomicLong(M.ms());
Iris.service(PreservationSVC.class).register(this);
} }
private synchronized ExecutorService getService() { private synchronized ExecutorService getService() {
last.set(M.ms()); last.set(M.ms());
if (service == null || service.isShutdown()) { if (service == null || service.isShutdown()) {
service = new ForkJoinPool(IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()), service = new ForkJoinPool(IrisSettings.getThreadCount(parallelism.getAsInt()),
new ForkJoinPool.ForkJoinWorkerThreadFactory() { new ForkJoinPool.ForkJoinWorkerThreadFactory() {
int m = 0; int m = 0;

View File

@@ -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();
}
}