diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java
index e059b30e1..9e2769ebb 100644
--- a/core/src/main/java/com/volmit/iris/Iris.java
+++ b/core/src/main/java/com/volmit/iris/Iris.java
@@ -620,12 +620,22 @@ public class Iris extends VolmitPlugin implements Listener {
Iris.warn("=");
Iris.warn("============================================");
}
- if (!instance.getServer().getVersion().contains("Purpur")) {
- passed = false;
+
+ try {
+ Class.forName("io.papermc.paper.configuration.PaperConfigurations");
+ } catch (ClassNotFoundException e) {
+ Iris.info(C.RED + "Iris requires paper or above to function properly..");
+ return false;
+ }
+
+ try {
+ Class.forName("org.purpurmc.purpur.PurpurConfig");
+ } catch (ClassNotFoundException e) {
Iris.info("We recommend using Purpur for the best experience with Iris.");
Iris.info("Purpur is a fork of Paper that is optimized for performance and stability.");
Iris.info("Plugins that work on Spigot / Paper work on Purpur.");
Iris.info("You can download it here: https://purpurmc.org");
+ return false;
}
return passed;
}
@@ -850,13 +860,6 @@ public class Iris extends VolmitPlugin implements Listener {
Iris.info("Server type & version: " + C.RED + Bukkit.getVersion());
} else { Iris.info("Server type & version: " + Bukkit.getVersion()); }
Iris.info("Java: " + getJava());
- if (!instance.getServer().getVersion().contains("Purpur")) {
- if (instance.getServer().getVersion().contains("Spigot") && instance.getServer().getVersion().contains("Bukkit")) {
- Iris.info(C.RED + " Iris requires paper or above to function properly..");
- } else {
- Iris.info(C.YELLOW + "Purpur is recommended to use with iris.");
- }
- }
if (getHardware.getProcessMemory() < 5999) {
Iris.warn("6GB+ Ram is recommended");
Iris.warn("Process Memory: " + getHardware.getProcessMemory() + " MB");
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 5381a7e39..cc96233c0 100644
--- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java
+++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java
@@ -24,7 +24,6 @@ import com.volmit.iris.util.io.IO;
import com.volmit.iris.util.json.JSONException;
import com.volmit.iris.util.json.JSONObject;
import com.volmit.iris.util.plugin.VolmitSender;
-import com.volmit.iris.util.scheduling.ChronoLatch;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -45,6 +44,7 @@ public class IrisSettings {
private IrisSettingsStudio studio = new IrisSettingsStudio();
private IrisSettingsPerformance performance = new IrisSettingsPerformance();
private IrisSettingsUpdater updater = new IrisSettingsUpdater();
+ private IrisSettingsPregen pregen = new IrisSettingsPregen();
public static int getThreadCount(int c) {
return switch (c) {
@@ -130,6 +130,7 @@ public class IrisSettings {
public boolean markerEntitySpawningSystem = true;
public boolean effectSystem = true;
public boolean worldEditWandCUI = true;
+ public boolean globalPregenCache = true;
}
@Data
@@ -137,6 +138,12 @@ public class IrisSettings {
public int parallelism = -1;
}
+ @Data
+ public static class IrisSettingsPregen {
+ public boolean useVirtualThreads = false;
+ public int maxConcurrency = 256;
+ }
+
@Data
public static class IrisSettingsPerformance {
public boolean trimMantleInStudio = false;
diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java
deleted file mode 100644
index d8cd6a260..000000000
--- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeepSearch.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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.core.commands;
-
-import com.volmit.iris.Iris;
-import com.volmit.iris.core.pregenerator.DeepSearchPregenerator;
-import com.volmit.iris.core.pregenerator.PregenTask;
-import com.volmit.iris.core.pregenerator.TurboPregenerator;
-import com.volmit.iris.core.tools.IrisToolbelt;
-import com.volmit.iris.util.data.Dimension;
-import com.volmit.iris.util.decree.DecreeExecutor;
-import com.volmit.iris.util.decree.annotations.Decree;
-import com.volmit.iris.util.decree.annotations.Param;
-import com.volmit.iris.util.format.C;
-import com.volmit.iris.util.math.Position2;
-import org.bukkit.Bukkit;
-import org.bukkit.World;
-import org.bukkit.util.Vector;
-
-import java.io.File;
-import java.io.IOException;
-
-@Decree(name = "DeepSearch", aliases = "search", description = "Pregenerate your Iris worlds!")
-public class CommandDeepSearch implements DecreeExecutor {
- public String worldName;
- @Decree(description = "DeepSearch a world")
- public void start(
- @Param(description = "The radius of the pregen in blocks", aliases = "size")
- int radius,
- @Param(description = "The world to pregen", contextual = true)
- World world,
- @Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
- Vector center
- ) {
-
- worldName = world.getName();
- File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
- File TurboFile = new File(worldDirectory, "DeepSearch.json");
- if (TurboFile.exists()) {
- if (DeepSearchPregenerator.getInstance() != null) {
- sender().sendMessage(C.BLUE + "DeepSearch is already in progress");
- Iris.info(C.YELLOW + "DeepSearch is already in progress");
- return;
- } else {
- try {
- TurboFile.delete();
- } catch (Exception e){
- Iris.error("Failed to delete the old instance file of DeepSearch!");
- return;
- }
- }
- }
-
- try {
- if (sender().isPlayer() && access() == null) {
- sender().sendMessage(C.RED + "The engine access for this world is null!");
- sender().sendMessage(C.RED + "Please make sure the world is loaded & the engine is initialized. Generate a new chunk, for example.");
- }
-
- DeepSearchPregenerator.DeepSearchJob DeepSearchJob = DeepSearchPregenerator.DeepSearchJob.builder()
- .world(world)
- .radiusBlocks(radius)
- .position(0)
- .build();
-
- File SearchGenFile = new File(worldDirectory, "DeepSearch.json");
- DeepSearchPregenerator pregenerator = new DeepSearchPregenerator(DeepSearchJob, SearchGenFile);
- pregenerator.start();
-
- String msg = C.GREEN + "DeepSearch started in " + C.GOLD + worldName + C.GREEN + " of " + C.GOLD + (radius * 2) + C.GREEN + " by " + C.GOLD + (radius * 2) + C.GREEN + " blocks from " + C.GOLD + center.getX() + "," + center.getZ();
- sender().sendMessage(msg);
- Iris.info(msg);
- } catch (Throwable e) {
- sender().sendMessage(C.RED + "Epic fail. See console.");
- Iris.reportError(e);
- e.printStackTrace();
- }
- }
-
- @Decree(description = "Stop the active DeepSearch task", aliases = "x")
- public void stop(@Param(aliases = "world", description = "The world to pause") World world) throws IOException {
- DeepSearchPregenerator DeepSearchInstance = DeepSearchPregenerator.getInstance();
- File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
- File turboFile = new File(worldDirectory, "DeepSearch.json");
-
- if (DeepSearchInstance != null) {
- DeepSearchInstance.shutdownInstance(world);
- sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
- } else if (turboFile.exists() && turboFile.delete()) {
- sender().sendMessage(C.LIGHT_PURPLE + "Closed Turbogen instance for " + world.getName());
- } else if (turboFile.exists()) {
- Iris.error("Failed to delete the old instance file of Turbo Pregen!");
- } else {
- sender().sendMessage(C.YELLOW + "No active pregeneration tasks to stop");
- }
- }
-
- @Decree(description = "Pause / continue the active pregeneration task", aliases = {"t", "resume", "unpause"})
- public void pause(
- @Param(aliases = "world", description = "The world to pause")
- World world
- ) {
- if (TurboPregenerator.getInstance() != null) {
- TurboPregenerator.setPausedTurbo(world);
- sender().sendMessage(C.GREEN + "Paused/unpaused Turbo Pregen, now: " + (TurboPregenerator.isPausedTurbo(world) ? "Paused" : "Running") + ".");
- } else {
- File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName());
- File TurboFile = new File(worldDirectory, "DeepSearch.json");
- if (TurboFile.exists()){
- TurboPregenerator.loadTurboGenerator(world.getName());
- sender().sendMessage(C.YELLOW + "Started DeepSearch back up!");
- } else {
- sender().sendMessage(C.YELLOW + "No active DeepSearch tasks to pause/unpause.");
- }
-
- }
- }
-}
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 9b4203490..f9590151a 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
@@ -36,6 +36,7 @@ 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.mantle.TectonicPlate;
+import com.volmit.iris.util.math.M;
import com.volmit.iris.util.nbt.mca.MCAFile;
import com.volmit.iris.util.nbt.mca.MCAUtil;
import com.volmit.iris.util.parallel.MultiBurst;
@@ -62,6 +63,7 @@ import java.util.zip.GZIPOutputStream;
@Decree(name = "Developer", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"})
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)
@@ -115,6 +117,42 @@ public class CommandDeveloper implements DecreeExecutor {
Iris.info("-------------------------");
}
+ @Decree(description = "Test")
+ public void dumpThreads() {
+ try {
+ File fi = Iris.instance.getDataFile("dump", "td-" + new java.sql.Date(M.ms()) + ".txt");
+ FileOutputStream fos = new FileOutputStream(fi);
+ Map f = Thread.getAllStackTraces();
+ PrintWriter pw = new PrintWriter(fos);
+
+ pw.println(Thread.activeCount() + "/" + f.size());
+ var run = Runtime.getRuntime();
+ pw.println("Memory:");
+ pw.println("\tMax: " + run.maxMemory());
+ pw.println("\tTotal: " + run.totalMemory());
+ pw.println("\tFree: " + run.freeMemory());
+ pw.println("\tUsed: " + (run.totalMemory() - run.freeMemory()));
+
+ for (Thread i : f.keySet()) {
+ pw.println("========================================");
+ pw.println("Thread: '" + i.getName() + "' ID: " + i.getId() + " STATUS: " + i.getState().name());
+
+ for (StackTraceElement j : f.get(i)) {
+ pw.println(" @ " + j.toString());
+ }
+
+ pw.println("========================================");
+ pw.println();
+ pw.println();
+ }
+
+ pw.close();
+ Iris.info("DUMPED! See " + fi.getAbsolutePath());
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+
@Decree(description = "Test")
public void benchmarkMantle(
@Param(description = "The world to bench", aliases = {"world"})
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 9279beb71..46e8719a9 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
@@ -24,7 +24,6 @@ import com.volmit.iris.core.loader.IrisData;
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.IrisBenchmarking;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.IrisDimension;
@@ -59,7 +58,6 @@ import java.util.List;
import static com.volmit.iris.Iris.service;
import static com.volmit.iris.core.service.EditSVC.deletingWorld;
-import static com.volmit.iris.core.tools.IrisBenchmarking.inProgress;
import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode;
import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities;
import static org.bukkit.Bukkit.getServer;
@@ -68,7 +66,6 @@ import static org.bukkit.Bukkit.getServer;
public class CommandIris implements DecreeExecutor {
private CommandStudio studio;
private CommandPregen pregen;
- private CommandLazyPregen lazyPregen;
private CommandSettings settings;
private CommandObject object;
private CommandJigsaw jigsaw;
@@ -175,16 +172,6 @@ public class CommandIris implements DecreeExecutor {
sender().sendMessage(C.GREEN + "Iris v" + Iris.instance.getDescription().getVersion() + " by Volmit Software");
}
- //todo Move to React
- @Decree(description = "Benchmark your server", origin = DecreeOrigin.CONSOLE)
- public void serverbenchmark() throws InterruptedException {
- if(!inProgress) {
- IrisBenchmarking.runBenchmark();
- } else {
- Iris.info(C.RED + "Benchmark already is in progress.");
- }
- }
-
/*
/todo
@Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE)
diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java b/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java
index 6d7bc42a6..2de5cef25 100644
--- a/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java
+++ b/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java
@@ -39,7 +39,9 @@ public class CommandPregen implements DecreeExecutor {
@Param(description = "The world to pregen", contextual = true)
World world,
@Param(aliases = "middle", description = "The center location of the pregen. Use \"me\" for your current location", defaultValue = "0,0")
- Vector center
+ Vector center,
+ @Param(description = "Open the Iris pregen gui", defaultValue = "true")
+ boolean gui
) {
try {
if (sender().isPlayer() && access() == null) {
@@ -50,7 +52,7 @@ public class CommandPregen implements DecreeExecutor {
IrisToolbelt.pregenerate(PregenTask
.builder()
.center(new Position2(center.getBlockX(), center.getBlockZ()))
- .gui(true)
+ .gui(gui)
.radiusX(radius)
.radiusZ(radius)
.build(), world);
diff --git a/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java b/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java
index 63b8919db..e12ca46a6 100644
--- a/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java
+++ b/core/src/main/java/com/volmit/iris/core/gui/PregeneratorJob.java
@@ -40,6 +40,8 @@ import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@@ -64,6 +66,7 @@ public class PregeneratorJob implements PregenListener {
private final Position2 max;
private final ChronoLatch cl = new ChronoLatch(TimeUnit.MINUTES.toMillis(1));
private final Engine engine;
+ private final ExecutorService service;
private JFrame frame;
private PregenRenderer renderer;
private int rgc = 0;
@@ -96,6 +99,7 @@ public class PregeneratorJob implements PregenListener {
}, "Iris Pregenerator");
t.setPriority(Thread.MIN_PRIORITY);
t.start();
+ service = Executors.newVirtualThreadPerTaskExecutor();
}
public static boolean shutdownInstance() {
@@ -219,10 +223,10 @@ public class PregeneratorJob implements PregenListener {
}
@Override
- public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) {
+ public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
info = new String[]{
(paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)",
- "Speed: " + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m",
+ "Speed: " + (cached ? "Cached " : "") + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m",
Form.duration(eta, 2) + " Remaining " + " (" + Form.duration(elapsed, 2) + " Elapsed)",
"Generation Method: " + method,
"Memory: " + Form.memSize(monitor.getUsedBytes(), 2) + " (" + Form.pc(monitor.getUsagePercent(), 0) + ") Pressure: " + Form.memSize(monitor.getPressure(), 0) + "/s",
@@ -240,13 +244,16 @@ public class PregeneratorJob implements PregenListener {
}
@Override
- public void onChunkGenerated(int x, int z) {
- if (engine != null) {
- draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8));
- return;
- }
+ public void onChunkGenerated(int x, int z, boolean cached) {
+ if (renderer == null || frame == null || !frame.isVisible()) return;
+ service.submit(() -> {
+ if (engine != null) {
+ draw(x, z, engine.draw((x << 4) + 8, (z << 4) + 8));
+ return;
+ }
- draw(x, z, COLOR_GENERATED);
+ draw(x, z, COLOR_GENERATED);
+ });
}
@Override
@@ -306,6 +313,7 @@ public class PregeneratorJob implements PregenListener {
close();
instance = null;
whenDone.forEach(Runnable::run);
+ service.shutdownNow();
}
@Override
diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java
index 57aa8b0eb..3201bebf4 100644
--- a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java
+++ b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java
@@ -31,28 +31,33 @@ import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper;
+import com.volmit.iris.util.scheduling.PrecisionStopwatch;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
public class IrisPregenerator {
+ private static final double INVALID = 9223372036854775807d;
private final PregenTask task;
private final PregeneratorMethod generator;
private final PregenListener listener;
private final Looper ticker;
private final AtomicBoolean paused;
private final AtomicBoolean shutdown;
+ private final RollingSequence cachedPerSecond;
private final RollingSequence chunksPerSecond;
private final RollingSequence chunksPerMinute;
private final RollingSequence regionsPerMinute;
private final KList chunksPerSecondHistory;
- private static AtomicInteger generated;
- private final AtomicInteger generatedLast;
- private final AtomicInteger generatedLastMinute;
- private static AtomicInteger totalChunks;
+ private final AtomicLong generated;
+ private final AtomicLong generatedLast;
+ private final AtomicLong generatedLastMinute;
+ private final AtomicLong cached;
+ private final AtomicLong cachedLast;
+ private final AtomicLong cachedLastMinute;
+ private final AtomicLong totalChunks;
private final AtomicLong startTime;
private final ChronoLatch minuteLatch;
private final AtomicReference currentGeneratorMethod;
@@ -74,46 +79,71 @@ public class IrisPregenerator {
net = new KSet<>();
currentGeneratorMethod = new AtomicReference<>("Void");
minuteLatch = new ChronoLatch(60000, false);
+ cachedPerSecond = new RollingSequence(5);
chunksPerSecond = new RollingSequence(10);
chunksPerMinute = new RollingSequence(10);
regionsPerMinute = new RollingSequence(10);
chunksPerSecondHistory = new KList<>();
- generated = new AtomicInteger(0);
- generatedLast = new AtomicInteger(0);
- generatedLastMinute = new AtomicInteger(0);
- totalChunks = new AtomicInteger(0);
+ generated = new AtomicLong(0);
+ generatedLast = new AtomicLong(0);
+ generatedLastMinute = new AtomicLong(0);
+ cached = new AtomicLong();
+ cachedLast = new AtomicLong(0);
+ cachedLastMinute = new AtomicLong(0);
+ totalChunks = new AtomicLong(0);
task.iterateAllChunks((_a, _b) -> totalChunks.incrementAndGet());
startTime = new AtomicLong(M.ms());
ticker = new Looper() {
@Override
protected long loop() {
long eta = computeETA();
- int secondGenerated = generated.get() - generatedLast.get();
- generatedLast.set(generated.get());
- chunksPerSecond.put(secondGenerated);
- chunksPerSecondHistory.add(secondGenerated);
- if (minuteLatch.flip()) {
- int minuteGenerated = generated.get() - generatedLastMinute.get();
- generatedLastMinute.set(generated.get());
- chunksPerMinute.put(minuteGenerated);
- regionsPerMinute.put((double) minuteGenerated / 1024D);
+ long secondCached = cached.get() - cachedLast.get();
+ cachedLast.set(cached.get());
+ cachedPerSecond.put(secondCached);
+
+ long secondGenerated = generated.get() - generatedLast.get() - secondCached;
+ generatedLast.set(generated.get());
+ if (secondCached == 0 || secondGenerated != 0) {
+ chunksPerSecond.put(secondGenerated);
+ chunksPerSecondHistory.add((int) secondGenerated);
}
- listener.onTick(chunksPerSecond.getAverage(), chunksPerMinute.getAverage(),
+ if (minuteLatch.flip()) {
+ long minuteCached = cached.get() - cachedLastMinute.get();
+ cachedLastMinute.set(cached.get());
+
+ long minuteGenerated = generated.get() - generatedLastMinute.get() - minuteCached;
+ generatedLastMinute.set(generated.get());
+ if (minuteCached == 0 || minuteGenerated != 0) {
+ chunksPerMinute.put(minuteGenerated);
+ regionsPerMinute.put((double) minuteGenerated / 1024D);
+ }
+ }
+ boolean cached = cachedPerSecond.getAverage() != 0;
+
+ listener.onTick(
+ cached ? cachedPerSecond.getAverage() : chunksPerSecond.getAverage(),
+ chunksPerMinute.getAverage(),
regionsPerMinute.getAverage(),
- (double) generated.get() / (double) totalChunks.get(),
- generated.get(), totalChunks.get(),
- totalChunks.get() - generated.get(),
- eta, M.ms() - startTime.get(), currentGeneratorMethod.get());
+ (double) generated.get() / (double) totalChunks.get(), generated.get(),
+ totalChunks.get(),
+ totalChunks.get() - generated.get(), eta, M.ms() - startTime.get(), currentGeneratorMethod.get(),
+ cached);
if (cl.flip()) {
double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100;
- if (!IrisPackBenchmarking.benchmarkInProgress) {
- Iris.info("Pregen: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage);
- } else {
- Iris.info("Benchmarking: " + Form.f(generated.get()) + " of " + Form.f(totalChunks.get()) + " (%.0f%%) " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration(eta, 2), percentage);
- }
+
+ Iris.info("%s: %s of %s (%.0f%%), %s/s ETA: %s",
+ IrisPackBenchmarking.benchmarkInProgress ? "Benchmarking" : "Pregen",
+ Form.f(generated.get()),
+ Form.f(totalChunks.get()),
+ percentage,
+ cached ?
+ "Cached " + Form.f((int) cachedPerSecond.getAverage()) :
+ Form.f((int) chunksPerSecond.getAverage()),
+ Form.duration(eta, 2)
+ );
}
return 1000;
}
@@ -121,12 +151,13 @@ public class IrisPregenerator {
}
private long computeETA() {
- return (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total?
+ double d = (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total?
// If yes, use smooth function (which gets more accurate over time since its less sensitive to outliers)
- ((totalChunks.get() - generated.get()) * ((double) (M.ms() - startTime.get()) / (double) generated.get())) :
+ ((totalChunks.get() - generated.get() - cached.get()) * ((double) (M.ms() - startTime.get()) / ((double) generated.get() - cached.get()))) :
// If no, use quick function (which is less accurate over time but responds better to the initial delay)
- ((totalChunks.get() - generated.get()) / chunksPerSecond.getAverage()) * 1000
+ ((totalChunks.get() - generated.get() - cached.get()) / chunksPerSecond.getAverage()) * 1000
);
+ return Double.isFinite(d) && d != INVALID ? (long) d : 0;
}
@@ -138,8 +169,10 @@ public class IrisPregenerator {
init();
ticker.start();
checkRegions();
+ var p = PrecisionStopwatch.start();
task.iterateRegions((x, z) -> visitRegion(x, z, true));
task.iterateRegions((x, z) -> visitRegion(x, z, false));
+ Iris.info("Pregen took " + Form.duration((long) p.getMilliseconds()));
shutdown();
if (!IrisPackBenchmarking.benchmarkInProgress) {
Iris.info(C.IRIS + "Pregen stopped.");
@@ -234,8 +267,8 @@ public class IrisPregenerator {
private PregenListener listenify(PregenListener listener) {
return new PregenListener() {
@Override
- public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) {
- listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method);
+ public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
+ listener.onTick(chunksPerSecond, chunksPerMinute, regionsPerMinute, percent, generated, totalChunks, chunksRemaining, eta, elapsed, method, cached);
}
@Override
@@ -244,9 +277,10 @@ public class IrisPregenerator {
}
@Override
- public void onChunkGenerated(int x, int z) {
- listener.onChunkGenerated(x, z);
+ public void onChunkGenerated(int x, int z, boolean c) {
+ listener.onChunkGenerated(x, z, c);
generated.addAndGet(1);
+ if (c) cached.addAndGet(1);
}
@Override
diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/PregenListener.java b/core/src/main/java/com/volmit/iris/core/pregenerator/PregenListener.java
index fb3ab3952..6f5d83194 100644
--- a/core/src/main/java/com/volmit/iris/core/pregenerator/PregenListener.java
+++ b/core/src/main/java/com/volmit/iris/core/pregenerator/PregenListener.java
@@ -19,11 +19,15 @@
package com.volmit.iris.core.pregenerator;
public interface PregenListener {
- void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method);
+ void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generated, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached);
void onChunkGenerating(int x, int z);
- void onChunkGenerated(int x, int z);
+ default void onChunkGenerated(int x, int z) {
+ onChunkGenerated(x, z, false);
+ }
+
+ void onChunkGenerated(int x, int z, boolean cached);
void onRegionGenerated(int x, int z);
diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java
new file mode 100644
index 000000000..dff3840cd
--- /dev/null
+++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCache.java
@@ -0,0 +1,70 @@
+package com.volmit.iris.core.pregenerator.cache;
+
+import com.volmit.iris.util.documentation.ChunkCoordinates;
+import com.volmit.iris.util.documentation.RegionCoordinates;
+
+import java.io.File;
+
+public interface PregenCache {
+ default boolean isThreadSafe() {
+ return false;
+ }
+
+ @ChunkCoordinates
+ boolean isChunkCached(int x, int z);
+
+ @RegionCoordinates
+ boolean isRegionCached(int x, int z);
+
+ @ChunkCoordinates
+ void cacheChunk(int x, int z);
+
+ @RegionCoordinates
+ void cacheRegion(int x, int z);
+
+ void write();
+
+ static PregenCache create(File directory) {
+ if (directory == null) return EMPTY;
+ return new PregenCacheImpl(directory);
+ }
+
+ default PregenCache sync() {
+ if (isThreadSafe()) return this;
+ return new SynchronizedCache(this);
+ }
+
+ PregenCache EMPTY = new PregenCache() {
+ @Override
+ public boolean isThreadSafe() {
+ return true;
+ }
+
+ @Override
+ public boolean isChunkCached(int x, int z) {
+ return false;
+ }
+
+ @Override
+ public boolean isRegionCached(int x, int z) {
+ return false;
+ }
+
+ @Override
+ public void cacheChunk(int x, int z) {
+
+ }
+
+ @Override
+ public void cacheRegion(int x, int z) {
+
+ }
+
+ @Override
+ public void write() {
+
+ }
+ };
+
+
+}
diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java
new file mode 100644
index 000000000..bf48e06e9
--- /dev/null
+++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java
@@ -0,0 +1,215 @@
+package com.volmit.iris.core.pregenerator.cache;
+
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
+import com.github.benmanes.caffeine.cache.RemovalCause;
+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.RegionCoordinates;
+import com.volmit.iris.util.parallel.HyperLock;
+import lombok.RequiredArgsConstructor;
+import net.jpountz.lz4.LZ4BlockInputStream;
+import net.jpountz.lz4.LZ4BlockOutputStream;
+import org.jetbrains.annotations.Nullable;
+
+import javax.annotation.concurrent.NotThreadSafe;
+import java.io.*;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
+
+@NotThreadSafe
+@RequiredArgsConstructor
+class PregenCacheImpl implements PregenCache {
+ private static final int SIZE = 32;
+ private final File directory;
+ private final HyperLock hyperLock = new HyperLock(SIZE * 2, true);
+ private final LoadingCache cache = Caffeine.newBuilder()
+ .expireAfterAccess(10, TimeUnit.SECONDS)
+ .maximumSize(SIZE)
+ .removalListener(this::onRemoval)
+ .evictionListener(this::onRemoval)
+ .build(this::load);
+
+ @ChunkCoordinates
+ public boolean isChunkCached(int x, int z) {
+ var plate = cache.get(new Pos(x >> 10, z >> 10));
+ if (plate == null) return false;
+ return plate.isCached((x >> 5) & 31, (z >> 5) & 31, r -> r.isCached(x & 31, z & 31));
+ }
+
+ @RegionCoordinates
+ public boolean isRegionCached(int x, int z) {
+ var plate = cache.get(new Pos(x >> 5, z >> 5));
+ if (plate == null) return false;
+ return plate.isCached(x & 31, z & 31, Region::isCached);
+ }
+
+ @ChunkCoordinates
+ public void cacheChunk(int x, int z) {
+ var plate = cache.get(new Pos(x >> 10, z >> 10));
+ plate.cache((x >> 5) & 31, (z >> 5) & 31, r -> r.cache(x & 31, z & 31));
+ }
+
+ @RegionCoordinates
+ public void cacheRegion(int x, int z) {
+ var plate = cache.get(new Pos(x >> 5, z >> 5));
+ plate.cache(x & 31, z & 31, Region::cache);
+ }
+
+ public void write() {
+ cache.asMap().values().forEach(this::write);
+ }
+
+ private Plate load(Pos key) {
+ hyperLock.lock(key.x, key.z);
+ try {
+ File file = fileForPlate(key);
+ if (!file.exists()) return new Plate(key);
+ try (var in = new DataInputStream(new LZ4BlockInputStream(new FileInputStream(file)))) {
+ return new Plate(key, in);
+ } catch (IOException e){
+ Iris.error("Failed to read pregen cache " + file);
+ e.printStackTrace();
+ return new Plate(key);
+ }
+ } finally {
+ hyperLock.unlock(key.x, key.z);
+ }
+ }
+
+ private void write(Plate plate) {
+ hyperLock.lock(plate.pos.x, plate.pos.z);
+ try {
+ File file = fileForPlate(plate.pos);
+ try (var out = new DataOutputStream(new LZ4BlockOutputStream(new FileOutputStream(file)))) {
+ plate.write(out);
+ } catch (IOException e) {
+ Iris.error("Failed to write pregen cache " + file);
+ e.printStackTrace();
+ }
+ } finally {
+ hyperLock.unlock(plate.pos.x, plate.pos.z);
+ }
+ }
+
+ private void onRemoval(@Nullable Pos key, @Nullable Plate plate, RemovalCause cause) {
+ if (plate == null) return;
+ write(plate);
+ }
+
+ private File fileForPlate(Pos pos) {
+ if (!directory.exists() && !directory.mkdirs())
+ throw new IllegalStateException("Cannot create directory: " + directory.getAbsolutePath());
+ return new File(directory, "c." + pos.x + "." + pos.z + ".lz4b");
+ }
+
+ private static class Plate {
+ private final Pos pos;
+ private short count;
+ private Region[] regions;
+
+ public Plate(Pos pos) {
+ this.pos = pos;
+ count = 0;
+ regions = new Region[1024];
+ }
+
+ public Plate(Pos pos, DataInput in) throws IOException {
+ this.pos = pos;
+ count = (short) Varint.readSignedVarInt(in);
+ if (count == 1024) return;
+ regions = new Region[1024];
+ for (int i = 0; i < 1024; i++) {
+ if (in.readBoolean()) continue;
+ regions[i] = new Region(in);
+ }
+ }
+
+ public boolean isCached(int x, int z, Predicate predicate) {
+ if (count == 1024) return true;
+ Region region = regions[x * 32 + z];
+ if (region == null) return false;
+ return predicate.test(region);
+ }
+
+ public void cache(int x, int z, Predicate predicate) {
+ if (count == 1024) return;
+ Region region = regions[x * 32 + z];
+ if (region == null) regions[x * 32 + z] = region = new Region();
+ if (predicate.test(region)) count++;
+ }
+
+ public void write(DataOutput out) throws IOException {
+ Varint.writeSignedVarInt(count, out);
+ if (count == 1024) return;
+ for (Region region : regions) {
+ out.writeBoolean(region == null);
+ if (region == null) continue;
+ region.write(out);
+ }
+ }
+ }
+
+ private static class Region {
+ private short count;
+ private long[] words;
+
+ public Region() {
+ count = 0;
+ words = new long[64];
+ }
+
+ public Region(DataInput in) throws IOException {
+ count = (short) Varint.readSignedVarInt(in);
+ if (count == 1024) return;
+ words = new long[64];
+ for (int i = 0; i < 64; i++) {
+ words[i] = Varint.readUnsignedVarLong(in);
+ }
+ }
+
+ public boolean cache() {
+ if (count == 1024) return false;
+ count = 1024;
+ words = null;
+ return true;
+ }
+
+ public boolean cache(int x, int z) {
+ if (count == 1024) return false;
+
+ int i = x * 32 + z;
+ int w = i >> 6;
+ long b = 1L << (i & 63);
+
+ var cur = (words[w] & b) != 0;
+ if (cur) return false;
+
+ if (++count == 1024) {
+ words = null;
+ return true;
+ } else words[w] |= b;
+ return false;
+ }
+
+ public boolean isCached() {
+ return count == 1024;
+ }
+
+ public boolean isCached(int x, int z) {
+ int i = x * 32 + z;
+ return count == 1024 || (words[i >> 6] & 1L << (i & 63)) != 0;
+ }
+
+ public void write(DataOutput out) throws IOException {
+ Varint.writeSignedVarInt(count, out);
+ if (isCached()) return;
+ for (long word : words) {
+ Varint.writeUnsignedVarLong(word, out);
+ }
+ }
+ }
+
+ private record Pos(int x, int z) {}
+}
diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java
new file mode 100644
index 000000000..52f3b7774
--- /dev/null
+++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/SynchronizedCache.java
@@ -0,0 +1,48 @@
+package com.volmit.iris.core.pregenerator.cache;
+
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+class SynchronizedCache implements PregenCache {
+ private final PregenCache cache;
+
+ @Override
+ public boolean isThreadSafe() {
+ return true;
+ }
+
+ @Override
+ public boolean isChunkCached(int x, int z) {
+ synchronized (cache) {
+ return cache.isChunkCached(x, z);
+ }
+ }
+
+ @Override
+ public boolean isRegionCached(int x, int z) {
+ synchronized (cache) {
+ return cache.isRegionCached(x, z);
+ }
+ }
+
+ @Override
+ public void cacheChunk(int x, int z) {
+ synchronized (cache) {
+ cache.cacheChunk(x, z);
+ }
+ }
+
+ @Override
+ public void cacheRegion(int x, int z) {
+ synchronized (cache) {
+ cache.cacheRegion(x, z);
+ }
+ }
+
+ @Override
+ public void write() {
+ synchronized (cache) {
+ cache.write();
+ }
+ }
+}
diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java
index 8496f924c..e2ab1dfc2 100644
--- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java
+++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java
@@ -19,6 +19,7 @@
package com.volmit.iris.core.pregenerator.methods;
import com.volmit.iris.Iris;
+import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.pregenerator.PregenListener;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
import com.volmit.iris.core.tools.IrisToolbelt;
@@ -33,12 +34,15 @@ import org.bukkit.World;
import java.util.ArrayList;
import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class AsyncPregenMethod implements PregeneratorMethod {
private final World world;
- private final MultiBurst burst;
+ private final ExecutorService service;
private final Semaphore semaphore;
+ private final int threads;
private final Map lastUse;
public AsyncPregenMethod(World world, int threads) {
@@ -47,8 +51,11 @@ public class AsyncPregenMethod implements PregeneratorMethod {
}
this.world = world;
- burst = new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
- semaphore = new Semaphore(256);
+ service = IrisSettings.get().getPregen().isUseVirtualThreads() ?
+ Executors.newVirtualThreadPerTaskExecutor() :
+ new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
+ this.threads = IrisSettings.get().getPregen().getMaxConcurrency();
+ semaphore = new Semaphore(threads);
this.lastUse = new KMap<>();
}
@@ -101,9 +108,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
@Override
public void close() {
- semaphore.acquireUninterruptibly(256);
+ semaphore.acquireUninterruptibly(threads);
unloadAndSaveAllChunks();
- burst.close();
+ service.shutdown();
}
@Override
@@ -129,7 +136,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
} catch (InterruptedException e) {
return;
}
- burst.complete(() -> completeChunk(x, z, listener));
+ service.submit(() -> completeChunk(x, z, listener));
}
@Override
diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/CachedPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/CachedPregenMethod.java
new file mode 100644
index 000000000..91c4ddb87
--- /dev/null
+++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/CachedPregenMethod.java
@@ -0,0 +1,86 @@
+package com.volmit.iris.core.pregenerator.methods;
+
+import com.volmit.iris.Iris;
+import com.volmit.iris.core.pregenerator.PregenListener;
+import com.volmit.iris.core.pregenerator.PregeneratorMethod;
+import com.volmit.iris.core.pregenerator.cache.PregenCache;
+import com.volmit.iris.core.service.GlobalCacheSVC;
+import com.volmit.iris.util.mantle.Mantle;
+import lombok.AllArgsConstructor;
+
+@AllArgsConstructor
+public class CachedPregenMethod implements PregeneratorMethod {
+ private final PregeneratorMethod method;
+ private final PregenCache cache;
+
+ public CachedPregenMethod(PregeneratorMethod method, String worldName) {
+ this.method = method;
+ var cache = Iris.service(GlobalCacheSVC.class).get(worldName);
+ if (cache == null) {
+ Iris.debug("Could not find existing cache for " + worldName + " creating fallback");
+ cache = GlobalCacheSVC.createDefault(worldName);
+ }
+ this.cache = cache;
+ }
+
+ @Override
+ public void init() {
+ method.init();
+ }
+
+ @Override
+ public void close() {
+ method.close();
+ cache.write();
+ }
+
+ @Override
+ public void save() {
+ method.save();
+ cache.write();
+ }
+
+ @Override
+ public boolean supportsRegions(int x, int z, PregenListener listener) {
+ return cache.isRegionCached(x, z) || method.supportsRegions(x, z, listener);
+ }
+
+ @Override
+ public String getMethod(int x, int z) {
+ return method.getMethod(x, z);
+ }
+
+ @Override
+ public void generateRegion(int x, int z, PregenListener listener) {
+ if (cache.isRegionCached(x, z)) {
+ listener.onRegionGenerated(x, z);
+
+ int rX = x << 5, rZ = z << 5;
+ for (int cX = 0; cX < 32; cX++) {
+ for (int cZ = 0; cZ < 32; cZ++) {
+ listener.onChunkGenerated(rX + cX, rZ + cZ, true);
+ listener.onChunkCleaned(rX + cX, rZ + cZ);
+ }
+ }
+ return;
+ }
+ method.generateRegion(x, z, listener);
+ cache.cacheRegion(x, z);
+ }
+
+ @Override
+ public void generateChunk(int x, int z, PregenListener listener) {
+ if (cache.isChunkCached(x, z)) {
+ listener.onChunkGenerated(x, z, true);
+ listener.onChunkCleaned(x, z);
+ return;
+ }
+ method.generateChunk(x, z, listener);
+ cache.cacheChunk(x, z);
+ }
+
+ @Override
+ public Mantle getMantle() {
+ return method.getMantle();
+ }
+}
diff --git a/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java b/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java
new file mode 100644
index 000000000..32d6af184
--- /dev/null
+++ b/core/src/main/java/com/volmit/iris/core/service/GlobalCacheSVC.java
@@ -0,0 +1,104 @@
+package com.volmit.iris.core.service;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.volmit.iris.core.IrisSettings;
+import com.volmit.iris.core.pregenerator.cache.PregenCache;
+import com.volmit.iris.util.collection.KMap;
+import com.volmit.iris.util.plugin.IrisService;
+import lombok.NonNull;
+import org.bukkit.Bukkit;
+import org.bukkit.World;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.world.ChunkLoadEvent;
+import org.bukkit.event.world.WorldInitEvent;
+import org.bukkit.event.world.WorldUnloadEvent;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.function.Function;
+
+public class GlobalCacheSVC implements IrisService {
+ private static final Cache REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build();
+ private final KMap globalCache = new KMap<>();
+ private transient boolean lastState;
+
+ @Override
+ public void onEnable() {
+ lastState = !IrisSettings.get().getWorld().isGlobalPregenCache();
+ if (lastState) return;
+ Bukkit.getWorlds().forEach(this::createCache);
+ }
+
+ @Override
+ public void onDisable() {
+ globalCache.values().forEach(PregenCache::write);
+ }
+
+ @Nullable
+ public PregenCache get(@NonNull World world) {
+ return globalCache.get(world.getName());
+ }
+
+ @Nullable
+ public PregenCache get(@NonNull String world) {
+ return globalCache.get(world);
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void on(WorldInitEvent event) {
+ if (isDisabled()) return;
+ createCache(event.getWorld());
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void on(WorldUnloadEvent event) {
+ var cache = globalCache.remove(event.getWorld().getName());
+ if (cache == null) return;
+ cache.write();
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void on(ChunkLoadEvent event) {
+ var cache = get(event.getWorld());
+ if (cache == null) return;
+ cache.cacheChunk(event.getChunk().getX(), event.getChunk().getZ());
+ }
+
+ private void createCache(World world) {
+ globalCache.computeIfAbsent(world.getName(), GlobalCacheSVC::createDefault);
+ }
+
+ private boolean isDisabled() {
+ boolean conf = IrisSettings.get().getWorld().isGlobalPregenCache();
+ if (lastState != conf)
+ return lastState;
+
+ if (conf) {
+ Bukkit.getWorlds().forEach(this::createCache);
+ } else {
+ globalCache.values().removeIf(cache -> {
+ cache.write();
+ return true;
+ });
+ }
+
+ return lastState = !conf;
+ }
+
+
+ @NonNull
+ public static PregenCache createCache(@NonNull String worldName, @NonNull Function provider) {
+ return REFERENCE_CACHE.get(worldName, provider);
+ }
+
+ @NonNull
+ public static PregenCache createDefault(@NonNull String worldName) {
+ return createCache(worldName, GlobalCacheSVC::createDefault0);
+ }
+
+ private static PregenCache createDefault0(String worldName) {
+ return PregenCache.create(new File(Bukkit.getWorldContainer(), String.join(File.separator, worldName, "iris", "pregen"))).sync();
+ }
+}
diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java b/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java
deleted file mode 100644
index 79bf1b643..000000000
--- a/core/src/main/java/com/volmit/iris/core/tools/IrisBenchmarking.java
+++ /dev/null
@@ -1,625 +0,0 @@
-package com.volmit.iris.core.tools;
-
-import com.volmit.iris.Iris;
-import com.volmit.iris.util.format.C;
-import oshi.SystemInfo;
-import oshi.hardware.CentralProcessor;
-import oshi.hardware.GlobalMemory;
-import oshi.hardware.HWDiskStore;
-import oshi.software.os.OperatingSystem;
-
-import java.io.*;
-import java.lang.management.ManagementFactory;
-import java.lang.management.MemoryMXBean;
-import java.lang.management.MemoryUsage;
-import java.security.SecureRandom;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.IntStream;
-import java.util.zip.Deflater;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-import static com.google.common.math.LongMath.isPrime;
-import static com.volmit.iris.util.misc.getHardware.getCPUModel;
-public class IrisBenchmarking {
- static String ServerOS;
- static String filePath = "benchmark.dat";
- static double avgWriteSpeedMBps;
- static double avgReadSpeedMBps;
- static double highestWriteSpeedMBps;
- static double highestReadSpeedMBps;
- static double lowestWriteSpeedMBps;
- static double lowestReadSpeedMBps;
- static double calculateIntegerMath;
- static double calculateFloatingPoint;
- static double calculatePrimeNumbers;
- static double calculateStringSorting;
- static double calculateDataEncryption;
- static double calculateDataCompression;
- static String currentRunning = "None";
- static int BenchmarksCompleted = 0;
- static int BenchmarksTotal = 7;
- static int totalTasks = 10;
- static int currentTasks = 0;
- static double WindowsCPUCompression;
- static double WindowsCPUEncryption;
- static double WindowsCPUCSHA1;
- static double elapsedTimeNs;
- static boolean Winsat = false;
- static boolean WindowsDiskSpeed = false;
- public static boolean inProgress = false;
- static double startTime;
- // Good enough for now. . .
-
- public static void runBenchmark() throws InterruptedException {
- inProgress = true;
- getServerOS();
- deleteTestFile(filePath);
- AtomicReference doneCalculateDiskSpeed = new AtomicReference<>((double) 0);
- startBenchmarkTimer();
- Iris.info("Benchmark Started!");
- Iris.warn("Although it may seem momentarily paused, it's actively processing.");
- BenchmarksCompleted = 0;
-
- CompletableFuture future = CompletableFuture.runAsync(() -> {
- currentRunning = "calculateDiskSpeed";
- progressBar();
- if (ServerOS.contains("Windows") && isRunningAsAdmin()) {
- WindowsDiskSpeed = true;
- WindowsDiskSpeedTest();
- } else {
- warningFallback();
- try {
- Thread.sleep(10);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- doneCalculateDiskSpeed.set(roundToTwoDecimalPlaces(calculateDiskSpeed()));
- BenchmarksCompleted++;
- }
-
-
- }).thenRun(() -> {
- currentRunning = "WindowsCpuSpeedTest";
- progressBar();
- if (ServerOS.contains("Windows") && isRunningAsAdmin()) {
- Winsat = true;
- WindowsCpuSpeedTest();
- } else {
- Iris.info("Skipping:" + C.BLUE + " Windows System Assessment Tool Benchmarks");
- if (!ServerOS.contains("Windows")) {
- Iris.info("Required Software:" + C.BLUE + " Windows");
- BenchmarksTotal = 6;
- }
- if (!isRunningAsAdmin()) {
- Iris.info(C.RED + "ERROR: " + C.DARK_RED + "Elevated privileges missing");
- BenchmarksTotal = 6;
- }
- }
-
- }).thenRun(() -> {
- currentRunning = "calculateIntegerMath";
- progressBar();
- calculateIntegerMath = roundToTwoDecimalPlaces(calculateIntegerMath());
- BenchmarksCompleted++;
- }).thenRun(() -> {
- currentRunning = "calculateFloatingPoint";
- progressBar();
- calculateFloatingPoint = roundToTwoDecimalPlaces(calculateFloatingPoint());
- BenchmarksCompleted++;
- }).thenRun(() -> {
- currentRunning = "calculateStringSorting";
- progressBar();
- calculateStringSorting = roundToTwoDecimalPlaces(calculateStringSorting());
- BenchmarksCompleted++;
- }).thenRun(() -> {
- currentRunning = "calculatePrimeNumbers";
- progressBar();
- calculatePrimeNumbers = roundToTwoDecimalPlaces(calculatePrimeNumbers());
- BenchmarksCompleted++;
- }).thenRun(() -> {
- currentRunning = "calculateDataEncryption";
- progressBar();
- calculateDataEncryption = roundToTwoDecimalPlaces(calculateDataEncryption());
- BenchmarksCompleted++;
- }).thenRun(() -> {
- currentRunning = "calculateDataCompression";
- progressBar();
- calculateDataCompression = roundToTwoDecimalPlaces(calculateDataCompression());
- BenchmarksCompleted++;
- }).thenRun(() -> {
- elapsedTimeNs = stopBenchmarkTimer();
- results();
- inProgress = false;
- });
-
- try {
- future.get();
- } catch (InterruptedException | ExecutionException e) {
- e.printStackTrace();
- }
- }
-
- public static void progressBar() {
- Iris.info("-----------------------------------------------------");
- Iris.info("Currently Running: " + C.BLUE + currentRunning);
- // Iris.info("Tasks: " + "Current Tasks: " + C.BLUE + currentTasks + C.WHITE + " / " + "Total Tasks: " + C.BLUE + totalTasks);
- Iris.info("Benchmarks Completed: " + C.BLUE + BenchmarksCompleted + C.WHITE + " / " + "Total: " + C.BLUE + BenchmarksTotal);
- Iris.info("-----------------------------------------------------");
- }
-
- public static void results() {
-
- SystemInfo systemInfo = new SystemInfo();
- GlobalMemory globalMemory = systemInfo.getHardware().getMemory();
- long totalMemoryMB = globalMemory.getTotal() / (1024 * 1024);
- long availableMemoryMB = globalMemory.getAvailable() / (1024 * 1024);
- long totalPageSize = globalMemory.getPageSize() / (1024 * 1024);
- long usedMemoryMB = totalMemoryMB - availableMemoryMB;
- MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
-
- Iris.info("OS: " + ServerOS);
- if (!isRunningAsAdmin() || !ServerOS.contains("Windows")) {
- Iris.info(C.GOLD + "For the full results use Windows + Admin Rights..");
- }
- Iris.info("CPU Model: " + getCPUModel());
- Iris.info("CPU Score: " + "WIP");
- Iris.info("- Integer Math: " + calculateIntegerMath + " MOps/Sec");
- Iris.info("- Floating Point Math: " + calculateFloatingPoint + " MOps/Sec");
- Iris.info("- Find Prime Numbers: " + calculatePrimeNumbers + " Primes/Sec");
- Iris.info("- Random String Sorting: " + calculateStringSorting + " Thousand Strings/Sec");
- Iris.info("- Data Encryption: " + formatDouble(calculateDataEncryption) + " MBytes/Sec");
- Iris.info("- Data Compression: " + formatDouble(calculateDataCompression) + " MBytes/Sec");
-
- if (WindowsDiskSpeed) {
- //Iris.info("Disk Model: " + getDiskModel());
- Iris.info(C.BLUE + "- Running with Windows System Assessment Tool");
- Iris.info("- Sequential 64.0 Write: " + C.BLUE + formatDouble(avgWriteSpeedMBps) + " Mbps");
- Iris.info("- Sequential 64.0 Read: " + C.BLUE + formatDouble(avgReadSpeedMBps) + " Mbps");
- } else {
- // Iris.info("Disk Model: " + getDiskModel());
- Iris.info(C.GREEN + "- Running in Native Mode");
- Iris.info("- Average Write Speed: " + C.GREEN + formatDouble(avgWriteSpeedMBps) + " Mbps");
- Iris.info("- Average Read Speed: " + C.GREEN + formatDouble(avgReadSpeedMBps) + " Mbps");
- Iris.info("- Highest Write Speed: " + formatDouble(highestWriteSpeedMBps) + " Mbps");
- Iris.info("- Highest Read Speed: " + formatDouble(highestReadSpeedMBps) + " Mbps");
- Iris.info("- Lowest Write Speed: " + formatDouble(lowestWriteSpeedMBps) + " Mbps");
- Iris.info("- Lowest Read Speed: " + formatDouble(lowestReadSpeedMBps) + " Mbps");
- }
- Iris.info("Ram Usage: ");
- Iris.info("- Total Ram: " + totalMemoryMB + " MB");
- Iris.info("- Used Ram: " + usedMemoryMB + " MB");
- Iris.info("- Total Process Ram: " + C.BLUE + getMaxMemoryUsage() + " MB");
- Iris.info("- Total Paging Size: " + totalPageSize + " MB");
- if (Winsat) {
- Iris.info(C.BLUE + "Windows System Assessment Tool: ");
- Iris.info("- CPU LZW Compression:" + C.BLUE + formatDouble(WindowsCPUCompression) + " MB/s");
- Iris.info("- CPU AES256 Encryption: " + C.BLUE + formatDouble(WindowsCPUEncryption) + " MB/s");
- Iris.info("- CPU SHA1 Hash: " + C.BLUE + formatDouble(WindowsCPUCSHA1) + " MB/s");
- Iris.info("Duration: " + roundToTwoDecimalPlaces(elapsedTimeNs) + " Seconds");
- }
-
- }
-
- public static long getMaxMemoryUsage() {
- MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
- MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();
- MemoryUsage nonHeapMemoryUsage = memoryMXBean.getNonHeapMemoryUsage();
- long maxHeapMemory = heapMemoryUsage.getMax();
- long maxNonHeapMemory = nonHeapMemoryUsage.getMax();
- long maxMemoryUsageMB = (maxHeapMemory + maxNonHeapMemory) / (1024 * 1024);
- return maxMemoryUsageMB;
- }
-
- public static void getServerOS() {
- SystemInfo systemInfo = new SystemInfo();
- OperatingSystem os = systemInfo.getOperatingSystem();
- ServerOS = os.toString();
- }
-
- public static boolean isRunningAsAdmin() {
- if (ServerOS.contains("Windows")) {
- try {
- Process process = Runtime.getRuntime().exec("winsat disk");
- process.waitFor();
- return process.exitValue() == 0;
- } catch (IOException | InterruptedException e) {
- // Hmm
- }
- }
- return false;
- }
-
- public static void warningFallback() {
- Iris.info(C.RED + "Using the " + C.DARK_RED + "FALLBACK" + C.RED + " method due to compatibility issues. ");
- Iris.info(C.RED + "Please note that this may result in less accurate results.");
- }
-
- private static String formatDouble(double value) {
- return String.format("%.2f", value);
- }
-
- private static void startBenchmarkTimer() {
- startTime = System.nanoTime();
- }
-
- private static double stopBenchmarkTimer() {
- long endTime = System.nanoTime();
- return (endTime - startTime) / 1_000_000_000.0;
- }
-
- private static double calculateIntegerMath() {
- final int numIterations = 1_000_000_000;
- final int numRuns = 30;
- double totalMopsPerSec = 0;
-
- for (int run = 0; run < numRuns; run++) {
- long startTime = System.nanoTime();
- int result = 0;
-
- for (int i = 0; i < numIterations; i++) {
- result += i * 2;
- result -= i / 2;
- result ^= i;
- result <<= 1;
- result >>= 1;
- }
-
- long endTime = System.nanoTime();
- double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
- double mopsPerSec = (numIterations / elapsedSeconds) / 1_000_000.0;
-
- totalMopsPerSec += mopsPerSec;
- }
-
- double averageMopsPerSec = totalMopsPerSec / numRuns;
- return averageMopsPerSec;
- }
-
- private static double calculateFloatingPoint() {
- long numIterations = 85_000_000;
- int numRuns = 30;
- double totalMopsPerSec = 0;
- for (int run = 0; run < numRuns; run++) {
- double result = 0;
- long startTime = System.nanoTime();
-
- for (int i = 0; i < numIterations; i++) {
- result += Math.sqrt(i) * Math.sin(i) / (i + 1);
- }
-
- long endTime = System.nanoTime();
- double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
- double mopsPerSec = (numIterations / elapsedSeconds) / 1_000_000.0;
-
- totalMopsPerSec += mopsPerSec;
- }
-
- double averageMopsPerSec = totalMopsPerSec / numRuns;
- return averageMopsPerSec;
- }
-
- private static double calculatePrimeNumbers() {
- int primeCount;
- long numIterations = 1_000_000;
- int numRuns = 30;
- double totalMopsPerSec = 0;
-
- for (int run = 0; run < numRuns; run++) {
- primeCount = 0;
- long startTime = System.nanoTime();
-
- for (int num = 2; primeCount < numIterations; num++) {
- if (isPrime(num)) {
- primeCount++;
- }
- }
-
- long endTime = System.nanoTime();
- double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
- double mopsPerSec = (primeCount / elapsedSeconds) / 1_000_000.0;
-
- totalMopsPerSec += mopsPerSec;
- }
-
- double averageMopsPerSec = totalMopsPerSec / numRuns;
- return averageMopsPerSec;
- }
-
- private static double calculateStringSorting() {
- int stringCount = 1_000_000;
- int stringLength = 100;
- int numRuns = 30;
- double totalMopsPerSec = 0;
-
- for (int run = 0; run < numRuns; run++) {
- List randomStrings = generateRandomStrings(stringCount, stringLength);
- long startTime = System.nanoTime();
- randomStrings.sort(String::compareTo);
- long endTime = System.nanoTime();
-
- double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
- double mopsPerSec = (stringCount / elapsedSeconds) / 1_000.0;
-
- totalMopsPerSec += mopsPerSec;
- }
-
- double averageMopsPerSec = totalMopsPerSec / numRuns;
- return averageMopsPerSec;
- }
-
- public static double calculateDataEncryption() {
- int dataSizeMB = 100;
- byte[] dataToEncrypt = generateRandomData(dataSizeMB * 1024 * 1024);
- int numRuns = 20;
- double totalMBytesPerSec = 0;
-
- for (int run = 0; run < numRuns; run++) {
- long startTime = System.nanoTime();
- byte[] encryptedData = performEncryption(dataToEncrypt, 1);
-
- long endTime = System.nanoTime();
- double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
- double mbytesPerSec = (dataToEncrypt.length / (1024 * 1024.0)) / elapsedSeconds;
-
- totalMBytesPerSec += mbytesPerSec;
- }
-
- double averageMBytesPerSec = totalMBytesPerSec / numRuns;
- return averageMBytesPerSec;
- }
-
- private static byte[] performEncryption(byte[] data, int numRuns) {
- byte[] key = "MyEncryptionKey".getBytes();
- byte[] result = Arrays.copyOf(data, data.length);
- for (int run = 0; run < numRuns; run++) {
- for (int i = 0; i < result.length; i++) {
- result[i] ^= key[i % key.length];
- }
- }
- return result;
- }
-
- public static double calculateDataCompression() {
- int dataSizeMB = 500;
- byte[] dataToCompress = generateRandomData(dataSizeMB * 1024 * 1024);
- long startTime = System.nanoTime();
- byte[] compressedData = performCompression(dataToCompress);
- long endTime = System.nanoTime();
-
- double elapsedSeconds = (endTime - startTime) / 1e9;
- double mbytesPerSec = (compressedData.length / (1024.0 * 1024.0)) / elapsedSeconds;
-
- return mbytesPerSec;
- }
-
- private static byte[] performCompression(byte[] data) {
- Deflater deflater = new Deflater();
- deflater.setInput(data);
- deflater.finish();
- ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
-
- byte[] buffer = new byte[1024];
- while (!deflater.finished()) {
- int count = deflater.deflate(buffer);
- outputStream.write(buffer, 0, count);
- }
-
- deflater.end();
- return outputStream.toByteArray();
- }
-
- private static List generateRandomStrings(int count, int length) {
- SecureRandom random = new SecureRandom();
- List randomStrings = new ArrayList<>();
-
- IntStream.range(0, count).forEach(i -> {
- byte[] bytes = new byte[length];
- random.nextBytes(bytes);
- randomStrings.add(Base64.getEncoder().encodeToString(bytes));
- });
- return randomStrings;
- }
-
- private static byte[] generateRandomData(int size) {
- SecureRandom random = new SecureRandom();
- byte[] data = new byte[size];
- random.nextBytes(data);
- return data;
- }
-
- private static double roundToTwoDecimalPlaces(double value) {
- return Double.parseDouble(String.format("%.2f", value));
- }
-
- private static double calculateCPUScore(long elapsedTimeNs) {
- return 1.0 / (elapsedTimeNs / 1_000_000.0);
- }
-
- public static double calculateDiskSpeed() {
- int numRuns = 10;
- int fileSizeMB = 1000;
-
- double[] writeSpeeds = new double[numRuns];
- double[] readSpeeds = new double[numRuns];
-
- for (int run = 0; run < numRuns; run++) {
- long writeStartTime = System.nanoTime();
- deleteTestFile(filePath);
- createTestFile(filePath, fileSizeMB);
- long writeEndTime = System.nanoTime();
-
- long readStartTime = System.nanoTime();
- readTestFile(filePath);
- long readEndTime = System.nanoTime();
-
- double writeSpeed = calculateDiskSpeedMBps(fileSizeMB, writeStartTime, writeEndTime);
- double readSpeed = calculateDiskSpeedMBps(fileSizeMB, readStartTime, readEndTime);
-
- writeSpeeds[run] = writeSpeed;
- readSpeeds[run] = readSpeed;
-
- if (run == 0) {
- lowestWriteSpeedMBps = writeSpeed;
- highestWriteSpeedMBps = writeSpeed;
- lowestReadSpeedMBps = readSpeed;
- highestReadSpeedMBps = readSpeed;
- } else {
- if (writeSpeed < lowestWriteSpeedMBps) {
- lowestWriteSpeedMBps = writeSpeed;
- }
- if (writeSpeed > highestWriteSpeedMBps) {
- highestWriteSpeedMBps = writeSpeed;
- }
- if (readSpeed < lowestReadSpeedMBps) {
- lowestReadSpeedMBps = readSpeed;
- }
- if (readSpeed > highestReadSpeedMBps) {
- highestReadSpeedMBps = readSpeed;
- }
- }
- }
- avgWriteSpeedMBps = calculateAverage(writeSpeeds);
- avgReadSpeedMBps = calculateAverage(readSpeeds);
- return 2;
- }
-
- public static void createTestFile(String filePath, int fileSizeMB) {
- try {
- File file = new File(filePath);
- byte[] data = new byte[1024 * 1024];
- Arrays.fill(data, (byte) 0);
- FileOutputStream fos = new FileOutputStream(file);
- for (int i = 0; i < fileSizeMB; i++) {
- fos.write(data);
- }
- fos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- public static void readTestFile(String filePath) {
- try {
- File file = new File(filePath);
- FileInputStream fis = new FileInputStream(file);
- byte[] buffer = new byte[1024];
- while (fis.read(buffer) != -1) {
- }
- fis.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- public static void deleteTestFile(String filePath) {
- File file = new File(filePath);
- file.delete();
- }
-
- public static double calculateDiskSpeedMBps(int fileSizeMB, long startTime, long endTime) {
- double elapsedSeconds = (endTime - startTime) / 1_000_000_000.0;
- double writeSpeed = (fileSizeMB / elapsedSeconds);
- return writeSpeed;
- }
-
- public static double calculateAverage(double[] values) {
- double sum = 0;
- for (double value : values) {
- sum += value;
- }
- return sum / values.length;
- }
-
- public static void WindowsDiskSpeedTest() {
- try {
- String command = "winsat disk";
- Process process = Runtime.getRuntime().exec(command);
- BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
- String line;
-
- while ((line = reader.readLine()) != null) {
- Iris.debug(line);
-
- if (line.contains("Disk Sequential 64.0 Read")) {
- avgReadSpeedMBps = extractSpeed(line);
- } else if (line.contains("Disk Sequential 64.0 Write")) {
- avgWriteSpeedMBps = extractSpeed(line);
- }
- }
-
- process.waitFor();
- process.destroy();
-
- Iris.debug("Sequential Read Speed: " + avgReadSpeedMBps + " MB/s");
- Iris.debug("Sequential Write Speed: " + avgWriteSpeedMBps + " MB/s");
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private static double extractSpeed(String line) {
- String[] tokens = line.split("\\s+");
- for (int i = 0; i < tokens.length; i++) {
- if (tokens[i].endsWith("MB/s") && i > 0) {
- try {
- return Double.parseDouble(tokens[i - 1]);
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- }
- }
- return 0.0;
- }
-
- public static void WindowsCpuSpeedTest() {
- try {
- String command = "winsat cpuformal";
- Process process = Runtime.getRuntime().exec(command);
- BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
- String line;
-
- while ((line = reader.readLine()) != null) {
- Iris.debug(line);
-
- if (line.contains("CPU AES256 Encryption")) {
- WindowsCPUEncryption = extractCpuInfo(line);
- }
- if (line.contains("CPU LZW Compression")) {
- WindowsCPUCompression = extractCpuInfo(line);
- }
- if (line.contains("CPU SHA1 Hash")) {
- WindowsCPUCSHA1 = extractCpuInfo(line);
- }
- }
- process.waitFor();
- process.destroy();
-
- Iris.debug("Winsat Encryption: " + WindowsCPUEncryption + " MB/s");
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private static double extractCpuInfo(String line) {
- String[] tokens = line.split("\\s+");
- for (int i = 0; i < tokens.length; i++) {
- if (tokens[i].endsWith("MB/s") && i > 0) {
- try {
- return Double.parseDouble(tokens[i - 1]);
- } catch (NumberFormatException e) {
- e.printStackTrace();
- }
- }
- }
- return 0.0;
- }
-
-}
\ No newline at end of file
diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java b/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java
index ad552ee81..15b3dc8d3 100644
--- a/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java
+++ b/core/src/main/java/com/volmit/iris/core/tools/IrisToolbelt.java
@@ -24,6 +24,7 @@ import com.volmit.iris.core.gui.PregeneratorJob;
import com.volmit.iris.core.loader.IrisData;
import com.volmit.iris.core.pregenerator.PregenTask;
import com.volmit.iris.core.pregenerator.PregeneratorMethod;
+import com.volmit.iris.core.pregenerator.methods.CachedPregenMethod;
import com.volmit.iris.core.pregenerator.methods.HybridPregenMethod;
import com.volmit.iris.core.service.StudioSVC;
import com.volmit.iris.engine.framework.Engine;
@@ -141,7 +142,18 @@ public class IrisToolbelt {
* @return the pregenerator job (already started)
*/
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine) {
- return new PregeneratorJob(task, method, engine);
+ return pregenerate(task, method, engine, true);
+ }
+
+ /**
+ * Start a pregenerator task
+ *
+ * @param task the scheduled task
+ * @param method the method to execute the task
+ * @return the pregenerator job (already started)
+ */
+ public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine engine, boolean cached) {
+ return new PregeneratorJob(task, cached && engine != null ? new CachedPregenMethod(method, engine.getWorld().name()) : method, engine);
}
/**
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 8d08771fd..fe99a322d 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
@@ -75,6 +75,7 @@ import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.Nullable;
import java.awt.Color;
import java.util.Arrays;
@@ -852,6 +853,25 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ());
}
+ @Nullable
+ @BlockCoordinates
+ default Position2 getNearestStronghold(Position2 pos) {
+ KList p = getDimension().getStrongholds(getSeedManager().getMantle());
+ if (p.isEmpty()) return null;
+
+ Position2 pr = null;
+ double d = Double.MAX_VALUE;
+
+ for (Position2 i : p) {
+ double dx = i.distance(pos);
+ if (dx < d) {
+ d = dx;
+ pr = i;
+ }
+ }
+ return pr;
+ }
+
default void gotoBiome(IrisBiome biome, Player player, boolean teleport) {
Set regionKeys = getDimension()
.getAllRegions(this).stream()
@@ -872,31 +892,10 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) {
if (s.getLoadKey().equals(getDimension().getStronghold())) {
- KList p = getDimension().getStrongholds(getSeedManager().getMantle());
-
- if (p.isEmpty()) {
+ Position2 pr = getNearestStronghold(new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ()));
+ if (pr == null) {
player.sendMessage(C.GOLD + "No strongholds in world.");
- }
-
- Position2 px = new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ());
- Position2 pr = null;
- double d = Double.MAX_VALUE;
-
- Iris.debug("Ps: " + p.size());
-
- for (Position2 i : p) {
- Iris.debug("- " + i.getX() + " " + i.getZ());
- }
-
- for (Position2 i : p) {
- double dx = i.distance(px);
- if (dx < d) {
- d = dx;
- pr = i;
- }
- }
-
- if (pr != null) {
+ } else {
Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ());
J.s(() -> player.teleport(ll));
}
diff --git a/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedWorldManager.java b/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedWorldManager.java
index 5f801033f..e53b35e5c 100644
--- a/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedWorldManager.java
+++ b/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedWorldManager.java
@@ -97,51 +97,6 @@ public abstract class EngineAssignedWorldManager extends EngineAssignedComponent
}
}
- @EventHandler
- public void onItemUse(PlayerInteractEvent e) {
- if (e.getItem() == null || e.getHand() != EquipmentSlot.HAND) {
- return;
- }
- if (e.getAction() == Action.LEFT_CLICK_BLOCK || e.getAction() == Action.LEFT_CLICK_AIR) {
- return;
- }
- if (e.getPlayer().getWorld().equals(getTarget().getWorld().realWorld()) && e.getItem().getType() == Material.ENDER_EYE) {
- if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.END_PORTAL_FRAME) {
- return;
- }
-
- KList positions = getEngine().getDimension().getStrongholds(getEngine().getSeedManager().getMantle());
- if (positions.isEmpty()) {
- return;
- }
-
- Position2 playerPos = new Position2(e.getPlayer().getLocation().getBlockX(), e.getPlayer().getLocation().getBlockZ());
- Position2 pr = positions.get(0);
- double d = pr.distance(playerPos);
-
- for (Position2 pos : positions) {
- double distance = pos.distance(playerPos);
- if (distance < d) {
- d = distance;
- pr = pos;
- }
- }
-
- if (e.getPlayer().getGameMode() != GameMode.CREATIVE) {
- if (e.getItem().getAmount() > 1) {
- e.getPlayer().getInventory().getItemInMainHand().setAmount(e.getItem().getAmount() - 1);
- } else {
- e.getPlayer().getInventory().setItemInMainHand(null);
- }
- }
-
- EnderSignal eye = e.getPlayer().getWorld().spawn(e.getPlayer().getLocation().clone().add(0, 0.5F, 0), EnderSignal.class);
- eye.setTargetLocation(new Location(e.getPlayer().getWorld(), pr.getX(), 40, pr.getZ()));
- eye.getWorld().playSound(eye, Sound.ENTITY_ENDER_EYE_LAUNCH, 1, 1);
- Iris.debug("ESignal: " + eye.getTargetLocation().getBlockX() + " " + eye.getTargetLocation().getBlockX());
- }
- }
-
@EventHandler
public void on(WorldUnloadEvent e) {
if (e.getWorld().equals(getTarget().getWorld().realWorld())) {
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 cf840572e..8a71ac48c 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
@@ -24,12 +24,14 @@ 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;
+import org.jetbrains.annotations.NotNull;
+import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
-public class MultiBurst {
+public class MultiBurst implements ExecutorService {
public static final MultiBurst burst = new MultiBurst();
private final AtomicLong last;
private final String name;
@@ -144,29 +146,106 @@ public class MultiBurst {
return getService().submit(o);
}
+ @Override
+ public void shutdown() {
+ close();
+ }
+
+ @NotNull
+ @Override
+ public List shutdownNow() {
+ close();
+ return List.of();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return service == null || service.isShutdown();
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return service == null || service.isTerminated();
+ }
+
+ @Override
+ public boolean awaitTermination(long timeout, @NotNull TimeUnit unit) throws InterruptedException {
+ return service == null || service.awaitTermination(timeout, unit);
+ }
+
+ @Override
+ public void execute(@NotNull Runnable command) {
+ getService().execute(command);
+ }
+
+ @NotNull
+ @Override
+ public Future submit(@NotNull Callable task) {
+ return getService().submit(task);
+ }
+
+ @NotNull
+ @Override
+ public Future submit(@NotNull Runnable task, T result) {
+ return getService().submit(task, result);
+ }
+
+ @NotNull
+ @Override
+ public Future> submit(@NotNull Runnable task) {
+ return getService().submit(task);
+ }
+
+ @NotNull
+ @Override
+ public List> invokeAll(@NotNull Collection extends Callable> tasks) throws InterruptedException {
+ return getService().invokeAll(tasks);
+ }
+
+ @NotNull
+ @Override
+ public List> invokeAll(@NotNull Collection extends Callable> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException {
+ return getService().invokeAll(tasks, timeout, unit);
+ }
+
+ @NotNull
+ @Override
+ public T invokeAny(@NotNull Collection extends Callable> tasks) throws InterruptedException, ExecutionException {
+ return getService().invokeAny(tasks);
+ }
+
+ @Override
+ public T invokeAny(@NotNull Collection extends Callable> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return getService().invokeAny(tasks, timeout, unit);
+ }
+
public void close() {
if (service != null) {
- service.shutdown();
- PrecisionStopwatch p = PrecisionStopwatch.start();
- try {
- while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
- Iris.info("Still waiting to shutdown burster...");
- if (p.getMilliseconds() > 7000) {
- Iris.warn("Forcing Shutdown...");
+ close(service);
+ }
+ }
- try {
- service.shutdownNow();
- } catch (Throwable e) {
+ public static void close(ExecutorService service) {
+ service.shutdown();
+ PrecisionStopwatch p = PrecisionStopwatch.start();
+ try {
+ while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
+ Iris.info("Still waiting to shutdown burster...");
+ if (p.getMilliseconds() > 7000) {
+ Iris.warn("Forcing Shutdown...");
- }
+ try {
+ service.shutdownNow();
+ } catch (Throwable e) {
- break;
}
+
+ break;
}
- } catch (Throwable e) {
- e.printStackTrace();
- Iris.reportError(e);
}
+ } catch (Throwable e) {
+ e.printStackTrace();
+ Iris.reportError(e);
}
}
}
diff --git a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java
index 30873ea31..bebaf4f36 100644
--- a/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java
+++ b/nms/v1_20_R1/src/main/java/com/volmit/iris/core/nms/v1_20_R1/IrisChunkGenerator.java
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
@@ -122,6 +123,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) {
+ if (holders.size() == 0) return null;
+ if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
+ var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
+ return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
+ }
if (engine.getDimension().isDisableExplorerMaps())
return null;
diff --git a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java
index 0d44523fe..e2dae5081 100644
--- a/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java
+++ b/nms/v1_20_R2/src/main/java/com/volmit/iris/core/nms/v1_20_R2/IrisChunkGenerator.java
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
@@ -122,6 +123,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) {
+ if (holders.size() == 0) return null;
+ if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
+ var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
+ return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
+ }
if (engine.getDimension().isDisableExplorerMaps())
return null;
diff --git a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java
index 26826709b..957901b41 100644
--- a/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java
+++ b/nms/v1_20_R3/src/main/java/com/volmit/iris/core/nms/v1_20_R3/IrisChunkGenerator.java
@@ -125,6 +125,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) {
+ if (holders.size() == 0) return null;
+ if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
+ var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
+ return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
+ }
if (engine.getDimension().isDisableExplorerMaps())
return null;
diff --git a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java
index 537acc9d9..728638cc3 100644
--- a/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java
+++ b/nms/v1_20_R4/src/main/java/com/volmit/iris/core/nms/v1_20_R4/IrisChunkGenerator.java
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
@@ -120,6 +121,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) {
+ if (holders.size() == 0) return null;
+ if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
+ var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
+ return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
+ }
if (engine.getDimension().isDisableExplorerMaps())
return null;
diff --git a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java
index 461446ed2..046c6a283 100644
--- a/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java
+++ b/nms/v1_21_R1/src/main/java/com/volmit/iris/core/nms/v1_21_R1/IrisChunkGenerator.java
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
@@ -121,6 +122,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) {
+ if (holders.size() == 0) return null;
+ if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
+ var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
+ return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
+ }
if (engine.getDimension().isDisableExplorerMaps())
return null;
diff --git a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/IrisChunkGenerator.java b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/IrisChunkGenerator.java
index 9dc702b05..a2d273393 100644
--- a/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/IrisChunkGenerator.java
+++ b/nms/v1_21_R2/src/main/java/com/volmit/iris/core/nms/v1_21_R2/IrisChunkGenerator.java
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
@@ -120,6 +121,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) {
+ if (holders.size() == 0) return null;
+ if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
+ var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
+ return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
+ }
if (engine.getDimension().isDisableExplorerMaps())
return null;
diff --git a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/IrisChunkGenerator.java b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/IrisChunkGenerator.java
index 653959edf..ef96101ac 100644
--- a/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/IrisChunkGenerator.java
+++ b/nms/v1_21_R3/src/main/java/com/volmit/iris/core/nms/v1_21_R3/IrisChunkGenerator.java
@@ -20,9 +20,11 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.tags.StructureTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.random.WeightedRandomList;
import net.minecraft.world.entity.MobCategory;
+import net.minecraft.world.item.EnderEyeItem;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.*;
import net.minecraft.world.level.chunk.ChunkAccess;
@@ -31,6 +33,7 @@ import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.blending.Blender;
+import net.minecraft.world.level.levelgen.structure.BuiltinStructures;
import net.minecraft.world.level.levelgen.structure.Structure;
import net.minecraft.world.level.levelgen.structure.StructureSet;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
@@ -114,6 +117,11 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
@Override
public @Nullable Pair> findNearestMapStructure(ServerLevel level, HolderSet holders, BlockPos pos, int radius, boolean findUnexplored) {
+ if (holders.size() == 0) return null;
+ if (holders.unwrapKey().orElse(null) == StructureTags.EYE_OF_ENDER_LOCATED) {
+ var next = engine.getNearestStronghold(new Position2(pos.getX(), pos.getZ()));
+ return next == null ? null : new Pair<>(new BlockPos(next.getX(), 0, next.getZ()), holders.get(0));
+ }
if (engine.getDimension().isDisableExplorerMaps())
return null;