diff --git a/build.gradle.kts b/build.gradle.kts index bd3d12062..a0ecc6a7a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -31,13 +31,12 @@ plugins { java `java-library` alias(libs.plugins.shadow) - alias(libs.plugins.sentry) alias(libs.plugins.download) alias(libs.plugins.runPaper) } group = "com.volmit" -version = "3.7.0-1.20.1-1.21.7" +version = "3.7.2-1.20.1-1.21.8" apply() @@ -107,7 +106,7 @@ nmsBindings.forEach { key, value -> pluginJars(tasks.jar.flatMap { it.archiveFile }) javaLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(jvmVersion.getOrDefault(key, 21))} runDirectory.convention(layout.buildDirectory.dir("run/$key")) - systemProperty("disable.watchdog", "") + systemProperty("disable.watchdog", "true") systemProperty("net.kyori.ansi.colorLevel", color) systemProperty("com.mojang.eula.agree", true) systemProperty("iris.suppressReporting", !errorReporting) @@ -158,12 +157,13 @@ tasks { group = "io.sentry" dependsOn("downloadCli") doLast { + val url = "http://sentry.volmit.com:8080" val authToken = project.findProperty("sentry.auth.token") ?: System.getenv("SENTRY_AUTH_TOKEN") - val org = "volmit-software" + val org = "sentry" val projectName = "iris" - exec(cli, "releases", "new", "--auth-token", authToken, "-o", org, "-p", projectName, version) - exec(cli, "releases", "set-commits", "--auth-token", authToken, "-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", "new", "-o", org, "-p", projectName, version) + exec(cli, "--url", url , "--auth-token", authToken, "releases", "set-commits", "-o", org, "-p", projectName, version, "--auto", "--ignore-missing") + //exec(cli, "--url", url, "--auth-token", authToken, "releases", "finalize", "-o", org, "-p", projectName, version) cli.delete() } } @@ -279,4 +279,4 @@ fun registerCustomOutputTaskUnix(name: String, path: String) { into(file(path)) rename { "Iris.jar" } } -} \ No newline at end of file +} diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 87c663107..e192e784a 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -114,10 +114,11 @@ java { } sentry { + url = "http://sentry.volmit.com:8080/" autoInstallation.enabled = false includeSourceContext = true - org = "volmit-software" + org = "sentry" projectName = "iris" authToken = findProperty("sentry.auth.token") as String? ?: System.getenv("SENTRY_AUTH_TOKEN") } diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 7838b0c3c..615d40d89 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -96,6 +96,7 @@ public class Iris extends VolmitPlugin implements Listener { public static IrisCompat compat; public static FileWatcher configWatcher; private static VolmitSender sender; + private static Thread shutdownHook; static { try { @@ -453,6 +454,7 @@ public class Iris extends VolmitPlugin implements Listener { configWatcher = new FileWatcher(getDataFile("settings.json")); services.values().forEach(IrisService::onEnable); services.values().forEach(this::registerListener); + addShutdownHook(); J.s(() -> { J.a(IrisSafeguard::suggestPaper); 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 filter) { try { IrisWorlds.readBukkitWorlds().forEach((s, generator) -> { @@ -547,17 +567,7 @@ public class Iris extends VolmitPlugin implements Listener { postShutdown.forEach(Runnable::run); super.onDisable(); - J.attempt(new JarScanner(instance.getJarFile(), "", false)::scan); - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - Bukkit.getWorlds() - .stream() - .map(IrisToolbelt::access) - .filter(Objects::nonNull) - .forEach(PlatformChunkGenerator::close); - - MultiBurst.burst.close(); - services.clear(); - })); + J.attempt(new JarScanner(instance.getJarFile(), "", false)::scanAll); } private void setupPapi() { diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index 30216b29e..dcab2aa18 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -49,11 +49,10 @@ public class IrisSettings { private IrisSettingsSentry sentry = new IrisSettingsSentry(); public static int getThreadCount(int c) { - return switch (c) { + return Math.max(switch (c) { case -1, -2, -4 -> Runtime.getRuntime().availableProcessors() / -c; - case 0, 1, 2 -> 1; default -> Math.max(c, 2); - }; + }, 1); } public static IrisSettings get() { @@ -138,6 +137,7 @@ public class IrisSettings { @Data public static class IrisSettingsConcurrency { public int parallelism = -1; + public int ioParallelism = -2; public int worldGenParallelism = -1; public int getWorldGenThreads() { @@ -176,13 +176,13 @@ public class IrisSettings { @Data public static class IrisSettingsUpdater { - public double threadMultiplier = 2; + public int maxConcurrency = 256; public double chunkLoadSensitivity = 0.7; public MsRange emptyMsRange = new MsRange(80, 100); public MsRange defaultMsRange = new MsRange(20, 40); - public double getThreadMultiplier() { - return Math.min(Math.abs(threadMultiplier), 0.1); + public int getMaxConcurrency() { + return Math.max(Math.abs(maxConcurrency), 1); } public double getChunkLoadSensitivity() { @@ -242,6 +242,7 @@ public class IrisSettings { public String defaultWorldType = "overworld"; public int maxBiomeChildDepth = 4; public boolean preventLeafDecay = true; + public boolean useMulticore = false; } @Data @@ -255,6 +256,7 @@ public class IrisSettings { @Data public static class IrisSettingsEngineSVC { public boolean useVirtualThreads = true; + public boolean forceMulticoreWrite = false; public int priority = Thread.NORM_PRIORITY; public int getPriority() { diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index e96eb29ed..06b961c13 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -37,6 +37,7 @@ import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.io.IO; import com.volmit.iris.util.mantle.TectonicPlate; 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.MCAUtil; import com.volmit.iris.util.parallel.MultiBurst; @@ -53,6 +54,7 @@ import org.bukkit.World; import java.io.*; import java.net.InetAddress; import java.net.NetworkInterface; +import java.nio.file.Files; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -64,7 +66,6 @@ import java.util.zip.GZIPOutputStream; public class CommandDeveloper implements DecreeExecutor { private CommandTurboPregen turboPregen; private CommandLazyPregen lazyPregen; - private CommandUpdater updater; @Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true) public void EngineStatus() { @@ -79,6 +80,33 @@ public class CommandDeveloper implements DecreeExecutor { 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") public void dumpThreads() { 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") public void packBenchmark( @Param(description = "The pack to bench", aliases = {"pack"}, defaultValue = "overworld") diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java index 3f1c0b588..75448c8dc 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java @@ -21,7 +21,6 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; 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.tools.IrisToolbelt; 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.specialhandlers.NullablePlayerHandler; 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.misc.ServerProperties; 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") public class CommandIris implements DecreeExecutor { + private CommandUpdater updater; private CommandStudio studio; private CommandPregen pregen; private CommandSettings settings; @@ -318,24 +317,6 @@ public class CommandIris implements DecreeExecutor { 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") public void aura( @Param(description = "The h color value", defaultValue = "-20") diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java index 7c0bba906..d3509cb9e 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java @@ -181,17 +181,32 @@ public class CommandStudio implements DecreeExecutor { int rad = engine.getMantle().getRadius(); var mantle = engine.getMantle().getMantle(); var chunkMap = new KMap(); - for (int i = -(radius + rad); i <= radius + rad; i++) { - for (int j = -(radius + rad); j <= radius + rad; j++) { - int xx = i + x, zz = j + z; - if (Math.abs(i) <= radius && Math.abs(j) <= radius) { - mantle.deleteChunk(xx, zz); - continue; + ParallelQueueJob prep = new ParallelQueueJob<>() { + @Override + public void execute(Position2 pos) { + var cpos = pos.add(x, z); + if (Math.abs(pos.getX()) <= radius && Math.abs(pos.getZ()) <= radius) { + mantle.deleteChunk(cpos.getX(), cpos.getZ()); + return; } - chunkMap.put(new Position2(xx, zz), mantle.getChunk(xx, zz)); - mantle.deleteChunk(xx, zz); + chunkMap.put(cpos, mantle.getChunk(cpos.getX(), cpos.getZ())); + 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 job = new ParallelQueueJob<>() { @Override @@ -209,21 +224,24 @@ public class CommandStudio implements DecreeExecutor { job.queue(new Position2(i + x, j + z)); } } - CountDownLatch latch = new CountDownLatch(1); job.execute(sender(), latch::countDown); latch.await(); int sections = mantle.getWorldHeight() >> 4; chunkMap.forEach((pos, chunk) -> { - var c = mantle.getChunk(pos.getX(), pos.getZ()); - c.copyFlags(chunk); - c.clear(); - for (int y = 0; y < sections; y++) { - var slice = chunk.get(y); - if (slice == null) continue; - var s = c.getOrCreate(y); - slice.getSliceMap().forEach(s::putSlice); + var c = mantle.getChunk(pos.getX(), pos.getZ()).use(); + try { + c.copyFlags(chunk); + c.clear(); + for (int y = 0; y < sections; y++) { + var slice = chunk.get(y); + if (slice == null) continue; + var s = c.getOrCreate(y); + slice.getSliceMap().forEach(s::putSlice); + } + } finally { + c.release(); } }); } catch (Throwable e) { diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java b/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java index 38aba40a5..e19fc61ba 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandUpdater.java @@ -18,6 +18,7 @@ package com.volmit.iris.core.commands; +import lombok.Synchronized; import org.bukkit.World; 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") 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") public void start( @@ -43,19 +45,22 @@ public class CommandUpdater implements DecreeExecutor { sender().sendMessage(C.GOLD + "This is not an Iris world"); return; } - if (chunkUpdater != null) { - chunkUpdater.stop(); - } + synchronized (lock) { + if (chunkUpdater != null) { + chunkUpdater.stop(); + } - chunkUpdater = new ChunkUpdater(world); - if (sender().isPlayer()) { - sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); - } else { - Iris.info(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); + chunkUpdater = new ChunkUpdater(world); + if (sender().isPlayer()) { + sender().sendMessage(C.GREEN + "Updating " + world.getName() + C.GRAY + " Total chunks: " + Form.f(chunkUpdater.getChunks())); + } else { + 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") public void pause( ) { if (chunkUpdater == null) { @@ -78,6 +83,7 @@ public class CommandUpdater implements DecreeExecutor { } } + @Synchronized("lock") @Decree(description = "Stops the updater") public void stop() { if (chunkUpdater == null) { diff --git a/core/src/main/java/com/volmit/iris/core/gui/VisionGUI.java b/core/src/main/java/com/volmit/iris/core/gui/VisionGUI.java index 24cc09f73..32bc1f79d 100644 --- a/core/src/main/java/com/volmit/iris/core/gui/VisionGUI.java +++ b/core/src/main/java/com/volmit/iris/core/gui/VisionGUI.java @@ -30,7 +30,6 @@ import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; 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.math.BlockPosition; 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.PrecisionStopwatch; import org.bukkit.Location; -import org.bukkit.attribute.Attribute; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; @@ -415,8 +413,7 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener } private double getWorldX(double screenX) { - //return (mscale * screenX) + ((oxp / scale) * mscale); - return (mscale * screenX) + ((oxp / scale)); + return (mscale * screenX) + ((oxp / scale) * mscale); } private double getWorldZ(double screenZ) { @@ -486,6 +483,13 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener 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()) { J.s(() -> { synchronized (lastEntities) { @@ -510,8 +514,8 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener int iscale = (int) scale; g.setColor(Color.white); g.clearRect(0, 0, w, h); - int posX = (int) oxp; - int posZ = (int) ozp; + double offsetX = oxp / scale; + double offsetZ = ozp / scale; m.set(3); 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 b = j - (h / 2); 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) { - 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) { double cx = getWorldX(getWidth() / 2); double cz = getWorldZ(getHeight() / 2); - ox += (wx - cx); - oz += (wz - cz); + ox += ((wx - cx) / mscale) * scale; + oz += ((wz - cz) / mscale) * scale; } private void renderPosition(Graphics2D g, double x, double z) { @@ -807,11 +815,28 @@ public class VisionGUI extends JPanel implements MouseWheelListener, KeyListener 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(); 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 diff --git a/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java index f030fc6a8..5327578bd 100644 --- a/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/ItemAdderDataProvider.java @@ -4,24 +4,27 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.link.ExternalDataProvider; import com.volmit.iris.core.link.Identifier; 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.data.B; +import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.data.IrisCustomData; import dev.lone.itemsadder.api.CustomBlock; import dev.lone.itemsadder.api.CustomStack; +import dev.lone.itemsadder.api.Events.ItemsAdderLoadDataEvent; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; +import org.bukkit.event.EventHandler; import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.List; import java.util.MissingResourceException; +import java.util.stream.Collectors; public class ItemAdderDataProvider extends ExternalDataProvider { - private KList itemNamespaces, blockNamespaces; + private final KSet itemNamespaces = new KSet<>(); + private final KSet blockNamespaces = new KSet<>(); public ItemAdderDataProvider() { super("ItemsAdder"); @@ -29,16 +32,12 @@ public class ItemAdderDataProvider extends ExternalDataProvider { @Override public void init() { - this.itemNamespaces = new KList<>(); - this.blockNamespaces = new KList<>(); + updateNamespaces(); + } - for (Identifier i : getTypes(DataType.ITEM)) { - itemNamespaces.addIfMissing(i.namespace()); - } - for (Identifier i : getTypes(DataType.BLOCK)) { - blockNamespaces.addIfMissing(i.namespace()); - Iris.info("Found ItemAdder Block: " + i); - } + @EventHandler + public void onLoadData(ItemsAdderLoadDataEvent event) { + updateNamespaces(); } @NotNull @@ -48,7 +47,7 @@ public class ItemAdderDataProvider extends ExternalDataProvider { if (block == null) { throw new MissingResourceException("Failed to find BlockData!", blockId.namespace(), blockId.key()); } - return new IrisCustomData(B.getAir(), blockId); + return new IrisCustomData(block.getBaseBlockData(), blockId); } @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 public boolean isValidProvider(@NotNull Identifier id, DataType dataType) { 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()); } } diff --git a/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java b/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java index 998fbf06f..58baa6e59 100644 --- a/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java +++ b/core/src/main/java/com/volmit/iris/core/link/data/NexoDataProvider.java @@ -26,11 +26,8 @@ import org.jetbrains.annotations.NotNull; import java.util.Collection; import java.util.List; import java.util.MissingResourceException; -import java.util.concurrent.atomic.AtomicBoolean; public class NexoDataProvider extends ExternalDataProvider { - private final AtomicBoolean failed = new AtomicBoolean(false); - public NexoDataProvider() { super("Nexo"); } @@ -125,9 +122,4 @@ public class NexoDataProvider extends ExternalDataProvider { if (dataType == DataType.ENTITY) return false; return "nexo".equalsIgnoreCase(id.namespace()); } - - @Override - public boolean isReady() { - return super.isReady() && !failed.get(); - } } diff --git a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java index c09fc5ca8..5edd4ba9f 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java +++ b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java @@ -501,7 +501,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { } public void savePrefetch(Engine engine) { - BurstExecutor b = MultiBurst.burst.burst(loaders.size()); + BurstExecutor b = MultiBurst.ioBurst.burst(loaders.size()); for (ResourceLoader i : loaders.values()) { b.queue(() -> { @@ -518,7 +518,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { } public void loadPrefetch(Engine engine) { - BurstExecutor b = MultiBurst.burst.burst(loaders.size()); + BurstExecutor b = MultiBurst.ioBurst.burst(loaders.size()); for (ResourceLoader i : loaders.values()) { b.queue(() -> { diff --git a/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java b/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java index 0f804e4e2..25d37642d 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java +++ b/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java @@ -23,6 +23,7 @@ import com.volmit.iris.Iris; import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.project.SchemaBuilder; 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.MeteredCache; import com.volmit.iris.util.collection.KList; @@ -47,7 +48,6 @@ import java.io.*; import java.util.Arrays; import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -60,7 +60,7 @@ import java.util.zip.GZIPOutputStream; public class ResourceLoader implements MeteredCache { public static final AtomicDouble tlt = new AtomicDouble(0); private static final int CACHE_SIZE = 100000; - protected final AtomicReference> folderCache; + protected final AtomicCache> folderCache; protected KSet firstAccess; protected File root; protected String folderName; @@ -76,7 +76,7 @@ public class ResourceLoader implements MeteredCache { public ResourceLoader(File root, IrisData manager, String folderName, String resourceTypeName, Class objectClass) { this.manager = manager; firstAccess = new KSet<>(); - folderCache = new AtomicReference<>(); + folderCache = new AtomicCache<>(); sec = new ChronoLatch(5000); loads = new AtomicInteger(); this.objectClass = objectClass; @@ -240,7 +240,7 @@ public class ResourceLoader implements MeteredCache { public KList loadAllParallel(KList s) { KList m = new KList<>(); - BurstExecutor burst = MultiBurst.burst.burst(s.size()); + BurstExecutor burst = MultiBurst.ioBurst.burst(s.size()); for (String i : s) { burst.queue(() -> { @@ -365,29 +365,24 @@ public class ResourceLoader implements MeteredCache { } public KList getFolders() { - synchronized (folderCache) { - if (folderCache.get() == null) { - KList fc = new KList<>(); + return folderCache.aquire(() -> { + KList fc = new KList<>(); - File[] files = root.listFiles(); - if (files == null) { - throw new IllegalStateException("Failed to list files in " + root); - } + File[] files = root.listFiles(); + if (files == null) { + throw new IllegalStateException("Failed to list files in " + root); + } - for (File i : files) { - if (i.isDirectory()) { - if (i.getName().equals(folderName)) { - fc.add(i); - break; - } + for (File i : files) { + if (i.isDirectory()) { + if (i.getName().equals(folderName)) { + fc.add(i); + break; } } - - folderCache.set(fc); } - } - - return folderCache.get(); + return fc; + }); } public KList getFolders(String rc) { @@ -407,7 +402,7 @@ public class ResourceLoader implements MeteredCache { public void clearCache() { possibleKeys = null; loadCache.invalidate(); - folderCache.set(null); + folderCache.reset(); } public File fileFor(T b) { @@ -433,7 +428,7 @@ public class ResourceLoader implements MeteredCache { } public void clearList() { - folderCache.set(null); + folderCache.reset(); possibleKeys = null; } diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java index bb5bea83b..bce79233c 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java @@ -39,14 +39,14 @@ public class ChunkUpdater { private final AtomicInteger chunksUpdated = new AtomicInteger(); private final AtomicBoolean serverEmpty = new AtomicBoolean(true); 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 Semaphore semaphore = new Semaphore(256); - private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, 256, IrisSettings.get().getUpdater().emptyMsRange); + private final int maxConcurrency = IrisSettings.get().getUpdater().getMaxConcurrency(); + private final Semaphore semaphore = new Semaphore(maxConcurrency); + private final LoadBalancer loadBalancer = new LoadBalancer(semaphore, maxConcurrency, IrisSettings.get().getUpdater().emptyMsRange); private final AtomicLong startTime = new AtomicLong(); private final Dimensions dimensions; private final PregenTask task; - private final ExecutorService executor = Executors.newFixedThreadPool(coreLimit); - private final ExecutorService chunkExecutor = Executors.newFixedThreadPool(coreLimit); + private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor(); + private final ExecutorService chunkExecutor = Executors.newVirtualThreadPerTaskExecutor(); private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private final CountDownLatch latch; private final Engine engine; @@ -138,10 +138,10 @@ public class ChunkUpdater { loadBalancer.close(); semaphore.acquire(256); - executor.shutdown(); - executor.awaitTermination(5, TimeUnit.SECONDS); chunkExecutor.shutdown(); chunkExecutor.awaitTermination(5, TimeUnit.SECONDS); + executor.shutdown(); + executor.awaitTermination(5, TimeUnit.SECONDS); scheduler.shutdownNow(); unloadAndSaveAllChunks(); } catch (Exception ignored) {} diff --git a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java index 49303b60c..42d82d3ab 100644 --- a/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/ExternalDataSVC.java @@ -69,8 +69,8 @@ public class ExternalDataSVC implements IrisService { @EventHandler public void onPluginEnable(PluginEnableEvent e) { - if (activeProviders.stream().noneMatch(p -> p.getPlugin().equals(e.getPlugin()))) { - providers.stream().filter(p -> p.isReady() && p.getPlugin().equals(e.getPlugin())).findFirst().ifPresent(edp -> { + if (activeProviders.stream().noneMatch(p -> e.getPlugin().equals(p.getPlugin()))) { + providers.stream().filter(p -> p.isReady() && e.getPlugin().equals(p.getPlugin())).findFirst().ifPresent(edp -> { activeProviders.add(edp); edp.init(); Iris.instance.registerListener(edp); diff --git a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java index df3951ea1..a9b31244d 100644 --- a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java @@ -157,6 +157,7 @@ public class IrisEngineSVC implements IrisService { private final class Registered { private final String name; private final PlatformChunkGenerator access; + private final int offset = RNG.r.nextInt(1000); private transient ScheduledFuture trimmer; private transient ScheduledFuture unloader; private transient boolean closed; @@ -193,7 +194,7 @@ public class IrisEngineSVC implements IrisService { Iris.error("EngineSVC: Failed to trim for " + name); e.printStackTrace(); } - }, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS); + }, offset, 2000, TimeUnit.MILLISECONDS); } if (unloader == null || unloader.isDone() || unloader.isCancelled()) { @@ -204,7 +205,7 @@ public class IrisEngineSVC implements IrisService { try { 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) { 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); e.printStackTrace(); } - }, RNG.r.nextInt(1000), 1000, TimeUnit.MILLISECONDS); + }, offset + 1000, 2000, TimeUnit.MILLISECONDS); } } diff --git a/core/src/main/java/com/volmit/iris/core/service/PreservationSVC.java b/core/src/main/java/com/volmit/iris/core/service/PreservationSVC.java index b43e3ee95..317e1731e 100644 --- a/core/src/main/java/com/volmit/iris/core/service/PreservationSVC.java +++ b/core/src/main/java/com/volmit/iris/core/service/PreservationSVC.java @@ -43,10 +43,6 @@ public class PreservationSVC implements IrisService { threads.add(t); } - public void register(MultiBurst burst) { - - } - public void register(ExecutorService service) { services.add(service); } diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java index 1df252cfa..9026729e9 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -296,67 +296,63 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat try { Semaphore semaphore = new Semaphore(3); chunk.raiseFlag(MantleFlag.ETCHED, () -> { - chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> J.s(() -> { - mantle.iterateChunk(c.getX(), c.getZ(), TileWrapper.class, (x, y, z, v) -> { - int betterY = y + getWorld().minHeight(); - if (!TileData.setTileState(c.getBlock(x, betterY, z), 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()); + chunk.raiseFlag(MantleFlag.TILE, run(semaphore, () -> { + chunk.iterate(TileWrapper.class, (x, y, z, v) -> { + Block block = c.getBlock(x & 15, y + getWorld().minHeight(), z & 15); + if (!TileData.setTileState(block, v.getData())) + 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()); }); - }))); - chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> J.s(() -> { - mantle.iterateChunk(c.getX(), c.getZ(), Identifier.class, (x, y, z, v) -> { + }, 0)); + chunk.raiseFlag(MantleFlag.CUSTOM, run(semaphore, () -> { + chunk.iterate(Identifier.class, (x, y, z, 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(); - KMap updates = new KMap<>(); - RNG r = new RNG(Cache.key(c.getX(), c.getZ())); - mantle.iterateChunk(c.getX(), c.getZ(), MatterCavern.class, (x, yf, z, v) -> { + int[][] grid = new int[16][16]; + for (int x = 0; x < 16; x++) { + 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(); - 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; } - boolean u = false; - if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.DOWN).getBlockData())) { - u = true; - } else if (B.isAir(c.getBlock(x & 15, y, z & 15).getRelative(BlockFace.WEST).getBlockData())) { - u = true; - } 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; - } + boolean u = B.isAir(block.getRelative(BlockFace.DOWN).getBlockData()) + || B.isAir(block.getRelative(BlockFace.WEST).getBlockData()) + || B.isAir(block.getRelative(BlockFace.EAST).getBlockData()) + || B.isAir(block.getRelative(BlockFace.SOUTH).getBlockData()) + || B.isAir(block.getRelative(BlockFace.NORTH).getBlockData()); - if (u) { - updates.compute(Cache.key(x & 15, z & 15), (k, vv) -> { - if (vv != null) { - return Math.max(vv, y); - } - - return y; - }); - } + if (u) grid[x][z] = Math.max(grid[x][z], y); }); - updates.forEach((k, v) -> update(Cache.keyX(k), v, Cache.keyZ(k), c, r)); - mantle.iterateChunk(c.getX(), c.getZ(), MatterUpdate.class, (x, yf, z, v) -> { + for (int x = 0; x < 16; x++) { + 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(); if (v != null && v.isUpdate()) { - int vx = x & 15; - 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); - } + update(x, y, z, c, rng); } }); - mantle.deleteChunkSlice(c.getX(), c.getZ(), MatterUpdate.class); + chunk.deleteSlices(MatterUpdate.class); getMetrics().getUpdates().put(p.getMilliseconds()); - }, RNG.r.i(0, 20)))); + }, RNG.r.i(1, 20))); //Why is there a random delay here? }); 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 () -> { if (!semaphore.tryAcquire()) return; - try { - runnable.run(); - } finally { - semaphore.release(); - } + + J.s(() -> { + try { + 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 @Override diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java index 695bc8b16..c31478b15 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java @@ -26,31 +26,30 @@ import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.framework.EngineTarget; import com.volmit.iris.engine.mantle.components.MantleJigsawComponent; 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.IrisPosition; -import com.volmit.iris.engine.object.TileData; import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.context.ChunkContext; import com.volmit.iris.util.context.IrisContext; import com.volmit.iris.util.data.B; -import com.volmit.iris.util.data.IrisCustomData; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.MantleChunk; import com.volmit.iris.util.mantle.flag.MantleFlag; +import com.volmit.iris.util.math.Position2; import com.volmit.iris.util.matter.*; import com.volmit.iris.util.matter.slices.UpdateMatter; -import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.MultiBurst; import org.bukkit.block.data.BlockData; import java.util.concurrent.TimeUnit; -// TODO: MOVE PLACER OUT OF MATTER INTO ITS OWN THING -public interface EngineMantle extends IObjectPlacer { +import static com.volmit.iris.util.parallel.StreamUtils.forEach; +import static com.volmit.iris.util.parallel.StreamUtils.streamRadius; + +public interface EngineMantle { BlockData AIR = B.get("AIR"); Mantle getMantle(); @@ -87,12 +86,10 @@ public interface EngineMantle extends IObjectPlacer { return getHighest(x, z, getData(), ignoreFluid); } - @Override default int getHighest(int x, int z, IrisData data) { return getHighest(x, z, data, false); } - @Override default int getHighest(int x, int z, IrisData data, boolean ignoreFluid) { 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); } + @Deprecated(forRemoval = true) default boolean isCarved(int x, int h, int z) { return getMantle().get(x, h, z, MatterCavern.class) != null; } - @Override - 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 + @Deprecated(forRemoval = true) default BlockData get(int x, int y, int z) { BlockData block = getMantle().get(x, y, z, BlockData.class); if (block == null) @@ -126,27 +111,18 @@ public interface EngineMantle extends IObjectPlacer { return block; } - @Override default boolean isPreventingDecay() { 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) { return getHighest(x, z, true) <= getFluidHeight(); } - @Override default int getFluidHeight() { return getEngine().getDimension().getFluidHeight(); } - @Override default boolean isDebugSmartBore() { return getEngine().getDimension().isDebugSmartBore(); } @@ -196,7 +172,7 @@ public interface EngineMantle extends IObjectPlacer { @ChunkCoordinates 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; } @@ -206,30 +182,32 @@ public interface EngineMantle extends IObjectPlacer { var pair = iterator.next(); int radius = pair.getB(); boolean last = !iterator.hasNext(); - BurstExecutor burst = burst().burst(radius * 2 + 1); - burst.setMulticore(multicore); + forEach(streamRadius(x, z, radius), + 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++) { - for (int j = -radius; j <= radius; j++) { - int xx = x + i; - int zz = z + j; - 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(); + if (!last) continue; + forEach(streamRadius(x, z, radius), + p -> writer.acquireChunk(x, z).flag(MantleFlag.PLANNED, true), + multicore ? burst() : null + ); } } } default void generateMantleComponent(MantleWriter writer, int x, int z, MantleComponent c, MantleChunk mc, ChunkContext context) { - mc.raiseFlag(c.getFlag(), () -> { + mc.raiseFlag(MantleFlag.PLANNED, c.getFlag(), () -> { if (c.isEnabled()) c.generateLayer(writer, x, z, context); }); } @@ -240,7 +218,12 @@ public interface EngineMantle extends IObjectPlacer { 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 diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java index ee2c9bf2d..408704925 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/MantleWriter.java @@ -29,12 +29,16 @@ import com.volmit.iris.engine.object.IrisPosition; import com.volmit.iris.engine.object.TileData; import com.volmit.iris.util.collection.KMap; 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.documentation.ChunkCoordinates; import com.volmit.iris.util.function.Function3; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.mantle.MantleChunk; import com.volmit.iris.util.math.RNG; 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 lombok.Data; import org.bukkit.block.data.BlockData; @@ -44,6 +48,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import static com.volmit.iris.engine.mantle.EngineMantle.AIR; + @Data public class MantleWriter implements IObjectPlacer, AutoCloseable { private final EngineMantle engineMantle; @@ -144,20 +150,41 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { return; } - if (cx >= this.x - radius && cx <= this.x + radius - && cz >= this.z - radius && cz <= this.z + radius) { - MantleChunk chunk = cachedChunks.computeIfAbsent(Cache.key(cx, cz), k -> mantle.getChunk(cx, cz).use()); + MantleChunk chunk = acquireChunk(cx, cz); + if (chunk == null) return; - if (chunk == null) { - Iris.error("Mantle Writer Accessed " + cx + "," + cz + " and came up null (and yet within bounds!)"); - return; - } + Matter matter = chunk.getOrCreate(y >> 4); + matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t); + } - Matter matter = chunk.getOrCreate(y >> 4); - matter.slice(matter.getClass(t)).set(x & 15, y & 15, z & 15, t); - } else { - Iris.error("Mantle Writer Accessed chunk out of bounds" + cx + "," + cz); + public T getData(int x, int y, int z, Class type) { + int cx = x >> 4; + int cz = z >> 4; + + if (y < 0 || y >= mantle.getWorldHeight()) { + return null; } + + MantleChunk chunk = acquireChunk(cx, cz); + if (chunk == null) { + return null; + } + + return chunk.getOrCreate(y >> 4) + .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 @@ -180,7 +207,10 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { @Override 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 @@ -190,12 +220,12 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { @Override public boolean isCarved(int x, int y, int z) { - return getEngineMantle().isCarved(x, y, z); + return getData(x, y, z, MatterCavern.class) != null; } @Override public boolean isSolid(int x, int y, int z) { - return getEngineMantle().isSolid(x, y, z); + return B.isSolid(get(x, y, z)); } @Override @@ -215,7 +245,7 @@ public class MantleWriter implements IObjectPlacer, AutoCloseable { @Override 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 diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java index 22d31800e..8f59ae93d 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/components/MantleJigsawComponent.java @@ -47,7 +47,7 @@ public class MantleJigsawComponent extends IrisMantleComponent { private final CNG cng; public MantleJigsawComponent(EngineMantle engineMantle) { - super(engineMantle, ReservedFlag.JIGSAW, 1); + super(engineMantle, ReservedFlag.JIGSAW, 2); cng = NoiseStyle.STATIC.create(new RNG(jigsaw())); } @@ -180,6 +180,10 @@ public class MantleJigsawComponent extends IrisMantleComponent { var dimension = getDimension(); KSet structures = new KSet<>(); + if (dimension.getStronghold() != null) { + structures.add(dimension.getStronghold()); + } + for (var placement : dimension.getJigsawStructures()) { structures.add(placement.getStructure()); } diff --git a/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java b/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java index a7bb391be..d8585fe93 100644 --- a/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java +++ b/core/src/main/java/com/volmit/iris/engine/modifier/IrisCarveModifier.java @@ -27,6 +27,7 @@ import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.context.ChunkContext; 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.hunk.Hunk; import com.volmit.iris.util.mantle.Mantle; @@ -53,10 +54,11 @@ public class IrisCarveModifier extends EngineAssignedModifier { } @Override + @ChunkCoordinates public void onModify(int x, int z, Hunk output, boolean multicore, ChunkContext context) { PrecisionStopwatch p = PrecisionStopwatch.start(); Mantle mantle = getEngine().getMantle().getMantle(); - MantleChunk mc = getEngine().getMantle().getMantle().getChunk(x, z).use(); + MantleChunk mc = mantle.getChunk(x, z).use(); KMap> positions = new KMap<>(); KMap walls = new KMap<>(); Consumer4 iterator = (xx, yy, zz, c) -> { @@ -81,19 +83,19 @@ public class IrisCarveModifier extends EngineAssignedModifier { //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); } - 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); } - 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); } - 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); } @@ -218,11 +220,15 @@ public class IrisCarveModifier extends EngineAssignedModifier { 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; } - 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; } diff --git a/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java b/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java index d998088b2..1e7b8ae23 100644 --- a/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java +++ b/core/src/main/java/com/volmit/iris/engine/modifier/IrisDepositModifier.java @@ -28,6 +28,7 @@ import com.volmit.iris.util.hunk.Hunk; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import org.bukkit.Material; import org.bukkit.block.data.BlockData; import org.bukkit.util.BlockVector; @@ -122,6 +123,9 @@ public class IrisDepositModifier extends EngineAssignedModifier { if (ny > height || nx > 15 || nx < 0 || ny > getEngine().getHeight() || ny < 0 || nz < 0 || nz > 15) { continue; } + if (!k.isReplaceBedrock() && data.get(nx, ny, nz).getMaterial() == Material.BEDROCK) { + continue; + } 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))); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java b/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java index ac1390d83..42caee79e 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisBiome.java @@ -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)") private IrisBiomePaletteLayer wall = new IrisBiomePaletteLayer().zero(); @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.") private KList layers = new KList().qadd(new IrisBiomePaletteLayer()); @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.") private KList caveCeilingLayers = new KList().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.") private KList seaLayers = new KList<>(); @ArrayType(min = 1, type = IrisDecorator.class) diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCommand.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCommand.java index 36f47fb44..0555bb6bb 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCommand.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCommand.java @@ -21,6 +21,7 @@ package com.volmit.iris.engine.object; import com.volmit.iris.Iris; import com.volmit.iris.engine.object.annotations.ArrayType; 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.util.collection.KList; import lombok.Data; @@ -37,6 +38,7 @@ import org.bukkit.World; @Data public class IrisCommand { + @Required @ArrayType(min = 1, type = String.class) @Desc("List of commands. Iris replaces {x} {y} and {z} with the location of the entity spawn") private KList commands = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisCommandRegistry.java b/core/src/main/java/com/volmit/iris/engine/object/IrisCommandRegistry.java index e9599d56f..39f36c3b5 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisCommandRegistry.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisCommandRegistry.java @@ -33,6 +33,7 @@ import org.bukkit.entity.Player; @Desc("Represents a casting location for a command") @Data public class IrisCommandRegistry { + @Required @ArrayType(min = 1, type = IrisCommand.class) @Desc("Run commands, at the exact location of the player") private KList rawCommands = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java b/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java index 19623ca39..b1e1c609b 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisDepositGenerator.java @@ -87,6 +87,8 @@ public class IrisDepositGenerator { @MaxNumber(64) @Desc("Ore varience is how many different objects clumps iris will create") 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) { KList objects = this.objects.aquire(() -> diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisEffect.java b/core/src/main/java/com/volmit/iris/engine/object/IrisEffect.java index c28467877..d2c6eca6b 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisEffect.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisEffect.java @@ -146,7 +146,6 @@ public class IrisEffect { @MinNumber(1) @Desc("The chance is 1 in CHANCE per interval") private int chance = 50; - @ArrayType(min = 1, type = IrisCommandRegistry.class) @Desc("Run commands, with configurable location parameters") private IrisCommandRegistry commandRegistry = null; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java index c69aca0e4..09d94a6d4 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisJigsawPiece.java @@ -50,8 +50,7 @@ public class IrisJigsawPiece extends IrisRegistrant { @Desc("The object this piece represents") private String object = ""; - @Required - @ArrayType(type = IrisJigsawPieceConnector.class, min = 1) + @ArrayType(type = IrisJigsawPieceConnector.class) @Desc("The connectors this object contains") private KList connectors = new KList<>(); diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java index 0338ab28d..210278bda 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisObject.java @@ -514,9 +514,9 @@ public class IrisObject extends IrisRegistrant { 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); - h = max.getBlockY() - min.getBlockY() + (min.getBlockY() <= 0 && max.getBlockY() >= 0 && min.getBlockY() != max.getBlockY() ? 1 : 0); - d = max.getBlockZ() - min.getBlockZ() + (min.getBlockZ() <= 0 && max.getBlockZ() >= 0 && min.getBlockZ() != max.getBlockZ() ? 1 : 0); + w = max.getBlockX() - min.getBlockX() + 1; + h = max.getBlockY() - min.getBlockY() + 1; + d = max.getBlockZ() - min.getBlockZ() + 1; center = new BlockVector(w / 2, h / 2, d / 2); } diff --git a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java index 1f7fbd9a6..f6c0cbb2c 100644 --- a/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java +++ b/core/src/main/java/com/volmit/iris/engine/platform/BukkitChunkGenerator.java @@ -19,6 +19,7 @@ package com.volmit.iris.engine.platform; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisWorlds; import com.volmit.iris.core.loader.IrisData; 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.util.collection.KList; 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.ChunkDataHunkHolder; import com.volmit.iris.util.io.ReactiveFolder; @@ -46,8 +46,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Setter; import org.bukkit.*; -import org.bukkit.block.Biome; -import org.bukkit.block.data.BlockData; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; @@ -206,7 +204,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun IrisBiomeStorage st = new IrisBiomeStorage(); TerrainChunk tc = TerrainChunk.createUnsafe(world, st); 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) .thenApply(d -> { @@ -367,7 +365,7 @@ public class BukkitChunkGenerator extends ChunkGenerator implements PlatformChun } else { ChunkDataHunkHolder blocks = new ChunkDataHunkHolder(tc); 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(); biomes.apply(); } diff --git a/core/src/main/java/com/volmit/iris/util/context/IrisContext.java b/core/src/main/java/com/volmit/iris/util/context/IrisContext.java index 73bae0e5e..a6b72cf9f 100644 --- a/core/src/main/java/com/volmit/iris/util/context/IrisContext.java +++ b/core/src/main/java/com/volmit/iris/util/context/IrisContext.java @@ -29,7 +29,7 @@ import lombok.Data; @Data public class IrisContext { private static final KMap context = new KMap<>(); - private static ChronoLatch cl = new ChronoLatch(60000); + private static final ChronoLatch cl = new ChronoLatch(60000); private final Engine engine; private ChunkContext chunkContext; @@ -53,9 +53,10 @@ public class IrisContext { } 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()) { dereference(); } @@ -63,15 +64,13 @@ public class IrisContext { } public static void dereference() { - synchronized (context) { - for (Thread i : context.k()) { - if (!i.isAlive() || context.get(i).engine.isClosed()) { - if (context.get(i).engine.isClosed()) { - Iris.debug("Dereferenced Context " + i.getName() + " " + i.getId()); - } - - context.remove(i); + for (Thread i : context.k()) { + if (!i.isAlive() || context.get(i).engine.isClosed()) { + if (context.get(i).engine.isClosed()) { + Iris.debug("Dereferenced Context " + i.getName() + " " + i.threadId()); } + + context.remove(i); } } } diff --git a/core/src/main/java/com/volmit/iris/util/data/B.java b/core/src/main/java/com/volmit/iris/util/data/B.java index 7e71d39f3..1bf8edb2f 100644 --- a/core/src/main/java/com/volmit/iris/util/data/B.java +++ b/core/src/main/java/com/volmit/iris/util/data/B.java @@ -610,8 +610,7 @@ public class B { } public static boolean isUpdatable(BlockData mat) { - return isLit(mat) - || isStorage(mat) + return isStorage(mat) || (mat instanceof PointedDripstone && ((PointedDripstone) mat).getThickness().equals(PointedDripstone.Thickness.TIP)); } diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/DataBits.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/DataBits.java index 2afc0c3b4..4ed18fa2d 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/DataBits.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/DataBits.java @@ -19,12 +19,12 @@ package com.volmit.iris.util.hunk.bits; import com.volmit.iris.util.data.Varint; +import lombok.Getter; import org.apache.commons.lang3.Validate; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLongArray; import java.util.function.IntConsumer; @@ -52,8 +52,10 @@ public class DataBits { 0, 5}; private final AtomicLongArray data; + @Getter private final int bits; private final long mask; + @Getter private final int size; private final int valuesPerLong; private final int divideMul; @@ -149,18 +151,9 @@ public class DataBits { return data; } - public int getSize() { - return size; - } - - public int getBits() { - return bits; - } - public DataBits setBits(int newBits) { if (bits != newBits) { DataBits newData = new DataBits(newBits, size); - AtomicInteger c = new AtomicInteger(0); for (int i = 0; i < size; i++) { newData.set(i, get(i)); diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/DataContainer.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/DataContainer.java index 44d25a1e7..ab672ca76 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/DataContainer.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/DataContainer.java @@ -19,78 +19,32 @@ package com.volmit.iris.util.hunk.bits; import com.volmit.iris.util.data.Varint; +import lombok.Synchronized; import java.io.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; public class DataContainer { protected static final int INITIAL_BITS = 3; 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[] BIT = computeBitLimits(); - private final AtomicReference> palette; - private final AtomicReference data; - private final AtomicInteger bits; + private volatile Palette palette; + private volatile DataBits data; private final int length; private final Writable writer; public DataContainer(Writable writer, int length) { this.writer = writer; this.length = length; - this.bits = new AtomicInteger(INITIAL_BITS); - this.data = new AtomicReference<>(new DataBits(INITIAL_BITS, length)); - this.palette = new AtomicReference<>(newPalette(INITIAL_BITS)); + this.data = new DataBits(INITIAL_BITS, length); + this.palette = newPalette(INITIAL_BITS); } public DataContainer(DataInputStream din, Writable writer) throws IOException { this.writer = writer; this.length = Varint.readUnsignedVarInt(din); - this.palette = new AtomicReference<>(newPalette(din)); - this.data = new AtomicReference<>(new DataBits(palette.get().bits(), length, din)); - this.bits = new AtomicInteger(palette.get().bits()); - } - - public static String readBitString(DataInputStream din) throws IOException { - DataContainer c = new DataContainer<>(din, new Writable() { - @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 c = new DataContainer<>(new Writable() { - @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); + this.palette = newPalette(din); + this.data = new DataBits(palette.bits(), length, din); } private static int[] computeBitLimits() { @@ -117,17 +71,9 @@ public class DataContainer { return DataContainer.BIT.length - 1; } - public DataBits getData() { - return data.get(); - } - - public Palette getPalette() { - return palette.get(); - } - public String toString() { - return "DataContainer <" + length + " x " + bits + " bits> -> Palette<" + palette.get().getClass().getSimpleName().replaceAll("\\QPalette\\E", "") + ">: " + palette.get().size() + - " " + data.get().toString() + " PalBit: " + palette.get().bits(); + return "DataContainer <" + length + " x " + data.getBits() + " bits> -> Palette<" + palette.getClass().getSimpleName().replaceAll("\\QPalette\\E", "") + ">: " + palette.size() + + " " + data.toString() + " PalBit: " + palette.bits(); } public byte[] write() throws IOException { @@ -140,11 +86,12 @@ public class DataContainer { writeDos(new DataOutputStream(out)); } + @Synchronized public void writeDos(DataOutputStream dos) throws IOException { Varint.writeUnsignedVarInt(length, dos); - Varint.writeUnsignedVarInt(palette.get().size(), dos); - palette.get().iterateIO((data, __) -> writer.writeNodeData(dos, data)); - data.get().write(dos); + Varint.writeUnsignedVarInt(palette.size(), dos); + palette.iterateIO((data, __) -> writer.writeNodeData(dos, data)); + data.write(dos); dos.flush(); } @@ -163,55 +110,44 @@ public class DataContainer { return new HashPalette<>(); } - public void ensurePaletted(T t) { - if (palette.get().id(t) == -1) { - expandOne(); - } - } - + @Synchronized public void set(int position, T t) { - synchronized (this) { - int id = palette.get().id(t); + int id = palette.id(t); - if (id == -1) { - expandOne(); - id = palette.get().add(t); - } - - data.get().set(position, id); + if (id == -1) { + id = palette.add(t); + updateBits(); } + + data.set(position, id); } - private void expandOne() { - if (palette.get().size() + 1 >= BIT[bits.get()]) { - setBits(bits.get() + 1); + @Synchronized + @SuppressWarnings("NonAtomicOperationOnVolatileField") + 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) { - synchronized (this) { - int id = data.get().get(position) + 1; + int id = data.get(position); - if (id <= 0) { - return null; - } - - return palette.get().get(id - 1); + if (id <= 0) { + return null; } - } - public void setBits(int bits) { - if (this.bits.get() != bits) { - if (this.bits.get() <= LINEAR_BITS_LIMIT != bits <= LINEAR_BITS_LIMIT) { - palette.set(newPalette(bits).from(palette.get())); - } - - this.bits.set(bits); - data.set(data.get().setBits(bits)); - } + return palette.get(id); } public int size() { - return getData().getSize(); + return data.getSize(); } } diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java index c35c02acc..134425f6f 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java @@ -21,26 +21,25 @@ package com.volmit.iris.util.hunk.bits; import com.volmit.iris.util.collection.KMap; import com.volmit.iris.util.function.Consumer2; +import java.io.DataInputStream; +import java.io.IOException; import java.util.LinkedHashMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantLock; public class HashPalette implements Palette { - private final ReentrantLock lock = new ReentrantLock(); private final LinkedHashMap palette; private final KMap lookup; private final AtomicInteger size; public HashPalette() { - this.size = new AtomicInteger(0); + this.size = new AtomicInteger(1); this.palette = new LinkedHashMap<>(); this.lookup = new KMap<>(); - add(null); } @Override public T get(int id) { - if (id < 0 || id >= size.get()) { + if (id <= 0 || id >= size.get()) { return null; } @@ -49,17 +48,16 @@ public class HashPalette implements Palette { @Override public int add(T t) { - lock.lock(); - try { - int index = size.getAndIncrement(); - palette.put(t, index); + if (t == null) { + return 0; + } - if (t != null) { + synchronized (palette) { + return palette.computeIfAbsent(t, $ -> { + int index = size.getAndIncrement(); lookup.put(index, t); - } - return index; - } finally { - lock.unlock(); + return index; + }); } } @@ -80,17 +78,33 @@ public class HashPalette implements Palette { @Override public void iterate(Consumer2 c) { - lock.lock(); - try { - for (T i : palette.keySet()) { - if (i == null) { - continue; - } - - c.accept(i, id(i)); + synchronized (palette) { + for (int i = 1; i < size.get(); i++) { + c.accept(lookup.get(i), i); } - } finally { - lock.unlock(); } } + + @Override + public Palette from(Palette 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 from(int size, Writable 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; + } } diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/LinearPalette.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/LinearPalette.java index f83183384..5e2c1d43e 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/LinearPalette.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/LinearPalette.java @@ -19,19 +19,19 @@ package com.volmit.iris.util.hunk.bits; import com.volmit.iris.util.function.Consumer2; +import lombok.Synchronized; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReferenceArray; public class LinearPalette implements Palette { - private final AtomicReference> palette; + private volatile AtomicReferenceArray palette; private final AtomicInteger size; public LinearPalette(int initialSize) { - this.size = new AtomicInteger(0); - this.palette = new AtomicReference<>(new AtomicReferenceArray<>(initialSize)); - palette.get().set(size.getAndIncrement(), null); + this.size = new AtomicInteger(1); + this.palette = new AtomicReferenceArray<>(initialSize); + palette.set(0, null); } @Override @@ -40,26 +40,29 @@ public class LinearPalette implements Palette { return null; } - return palette.get().get(id); + return palette.get(id); } @Override public int add(T t) { + if (t == null) { + return 0; + } int index = size.getAndIncrement(); grow(index + 1); - palette.get().set(index, t); + palette.set(index, t); return index; } - private void grow(int newLength) { - if (newLength > palette.get().length()) { - AtomicReferenceArray a = new AtomicReferenceArray<>(newLength + size.get()); + private synchronized void grow(int newLength) { + if (newLength > palette.length()) { + AtomicReferenceArray a = new AtomicReferenceArray<>(newLength); - for (int i = 0; i < palette.get().length(); i++) { - a.set(i, palette.get().get(i)); + for (int i = 0; i < palette.length(); i++) { + a.set(i, palette.get(i)); } - palette.set(a); + palette = a; } } @@ -69,8 +72,8 @@ public class LinearPalette implements Palette { return 0; } - for (int i = 1; i < size() + 1; i++) { - if (t.equals(palette.get().get(i))) { + for (int i = 1; i < size.get(); i++) { + if (t.equals(palette.get(i))) { return i; } } @@ -85,8 +88,8 @@ public class LinearPalette implements Palette { @Override public void iterate(Consumer2 c) { - for (int i = 1; i < size() + 1; i++) { - c.accept(palette.get().get(i), i); + for (int i = 1; i <= size(); i++) { + c.accept(palette.get(i), i); } } } diff --git a/core/src/main/java/com/volmit/iris/util/io/CountingDataInputStream.java b/core/src/main/java/com/volmit/iris/util/io/CountingDataInputStream.java index 6239518b1..65fe8a36d 100644 --- a/core/src/main/java/com/volmit/iris/util/io/CountingDataInputStream.java +++ b/core/src/main/java/com/volmit/iris/util/io/CountingDataInputStream.java @@ -44,14 +44,14 @@ public class CountingDataInputStream extends DataInputStream { } @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); - count(i); + if (i != -1) count(i); return i; } private void count(int i) { - count += i; + count = Math.addExact(count, i); if (mark == -1) return; @@ -69,6 +69,12 @@ public class CountingDataInputStream extends DataInputStream { public synchronized void mark(int readlimit) { if (!in.markSupported()) return; in.mark(readlimit); + if (readlimit <= 0) { + mark = -1; + markLimit = 0; + return; + } + mark = count; markLimit = readlimit; } diff --git a/core/src/main/java/com/volmit/iris/util/io/IO.java b/core/src/main/java/com/volmit/iris/util/io/IO.java index 3c3504be1..7506c5651 100644 --- a/core/src/main/java/com/volmit/iris/util/io/IO.java +++ b/core/src/main/java/com/volmit/iris/util/io/IO.java @@ -24,6 +24,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.volmit.iris.Iris; 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.IOFunction; import lombok.SneakyThrows; @@ -34,10 +35,15 @@ import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; 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.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; +import java.nio.file.StandardOpenOption; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -1692,13 +1698,24 @@ public class IO { dir.mkdirs(); dir.deleteOnExit(); 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))) { action.accept(out); } - Files.move(temp.toPath(), file.toPath(), StandardCopyOption.REPLACE_EXISTING); + Files.copy(temp.toPath(), Channels.newOutputStream(target)); } finally { temp.delete(); } } + + public static FileLock lock(FileChannel channel) throws IOException { + while (true) { + try { + return channel.lock(); + } catch (OverlappingFileLockException e) {} + J.sleep(1); + } + } } diff --git a/core/src/main/java/com/volmit/iris/util/io/JarScanner.java b/core/src/main/java/com/volmit/iris/util/io/JarScanner.java index d7dcd31e7..cf8aa3271 100644 --- a/core/src/main/java/com/volmit/iris/util/io/JarScanner.java +++ b/core/src/main/java/com/volmit/iris/util/io/JarScanner.java @@ -83,6 +83,28 @@ public class JarScanner { 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 * diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 86486fb19..f795d4f14 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -35,6 +35,7 @@ import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.function.Consumer4; 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.math.M; import com.volmit.iris.util.matter.Matter; @@ -45,9 +46,7 @@ import com.volmit.iris.util.parallel.MultiBurst; import lombok.Getter; import org.bukkit.Chunk; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -70,6 +69,7 @@ public class Mantle { private final MultiBurst ioBurst; private final Semaphore ioTrim; private final Semaphore ioTectonicUnload; + private final IOWorker worker; private final AtomicDouble adjustedIdleDuration; private final KSet toUnload; @@ -89,9 +89,10 @@ public class Mantle { this.ioTectonicUnload = new Semaphore(LOCK_SIZE, true); loadedRegions = new KMap<>(); lastUse = new KMap<>(); - ioBurst = MultiBurst.burst; + ioBurst = MultiBurst.ioBurst; adjustedIdleDuration = new AtomicDouble(0); toUnload = new KSet<>(); + worker = new IOWorker(dataFolder, worldHeight); Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath()); } @@ -380,7 +381,7 @@ public class Mantle { loadedRegions.forEach((i, plate) -> b.queue(() -> { try { plate.close(); - plate.write(fileForRegion(dataFolder, i, false)); + worker.write(fileForRegion(dataFolder, i, false).getName(), plate); oldFileForRegion(dataFolder, i).delete(); } catch (Throwable e) { 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) { Iris.reportError(e); } + try { + worker.close(); + } catch (Throwable e) { + Iris.reportError(e); + } IO.delete(new File(dataFolder, ".tmp")); Iris.debug("The Mantle has Closed " + C.DARK_AQUA + dataFolder.getAbsolutePath()); @@ -463,8 +469,8 @@ public class Mantle { ioTectonicUnload.acquireUninterruptibly(LOCK_SIZE); try { + double unloadTime = M.ms() - adjustedIdleDuration.get(); for (long id : toUnload) { - double unloadTime = M.ms() - adjustedIdleDuration.get(); burst.queue(() -> hyperLock.withLong(id, () -> { TectonicPlate m = loadedRegions.get(id); if (m == null) { @@ -485,14 +491,15 @@ public class Mantle { } try { - m.write(fileForRegion(dataFolder, id, false)); + m.close(); + worker.write(fileForRegion(dataFolder, id, false).getName(), m); oldFileForRegion(dataFolder, id).delete(); loadedRegions.remove(id, m); lastUse.remove(id); toUnload.remove(id); i.incrementAndGet(); Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); - } catch (IOException e) { + } catch (IOException | InterruptedException e) { Iris.reportError(e); } })); @@ -524,30 +531,31 @@ public class Mantle { try { if (!trim || !unload) { try { - return getSafe(x, z).get(); - } catch (InterruptedException e) { - e.printStackTrace(); - } catch (ExecutionException e) { + return getSafe(x, z); + } catch (Throwable e) { 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 { - return getSafe(x, z).get(); + return getSafe(x, z); } catch (InterruptedException e) { Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread intterruption (hotload?)"); Iris.reportError(e); } catch (ExecutionException e) { Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a thread execution exception (engine close?)"); Iris.reportError(e); + } catch (Throwable e) { + Iris.warn("Failed to get Tectonic Plate " + x + " " + z + " Due to a unknown exception"); + Iris.reportError(e); } } finally { if (trim) ioTrim.release(); @@ -567,50 +575,52 @@ public class Mantle { * @return the future of a tectonic plate. */ @RegionCoordinates - private Future getSafe(int x, int z) { - return ioBurst.completeValue(() -> hyperLock.withResult(x, z, () -> { + private TectonicPlate getSafe(int x, int z) throws Throwable { + return hyperLock.withNastyResult(x, z, () -> { Long k = key(x, z); use(k); - TectonicPlate region = loadedRegions.get(k); - - if (region != null && !region.isClosed()) { - return region; + TectonicPlate r = loadedRegions.get(k); + if (r != null && !r.isClosed()) { + return r; } - File file = fileForRegion(dataFolder, x, z); - if (file.exists()) { - try { - Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath()); - region = TectonicPlate.read(worldHeight, file, file.getName().startsWith("pv.")); + return ioBurst.completeValue(() -> { + TectonicPlate region; + File file = fileForRegion(dataFolder, x, z); + if (file.exists()) { + try { + Iris.addPanic("reading.tectonic-plate", file.getAbsolutePath()); + region = worker.read(file.getName()); - 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); + 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); + } + + 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); - 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); + use(k); + return region; } + 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; - } - - 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; - })); + }).get(); + }); } private void use(Long key) { diff --git a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java index ff69c9d9f..78dcf2434 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/MantleChunk.java @@ -21,6 +21,7 @@ package com.volmit.iris.util.mantle; import com.volmit.iris.Iris; import com.volmit.iris.util.data.Varint; import com.volmit.iris.util.documentation.ChunkCoordinates; +import com.volmit.iris.util.documentation.ChunkRelativeBlockCoordinates; import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.io.CountingDataInputStream; import com.volmit.iris.util.mantle.flag.MantleFlag; @@ -30,6 +31,7 @@ import com.volmit.iris.util.matter.MatterSlice; import com.volmit.iris.util.parallel.AtomicBooleanArray; import lombok.Getter; import lombok.SneakyThrows; +import org.jetbrains.annotations.Nullable; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -48,6 +50,7 @@ public class MantleChunk { @Getter private final int z; private final AtomicBooleanArray flags; + private final Object[] flagLocks; private final AtomicReferenceArray sections; private final Semaphore ref = new Semaphore(Integer.MAX_VALUE, true); private final AtomicBoolean closed = new AtomicBoolean(false); @@ -61,8 +64,13 @@ public class MantleChunk { public MantleChunk(int sectionHeight, int x, int z) { sections = new AtomicReferenceArray<>(sectionHeight); flags = new AtomicBooleanArray(MantleFlag.MAX_ORDINAL + 1); + flagLocks = new Object[flags.length()]; this.x = x; this.z = z; + + for (int i = 0; i < flags.length(); i++) { + flagLocks[i] = new Object(); + } } /** @@ -116,6 +124,9 @@ public class MantleChunk { din.skipTo(end); 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) { - synchronized (this) { - if (!isFlagged(flag)) flag(flag, true); - else return; + raiseFlag(null, flag, r); + } + + public void raiseFlag(@Nullable MantleFlag guard, MantleFlag flag, Runnable r) { + if (closed.get()) throw new IllegalStateException("Chunk is closed!"); + if (guard != null && isFlagged(guard)) return; + synchronized (flagLocks[flag.ordinal()]) { + if (flags.compareAndSet(flag.ordinal(), false, true)) { + r.run(); + } } - r.run(); } public boolean isFlagged(MantleFlag flag) { @@ -191,6 +208,15 @@ public class MantleChunk { return sections.get(section); } + @Nullable + @ChunkRelativeBlockCoordinates + @SuppressWarnings("unchecked") + public T get(int x, int y, int z, Class type) { + return (T) getOrCreate(y >> 4) + .slice(type) + .get(x & 15, y & 15, z & 15); + } + /** * Clear all matter from this chunk */ @@ -222,7 +248,9 @@ public class MantleChunk { if (matter == null) { matter = new IrisMatter(16, 16, 16); - sections.set(section, matter); + if (!sections.compareAndSet(section, null, matter)) { + matter = get(section); + } } return matter; diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index aed5ae18c..1b9ea817e 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -19,26 +19,14 @@ package com.volmit.iris.util.mantle; import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.util.data.Varint; 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.IO; -import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Getter; -import net.jpountz.lz4.LZ4BlockInputStream; -import net.jpountz.lz4.LZ4BlockOutputStream; 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.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() { for (int i = 0; i < chunks.length(); i++) { MantleChunk chunk = chunks.get(i); @@ -223,18 +186,6 @@ public class TectonicPlate { 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 * @@ -268,4 +219,12 @@ public class TectonicPlate { public static void addError() { errors.set(true); } + + public static boolean hasError() { + try { + return errors.get(); + } finally { + errors.remove(); + } + } } diff --git a/core/src/main/java/com/volmit/iris/util/mantle/io/DelegateStream.java b/core/src/main/java/com/volmit/iris/util/mantle/io/DelegateStream.java new file mode 100644 index 000000000..2ccbbbe14 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/io/DelegateStream.java @@ -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 . + */ + +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); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/io/Holder.java b/core/src/main/java/com/volmit/iris/util/mantle/io/Holder.java new file mode 100644 index 000000000..fa36ce9c5 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/io/Holder.java @@ -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 . + */ + +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(); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/io/IOWorker.java b/core/src/main/java/com/volmit/iris/util/mantle/io/IOWorker.java new file mode 100644 index 000000000..d2edb4995 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/io/IOWorker.java @@ -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 . + */ + +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 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 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)); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/util/mantle/io/SynchronizedChannel.java b/core/src/main/java/com/volmit/iris/util/mantle/io/SynchronizedChannel.java new file mode 100644 index 000000000..a26d02967 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/mantle/io/SynchronizedChannel.java @@ -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 . + */ + +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(); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/matter/IrisMatter.java b/core/src/main/java/com/volmit/iris/util/matter/IrisMatter.java index d645df076..233a9173e 100644 --- a/core/src/main/java/com/volmit/iris/util/matter/IrisMatter.java +++ b/core/src/main/java/com/volmit/iris/util/matter/IrisMatter.java @@ -25,6 +25,8 @@ import com.volmit.iris.util.json.JSONObject; import com.volmit.iris.util.plugin.VolmitSender; import lombok.Getter; +import java.util.Objects; + public class IrisMatter extends IrisRegistrant implements Matter { protected static final KMap, MatterSlice> slicers = buildSlicers(); @@ -65,6 +67,12 @@ public class IrisMatter extends IrisRegistrant implements Matter { return c; } + @Override + @SuppressWarnings("unchecked") + public MatterSlice slice(Class c) { + return (MatterSlice) sliceMap.computeIfAbsent(c, $ -> Objects.requireNonNull(createSlice(c, this), "Bad slice " + c.getCanonicalName())); + } + @Override public MatterSlice createSlice(Class type, Matter m) { MatterSlice slice = slicers.get(type); diff --git a/core/src/main/java/com/volmit/iris/util/matter/Matter.java b/core/src/main/java/com/volmit/iris/util/matter/Matter.java index 086b397bd..0fe0dca0c 100644 --- a/core/src/main/java/com/volmit/iris/util/matter/Matter.java +++ b/core/src/main/java/com/volmit/iris/util/matter/Matter.java @@ -99,10 +99,9 @@ public interface Matter { } static Matter read(File f) throws IOException { - FileInputStream in = new FileInputStream(f); - Matter m = read(in); - in.close(); - return m; + try (var in = new FileInputStream(f)) { + return read(in); + } } static Matter read(InputStream in) throws IOException { @@ -142,6 +141,7 @@ public interface Matter { long size = din.readInt(); if (size == 0) continue; long start = din.count(); + long end = start + size; Iris.addPanic("read.matter.slice", i + ""); try { @@ -151,9 +151,9 @@ public interface Matter { Class type = Class.forName(cn); MatterSlice slice = matter.createSlice(type, matter); slice.read(din); + if (din.count() < end) throw new IOException("Matter slice read size mismatch!"); matter.putSlice(type, slice); } catch (Throwable e) { - long end = start + size; if (!(e instanceof ClassNotFoundException)) { Iris.error("Failed to read matter slice, skipping it."); Iris.addPanic("read.byte.range", start + " " + end); @@ -165,6 +165,10 @@ public interface Matter { } din.skipTo(end); } + + if (din.count() != end) { + throw new IOException("Matter slice read size mismatch!"); + } } return matter; diff --git a/core/src/main/java/com/volmit/iris/util/misc/Bindings.java b/core/src/main/java/com/volmit/iris/util/misc/Bindings.java index 6dea9f4a8..1a82c55c4 100644 --- a/core/src/main/java/com/volmit/iris/util/misc/Bindings.java +++ b/core/src/main/java/com/volmit/iris/util/misc/Bindings.java @@ -51,7 +51,7 @@ public class Bindings { YamlConfiguration desc = resource != null ? YamlConfiguration.loadConfiguration(new InputStreamReader(resource)) : new YamlConfiguration(); 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) { options.setLogger(new IrisLogger()); options.setDebug(true); @@ -79,7 +79,6 @@ public class Bindings { scope.setTag("server.api", Bukkit.getBukkitVersion()); scope.setTag("iris.commit", desc.getString("commit", "unknown")); }); - Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close)); } private static boolean suppress(Throwable e) { diff --git a/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java b/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java index 6d0eaaee0..56596eddf 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/HyperLock.java @@ -22,6 +22,7 @@ import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap; import com.volmit.iris.Iris; import com.volmit.iris.engine.data.cache.Cache; import com.volmit.iris.util.function.NastyRunnable; +import com.volmit.iris.util.function.NastySupplier; import com.volmit.iris.util.io.IORunnable; import java.io.IOException; @@ -107,6 +108,15 @@ public class HyperLock { return t; } + public T withNastyResult(int x, int z, NastySupplier r) throws Throwable { + lock(x, z); + try { + return r.get(); + } finally { + unlock(x, z); + } + } + public boolean tryLock(int x, int z) { return getLock(x, z).tryLock(); } diff --git a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java index 500a4a0b2..3762d9ecd 100644 --- a/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java +++ b/core/src/main/java/com/volmit/iris/util/parallel/MultiBurst.java @@ -20,7 +20,6 @@ package com.volmit.iris.util.parallel; import com.volmit.iris.Iris; 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.math.M; import com.volmit.iris.util.scheduling.PrecisionStopwatch; @@ -30,30 +29,37 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.IntSupplier; public class MultiBurst implements ExecutorService { private static final long TIMEOUT = Long.getLong("iris.burst.timeout", 15000); 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 String name; private final int priority; + private final IntSupplier parallelism; private ExecutorService service; 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.priority = priority; + this.parallelism = parallelism; last = new AtomicLong(M.ms()); - Iris.service(PreservationSVC.class).register(this); } private synchronized ExecutorService getService() { last.set(M.ms()); if (service == null || service.isShutdown()) { - service = new ForkJoinPool(IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()), + service = new ForkJoinPool(IrisSettings.getThreadCount(parallelism.getAsInt()), new ForkJoinPool.ForkJoinWorkerThreadFactory() { int m = 0; diff --git a/core/src/main/java/com/volmit/iris/util/parallel/StreamUtils.java b/core/src/main/java/com/volmit/iris/util/parallel/StreamUtils.java new file mode 100644 index 000000000..0c88d0d35 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/parallel/StreamUtils.java @@ -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 streamRadius(int x, int z, int radius) { + return streamRadius(x, z, radius, radius); + } + + public static Stream 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 void forEach(Stream stream, Function> mapper, Consumer consumer, @Nullable MultiBurst burst) { + forEach(stream.flatMap(mapper), consumer, burst); + } + + @SneakyThrows + public static void forEach(Stream stream, Consumer task, @Nullable MultiBurst burst) { + if (burst == null) stream.forEach(task); + else burst.submit(() -> stream.parallel().forEach(task)).get(); + } +}