mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-07-16 11:53:42 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9b96b4d619 | |||
| 9e0258089b | |||
| e6874a0e10 | |||
| 647214a335 |
+1
-1
@@ -36,7 +36,7 @@ plugins {
|
||||
}
|
||||
|
||||
|
||||
version '3.6.7-1.20.1-1.21.4'
|
||||
version '3.6.6-1.20.1-1.21.4'
|
||||
|
||||
// ADD YOURSELF AS A NEW LINE IF YOU WANT YOUR OWN BUILD TASK GENERATED
|
||||
// ======================== WINDOWS =============================
|
||||
|
||||
@@ -624,22 +624,12 @@ public class Iris extends VolmitPlugin implements Listener {
|
||||
Iris.warn("=");
|
||||
Iris.warn("============================================");
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!instance.getServer().getVersion().contains("Purpur")) {
|
||||
passed = false;
|
||||
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;
|
||||
}
|
||||
@@ -864,6 +854,13 @@ 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");
|
||||
|
||||
@@ -24,6 +24,7 @@ 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;
|
||||
@@ -44,7 +45,6 @@ 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,7 +130,6 @@ public class IrisSettings {
|
||||
public boolean markerEntitySpawningSystem = true;
|
||||
public boolean effectSystem = true;
|
||||
public boolean worldEditWandCUI = true;
|
||||
public boolean globalPregenCache = true;
|
||||
}
|
||||
|
||||
@Data
|
||||
@@ -138,12 +137,6 @@ 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;
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Iris is a World Generator for Minecraft Bukkit Servers
|
||||
* Copyright (c) 2022 Arcane Arts (Volmit Software)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.volmit.iris.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.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,11 @@ import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.ServerConfigurator;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisDimension;
|
||||
import com.volmit.iris.engine.service.EngineStatusSVC;
|
||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
||||
import com.volmit.iris.util.decree.DecreeOrigin;
|
||||
import com.volmit.iris.util.decree.annotations.Decree;
|
||||
@@ -36,7 +36,6 @@ 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;
|
||||
@@ -63,94 +62,24 @@ 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)
|
||||
public void EngineStatus() {
|
||||
List<World> IrisWorlds = new ArrayList<>();
|
||||
int TotalLoadedChunks = 0;
|
||||
int TotalQueuedTectonicPlates = 0;
|
||||
int TotalNotQueuedTectonicPlates = 0;
|
||||
int TotalTectonicPlates = 0;
|
||||
var status = EngineStatusSVC.getStatus();
|
||||
|
||||
long lowestUnloadDuration = 0;
|
||||
long highestUnloadDuration = 0;
|
||||
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
try {
|
||||
if (IrisToolbelt.access(world).getEngine() != null) {
|
||||
IrisWorlds.add(world);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// no
|
||||
}
|
||||
}
|
||||
|
||||
for (World world : IrisWorlds) {
|
||||
Engine engine = IrisToolbelt.access(world).getEngine();
|
||||
TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload();
|
||||
TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions();
|
||||
TotalTectonicPlates += engine.getMantle().getLoadedRegionCount();
|
||||
if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) {
|
||||
highestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
|
||||
}
|
||||
if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) {
|
||||
lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
|
||||
}
|
||||
for (Chunk chunk : world.getLoadedChunks()) {
|
||||
if (chunk.isLoaded()) {
|
||||
TotalLoadedChunks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
Iris.info("-------------------------");
|
||||
Iris.info(C.DARK_PURPLE + "Engine Status");
|
||||
Iris.info(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + TotalLoadedChunks);
|
||||
Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit());
|
||||
Iris.info(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + TotalTectonicPlates);
|
||||
Iris.info(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + TotalNotQueuedTectonicPlates);
|
||||
Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + TotalQueuedTectonicPlates);
|
||||
Iris.info(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(lowestUnloadDuration));
|
||||
Iris.info(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(highestUnloadDuration));
|
||||
Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
|
||||
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<Thread, StackTraceElement[]> 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();
|
||||
}
|
||||
sender().sendMessage("-------------------------");
|
||||
sender().sendMessage(C.DARK_PURPLE + "Engine Status");
|
||||
sender().sendMessage(C.DARK_PURPLE + "Total Engines: " + C.LIGHT_PURPLE + status.engineCount());
|
||||
sender().sendMessage(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + status.loadedChunks());
|
||||
sender().sendMessage(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + status.tectonicLimit());
|
||||
sender().sendMessage(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + status.tectonicPlates());
|
||||
sender().sendMessage(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + status.activeTectonicPlates());
|
||||
sender().sendMessage(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + status.queuedTectonicPlates());
|
||||
sender().sendMessage(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(status.minTectonicUnloadDuration()));
|
||||
sender().sendMessage(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(status.maxTectonicUnloadDuration()));
|
||||
sender().sendMessage(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
|
||||
sender().sendMessage("-------------------------");
|
||||
}
|
||||
|
||||
@Decree(description = "Test")
|
||||
|
||||
@@ -24,6 +24,7 @@ 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;
|
||||
@@ -58,6 +59,7 @@ 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;
|
||||
@@ -66,6 +68,7 @@ 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;
|
||||
@@ -172,6 +175,16 @@ 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)
|
||||
|
||||
@@ -39,9 +39,7 @@ 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,
|
||||
@Param(description = "Open the Iris pregen gui", defaultValue = "true")
|
||||
boolean gui
|
||||
Vector center
|
||||
) {
|
||||
try {
|
||||
if (sender().isPlayer() && access() == null) {
|
||||
@@ -52,7 +50,7 @@ public class CommandPregen implements DecreeExecutor {
|
||||
IrisToolbelt.pregenerate(PregenTask
|
||||
.builder()
|
||||
.center(new Position2(center.getBlockX(), center.getBlockZ()))
|
||||
.gui(gui)
|
||||
.gui(true)
|
||||
.radiusX(radius)
|
||||
.radiusZ(radius)
|
||||
.build(), world);
|
||||
|
||||
@@ -21,7 +21,6 @@ package com.volmit.iris.core.commands;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.pregenerator.ChunkUpdater;
|
||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
|
||||
@@ -40,8 +40,6 @@ 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;
|
||||
@@ -66,7 +64,6 @@ 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;
|
||||
@@ -99,7 +96,6 @@ public class PregeneratorJob implements PregenListener {
|
||||
}, "Iris Pregenerator");
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
t.start();
|
||||
service = Executors.newVirtualThreadPerTaskExecutor();
|
||||
}
|
||||
|
||||
public static boolean shutdownInstance() {
|
||||
@@ -223,10 +219,10 @@ public class PregeneratorJob implements PregenListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
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) {
|
||||
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method) {
|
||||
info = new String[]{
|
||||
(paused() ? "PAUSED" : (saving ? "Saving... " : "Generating")) + " " + Form.f(generated) + " of " + Form.f(totalChunks) + " (" + Form.pc(percent, 0) + " Complete)",
|
||||
"Speed: " + (cached ? "Cached " : "") + Form.f(chunksPerSecond, 0) + " Chunks/s, " + Form.f(regionsPerMinute, 1) + " Regions/m, " + Form.f(chunksPerMinute, 0) + " Chunks/m",
|
||||
"Speed: " + 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",
|
||||
@@ -244,16 +240,13 @@ public class PregeneratorJob implements PregenListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
public void onChunkGenerated(int x, int z) {
|
||||
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
|
||||
@@ -313,7 +306,6 @@ public class PregeneratorJob implements PregenListener {
|
||||
close();
|
||||
instance = null;
|
||||
whenDone.forEach(Runnable::run);
|
||||
service.shutdownNow();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -44,6 +44,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
@@ -219,6 +220,10 @@ public class ResourceLoader<T extends IrisRegistrant> implements MeteredCache {
|
||||
return s.map(this::load);
|
||||
}
|
||||
|
||||
public Stream<T> streamAllPossible() {
|
||||
return streamAll(Arrays.stream(getPossibleKeys()));
|
||||
}
|
||||
|
||||
public KList<T> loadAll(KList<String> s) {
|
||||
KList<T> m = new KList<>();
|
||||
|
||||
|
||||
@@ -31,33 +31,28 @@ 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<Integer> chunksPerSecondHistory;
|
||||
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 static AtomicInteger generated;
|
||||
private final AtomicInteger generatedLast;
|
||||
private final AtomicInteger generatedLastMinute;
|
||||
private static AtomicInteger totalChunks;
|
||||
private final AtomicLong startTime;
|
||||
private final ChronoLatch minuteLatch;
|
||||
private final AtomicReference<String> currentGeneratorMethod;
|
||||
@@ -79,71 +74,46 @@ 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 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);
|
||||
generated = new AtomicInteger(0);
|
||||
generatedLast = new AtomicInteger(0);
|
||||
generatedLastMinute = new AtomicInteger(0);
|
||||
totalChunks = new AtomicInteger(0);
|
||||
task.iterateAllChunks((_a, _b) -> totalChunks.incrementAndGet());
|
||||
startTime = new AtomicLong(M.ms());
|
||||
ticker = new Looper() {
|
||||
@Override
|
||||
protected long loop() {
|
||||
long eta = computeETA();
|
||||
|
||||
long secondCached = cached.get() - cachedLast.get();
|
||||
cachedLast.set(cached.get());
|
||||
cachedPerSecond.put(secondCached);
|
||||
|
||||
long secondGenerated = generated.get() - generatedLast.get() - secondCached;
|
||||
int secondGenerated = generated.get() - generatedLast.get();
|
||||
generatedLast.set(generated.get());
|
||||
if (secondCached == 0 || secondGenerated != 0) {
|
||||
chunksPerSecond.put(secondGenerated);
|
||||
chunksPerSecondHistory.add((int) secondGenerated);
|
||||
}
|
||||
chunksPerSecond.put(secondGenerated);
|
||||
chunksPerSecondHistory.add(secondGenerated);
|
||||
|
||||
if (minuteLatch.flip()) {
|
||||
long minuteCached = cached.get() - cachedLastMinute.get();
|
||||
cachedLastMinute.set(cached.get());
|
||||
|
||||
long minuteGenerated = generated.get() - generatedLastMinute.get() - minuteCached;
|
||||
int minuteGenerated = generated.get() - generatedLastMinute.get();
|
||||
generatedLastMinute.set(generated.get());
|
||||
if (minuteCached == 0 || minuteGenerated != 0) {
|
||||
chunksPerMinute.put(minuteGenerated);
|
||||
regionsPerMinute.put((double) minuteGenerated / 1024D);
|
||||
}
|
||||
chunksPerMinute.put(minuteGenerated);
|
||||
regionsPerMinute.put((double) minuteGenerated / 1024D);
|
||||
}
|
||||
boolean cached = cachedPerSecond.getAverage() != 0;
|
||||
|
||||
listener.onTick(
|
||||
cached ? cachedPerSecond.getAverage() : chunksPerSecond.getAverage(),
|
||||
chunksPerMinute.getAverage(),
|
||||
listener.onTick(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(),
|
||||
cached);
|
||||
(double) generated.get() / (double) totalChunks.get(),
|
||||
generated.get(), totalChunks.get(),
|
||||
totalChunks.get() - generated.get(),
|
||||
eta, M.ms() - startTime.get(), currentGeneratorMethod.get());
|
||||
|
||||
if (cl.flip()) {
|
||||
double percentage = ((double) generated.get() / (double) totalChunks.get()) * 100;
|
||||
|
||||
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)
|
||||
);
|
||||
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);
|
||||
}
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
@@ -151,13 +121,12 @@ public class IrisPregenerator {
|
||||
}
|
||||
|
||||
private long computeETA() {
|
||||
double d = (long) (totalChunks.get() > 1024 ? // Generated chunks exceed 1/8th of total?
|
||||
return (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() - cached.get()) * ((double) (M.ms() - startTime.get()) / ((double) generated.get() - cached.get()))) :
|
||||
((totalChunks.get() - generated.get()) * ((double) (M.ms() - startTime.get()) / (double) generated.get())) :
|
||||
// If no, use quick function (which is less accurate over time but responds better to the initial delay)
|
||||
((totalChunks.get() - generated.get() - cached.get()) / chunksPerSecond.getAverage()) * 1000
|
||||
((totalChunks.get() - generated.get()) / chunksPerSecond.getAverage()) * 1000
|
||||
);
|
||||
return Double.isFinite(d) && d != INVALID ? (long) d : 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -169,10 +138,8 @@ 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.");
|
||||
@@ -267,8 +234,8 @@ public class IrisPregenerator {
|
||||
private PregenListener listenify(PregenListener listener) {
|
||||
return new PregenListener() {
|
||||
@Override
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -277,10 +244,9 @@ public class IrisPregenerator {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkGenerated(int x, int z, boolean c) {
|
||||
listener.onChunkGenerated(x, z, c);
|
||||
public void onChunkGenerated(int x, int z) {
|
||||
listener.onChunkGenerated(x, z);
|
||||
generated.addAndGet(1);
|
||||
if (c) cached.addAndGet(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,15 +19,11 @@
|
||||
package com.volmit.iris.core.pregenerator;
|
||||
|
||||
public interface PregenListener {
|
||||
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 onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, int generated, int totalChunks, int chunksRemaining, long eta, long elapsed, String method);
|
||||
|
||||
void onChunkGenerating(int x, int z);
|
||||
|
||||
default void onChunkGenerated(int x, int z) {
|
||||
onChunkGenerated(x, z, false);
|
||||
}
|
||||
|
||||
void onChunkGenerated(int x, int z, boolean cached);
|
||||
void onChunkGenerated(int x, int z);
|
||||
|
||||
void onRegionGenerated(int x, int z);
|
||||
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
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() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
-215
@@ -1,215 +0,0 @@
|
||||
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<Pos, Plate> 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<Region> 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<Region> 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) {}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-13
@@ -19,7 +19,6 @@
|
||||
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;
|
||||
@@ -34,15 +33,12 @@ 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 ExecutorService service;
|
||||
private final MultiBurst burst;
|
||||
private final Semaphore semaphore;
|
||||
private final int threads;
|
||||
private final Map<Chunk, Long> lastUse;
|
||||
|
||||
public AsyncPregenMethod(World world, int threads) {
|
||||
@@ -51,11 +47,8 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
}
|
||||
|
||||
this.world = world;
|
||||
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);
|
||||
burst = new MultiBurst("Iris Async Pregen", Thread.MIN_PRIORITY);
|
||||
semaphore = new Semaphore(256);
|
||||
this.lastUse = new KMap<>();
|
||||
}
|
||||
|
||||
@@ -108,9 +101,9 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
semaphore.acquireUninterruptibly(threads);
|
||||
semaphore.acquireUninterruptibly(256);
|
||||
unloadAndSaveAllChunks();
|
||||
service.shutdown();
|
||||
burst.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,7 +129,7 @@ public class AsyncPregenMethod implements PregeneratorMethod {
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
service.submit(() -> completeChunk(x, z, listener));
|
||||
burst.complete(() -> completeChunk(x, z, listener));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
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<String, PregenCache> REFERENCE_CACHE = Caffeine.newBuilder().weakValues().build();
|
||||
private final KMap<String, PregenCache> 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<String, PregenCache> 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();
|
||||
}
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
package com.volmit.iris.core.service;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.plugin.IrisService;
|
||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
||||
import com.volmit.iris.util.scheduling.Looper;
|
||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.server.PluginDisableEvent;
|
||||
import org.bukkit.event.server.ServerLoadEvent;
|
||||
import org.bukkit.event.world.WorldLoadEvent;
|
||||
import org.bukkit.event.world.WorldUnloadEvent;
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class IrisEngineSVC implements IrisService {
|
||||
public static IrisEngineSVC instance;
|
||||
public boolean isServerShuttingDown = false;
|
||||
public boolean isServerLoaded = false;
|
||||
private static final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
||||
private ReentrantLock lastUseLock;
|
||||
private KMap<World, Long> lastUse;
|
||||
private List<World> IrisWorlds;
|
||||
private Looper cacheTicker;
|
||||
private Looper trimTicker;
|
||||
private Looper unloadTicker;
|
||||
private Looper updateTicker;
|
||||
private PrecisionStopwatch trimAlive;
|
||||
private PrecisionStopwatch unloadAlive;
|
||||
public PrecisionStopwatch trimActiveAlive;
|
||||
public PrecisionStopwatch unloadActiveAlive;
|
||||
private AtomicInteger TotalTectonicPlates;
|
||||
private AtomicInteger TotalQueuedTectonicPlates;
|
||||
private AtomicInteger TotalNotQueuedTectonicPlates;
|
||||
private AtomicBoolean IsUnloadAlive;
|
||||
private AtomicBoolean IsTrimAlive;
|
||||
ChronoLatch cl;
|
||||
|
||||
public List<World> corruptedIrisWorlds = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
this.cl = new ChronoLatch(5000);
|
||||
lastUse = new KMap<>();
|
||||
lastUseLock = new ReentrantLock();
|
||||
IrisWorlds = new ArrayList<>();
|
||||
IsUnloadAlive = new AtomicBoolean(true);
|
||||
IsTrimAlive = new AtomicBoolean(true);
|
||||
trimActiveAlive = new PrecisionStopwatch();
|
||||
unloadActiveAlive = new PrecisionStopwatch();
|
||||
trimAlive = new PrecisionStopwatch();
|
||||
unloadAlive = new PrecisionStopwatch();
|
||||
TotalTectonicPlates = new AtomicInteger();
|
||||
TotalQueuedTectonicPlates = new AtomicInteger();
|
||||
TotalNotQueuedTectonicPlates = new AtomicInteger();
|
||||
tectonicLimit.set(2);
|
||||
long t = getHardware.getProcessMemory();
|
||||
while (t > 200) {
|
||||
tectonicLimit.getAndAdd(1);
|
||||
t = t - 200;
|
||||
}
|
||||
this.setup();
|
||||
this.TrimLogic();
|
||||
this.UnloadLogic();
|
||||
|
||||
trimAlive.begin();
|
||||
unloadAlive.begin();
|
||||
trimActiveAlive.begin();
|
||||
unloadActiveAlive.begin();
|
||||
|
||||
updateTicker.start();
|
||||
cacheTicker.start();
|
||||
//trimTicker.start();
|
||||
//unloadTicker.start();
|
||||
instance = this;
|
||||
|
||||
}
|
||||
|
||||
public void engineStatus() {
|
||||
boolean trimAlive = trimTicker.isAlive();
|
||||
boolean unloadAlive = unloadTicker.isAlive();
|
||||
Iris.info("Status:");
|
||||
Iris.info("- Trim: " + trimAlive);
|
||||
Iris.info("- Unload: " + unloadAlive);
|
||||
|
||||
}
|
||||
|
||||
public static int getTectonicLimit() {
|
||||
return tectonicLimit.get();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldUnload(WorldUnloadEvent event) {
|
||||
updateWorlds();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onWorldLoad(WorldLoadEvent event) {
|
||||
updateWorlds();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onServerBoot(ServerLoadEvent event) {
|
||||
isServerLoaded = true;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPluginDisable(PluginDisableEvent event) {
|
||||
if (event.getPlugin().equals(Iris.instance)) {
|
||||
isServerShuttingDown = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void updateWorlds() {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
try {
|
||||
if (IrisToolbelt.access(world).getEngine() != null) {
|
||||
IrisWorlds.add(world);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// no
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
cacheTicker = new Looper() {
|
||||
@Override
|
||||
protected long loop() {
|
||||
long now = System.currentTimeMillis();
|
||||
lastUseLock.lock();
|
||||
try {
|
||||
for (World key : new ArrayList<>(lastUse.keySet())) {
|
||||
Long last = lastUse.get(key);
|
||||
if (last == null)
|
||||
continue;
|
||||
if (now - last > 60000) {
|
||||
lastUse.remove(key);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
lastUseLock.unlock();
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
};
|
||||
|
||||
updateTicker = new Looper() {
|
||||
@Override
|
||||
protected long loop() {
|
||||
try {
|
||||
TotalQueuedTectonicPlates.set(0);
|
||||
TotalNotQueuedTectonicPlates.set(0);
|
||||
TotalTectonicPlates.set(0);
|
||||
for (World world : IrisWorlds) {
|
||||
Engine engine = Objects.requireNonNull(IrisToolbelt.access(world)).getEngine();
|
||||
TotalQueuedTectonicPlates.addAndGet((int) engine.getMantle().getToUnload());
|
||||
TotalNotQueuedTectonicPlates.addAndGet((int) engine.getMantle().getNotQueuedLoadedRegions());
|
||||
TotalTectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
|
||||
}
|
||||
if (!isServerShuttingDown && isServerLoaded) {
|
||||
if (!trimTicker.isAlive()) {
|
||||
Iris.info(C.RED + "TrimTicker found dead! Booting it up!");
|
||||
try {
|
||||
TrimLogic();
|
||||
} catch (Exception e) {
|
||||
Iris.error("What happened?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (!unloadTicker.isAlive()) {
|
||||
Iris.info(C.RED + "UnloadTicker found dead! Booting it up!");
|
||||
try {
|
||||
UnloadLogic();
|
||||
} catch (Exception e) {
|
||||
Iris.error("What happened?");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
};
|
||||
}
|
||||
public void TrimLogic() {
|
||||
if (trimTicker == null || !trimTicker.isAlive()) {
|
||||
trimTicker = new Looper() {
|
||||
private final Supplier<Engine> supplier = createSupplier();
|
||||
|
||||
@Override
|
||||
protected long loop() {
|
||||
long start = System.currentTimeMillis();
|
||||
trimAlive.reset();
|
||||
try {
|
||||
Engine engine = supplier.get();
|
||||
if (engine != null) {
|
||||
engine.getMantle().trim(tectonicLimit.get() / lastUse.size());
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.info(C.RED + "EngineSVC: Failed to trim.");
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int size = lastUse.size();
|
||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
||||
if (time <= 0)
|
||||
return 0;
|
||||
return time;
|
||||
}
|
||||
};
|
||||
trimTicker.start();
|
||||
}
|
||||
}
|
||||
public void UnloadLogic() {
|
||||
if (unloadTicker == null || !unloadTicker.isAlive()) {
|
||||
unloadTicker = new Looper() {
|
||||
private final Supplier<Engine> supplier = createSupplier();
|
||||
|
||||
@Override
|
||||
protected long loop() {
|
||||
long start = System.currentTimeMillis();
|
||||
unloadAlive.reset();
|
||||
try {
|
||||
Engine engine = supplier.get();
|
||||
if (engine != null) {
|
||||
long unloadStart = System.currentTimeMillis();
|
||||
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size());
|
||||
if (count > 0) {
|
||||
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.reportError(e);
|
||||
Iris.info(C.RED + "EngineSVC: Failed to unload.");
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int size = lastUse.size();
|
||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
||||
if (time <= 0)
|
||||
return 0;
|
||||
return time;
|
||||
}
|
||||
};
|
||||
unloadTicker.start();
|
||||
}
|
||||
}
|
||||
|
||||
private Supplier<Engine> createSupplier() {
|
||||
AtomicInteger i = new AtomicInteger();
|
||||
return () -> {
|
||||
List<World> worlds = Bukkit.getWorlds();
|
||||
if (i.get() >= worlds.size()) {
|
||||
i.set(0);
|
||||
}
|
||||
try {
|
||||
for (int j = 0; j < worlds.size(); j++) {
|
||||
World world = worlds.get(i.getAndIncrement());
|
||||
PlatformChunkGenerator generator = IrisToolbelt.access(world);
|
||||
if (i.get() >= worlds.size()) {
|
||||
i.set(0);
|
||||
}
|
||||
|
||||
if (generator != null) {
|
||||
Engine engine = generator.getEngine();
|
||||
boolean closed = engine.getMantle().getData().isClosed();
|
||||
if (engine != null && !engine.isStudio() && !closed) {
|
||||
lastUseLock.lock();
|
||||
lastUse.put(world, System.currentTimeMillis());
|
||||
lastUseLock.unlock();
|
||||
return engine;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Iris.info(C.RED + "EngineSVC: Failed to create supplier.");
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
cacheTicker.interrupt();
|
||||
trimTicker.interrupt();
|
||||
unloadTicker.interrupt();
|
||||
lastUse.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,625 @@
|
||||
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<Double> 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<Void> 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<String> 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<String> generateRandomStrings(int count, int length) {
|
||||
SecureRandom random = new SecureRandom();
|
||||
List<String> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,7 +24,6 @@ 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;
|
||||
@@ -142,18 +141,7 @@ public class IrisToolbelt {
|
||||
* @return the pregenerator job (already started)
|
||||
*/
|
||||
public static PregeneratorJob pregenerate(PregenTask task, PregeneratorMethod method, Engine 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);
|
||||
return new PregeneratorJob(task, method, engine);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.format.Form;
|
||||
import com.volmit.iris.util.hunk.Hunk;
|
||||
import com.volmit.iris.util.io.IO;
|
||||
import com.volmit.iris.util.io.JarScanner;
|
||||
import com.volmit.iris.util.mantle.MantleFlag;
|
||||
import com.volmit.iris.util.math.M;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
@@ -60,7 +61,11 @@ import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -71,6 +76,8 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
@EqualsAndHashCode(exclude = "context")
|
||||
@ToString(exclude = "context")
|
||||
public class IrisEngine implements Engine {
|
||||
private static final Map<Class<? extends IrisEngineService>, Constructor<? extends IrisEngineService>> SERVICES = scanServices();
|
||||
private final KMap<Class<? extends IrisEngineService>, IrisEngineService> services;
|
||||
private final AtomicInteger bud;
|
||||
private final AtomicInteger buds;
|
||||
private final AtomicInteger generated;
|
||||
@@ -111,6 +118,7 @@ public class IrisEngine implements Engine {
|
||||
getEngineData();
|
||||
verifySeed();
|
||||
this.seedManager = new SeedManager(target.getWorld().getRawWorldSeed());
|
||||
services = new KMap<>();
|
||||
bud = new AtomicInteger(0);
|
||||
buds = new AtomicInteger(0);
|
||||
metrics = new EngineMetrics(32);
|
||||
@@ -137,6 +145,26 @@ public class IrisEngine implements Engine {
|
||||
Iris.debug("Engine Initialized " + getCacheID());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<Class<? extends IrisEngineService>, Constructor<? extends IrisEngineService>> scanServices() {
|
||||
JarScanner js = new JarScanner(Iris.instance.getJarFile(), "com.volmit.iris.engine.service");
|
||||
J.attempt(js::scan);
|
||||
KMap<Class<? extends IrisEngineService>, Constructor<? extends IrisEngineService>> map = new KMap<>();
|
||||
js.getClasses()
|
||||
.stream()
|
||||
.filter(IrisEngineService.class::isAssignableFrom)
|
||||
.map(c -> (Class<? extends IrisEngineService>) c)
|
||||
.forEach(c -> {
|
||||
try {
|
||||
map.put(c, c.getConstructor(Engine.class));
|
||||
} catch (NoSuchMethodException e) {
|
||||
Iris.warn("Failed to load service " + c.getName() + " due to missing constructor");
|
||||
}
|
||||
});
|
||||
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
private void verifySeed() {
|
||||
if (getEngineData().getSeed() != null && getEngineData().getSeed() != target.getWorld().getRawWorldSeed()) {
|
||||
target.getWorld().setRawWorldSeed(getEngineData().getSeed());
|
||||
@@ -161,6 +189,8 @@ public class IrisEngine implements Engine {
|
||||
execution.close();
|
||||
effects.close();
|
||||
mode.close();
|
||||
services.values().forEach(s -> s.onDisable(true));
|
||||
services.values().forEach(Iris.instance::unregisterListener);
|
||||
|
||||
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
|
||||
}
|
||||
@@ -169,6 +199,24 @@ public class IrisEngine implements Engine {
|
||||
try {
|
||||
Iris.debug("Setup Engine " + getCacheID());
|
||||
cacheId = RNG.r.nextInt();
|
||||
boolean hotload = true;
|
||||
if (services.isEmpty()) {
|
||||
SERVICES.forEach((s, c) -> {
|
||||
try {
|
||||
services.put(s, c.newInstance(this));
|
||||
} catch (InstantiationException | IllegalAccessException |
|
||||
InvocationTargetException e) {
|
||||
Iris.error("Failed to create service " + s.getName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
hotload = false;
|
||||
}
|
||||
for (var service : services.values()) {
|
||||
service.onEnable(hotload);
|
||||
Iris.instance.registerListener(service);
|
||||
}
|
||||
|
||||
worldManager = new IrisWorldManager(this);
|
||||
complex = new IrisComplex(this);
|
||||
execution = new IrisExecutionEnvironment(this);
|
||||
@@ -418,6 +466,7 @@ public class IrisEngine implements Engine {
|
||||
PregeneratorJob.shutdownInstance();
|
||||
closed = true;
|
||||
J.car(art);
|
||||
services.values().forEach(s -> s.onDisable(false));
|
||||
getWorldManager().close();
|
||||
getTarget().close();
|
||||
saveEngineData();
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.volmit.iris.engine.object.*;
|
||||
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.format.Form;
|
||||
import com.volmit.iris.util.mantle.Mantle;
|
||||
import com.volmit.iris.util.mantle.MantleFlag;
|
||||
import com.volmit.iris.util.math.M;
|
||||
@@ -72,7 +71,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
private final Looper looper;
|
||||
private final int id;
|
||||
private final KList<Runnable> updateQueue = new KList<>();
|
||||
private final ChronoLatch cl;
|
||||
private final ChronoLatch clw;
|
||||
private final ChronoLatch ecl;
|
||||
private final ChronoLatch cln;
|
||||
@@ -83,12 +81,10 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
private long charge = 0;
|
||||
private int actuallySpawned = 0;
|
||||
private int cooldown = 0;
|
||||
private List<Entity> precount = new KList<>();
|
||||
private KSet<Position2> injectBiomes = new KSet<>();
|
||||
|
||||
public IrisWorldManager() {
|
||||
super(null);
|
||||
cl = null;
|
||||
ecl = null;
|
||||
cln = null;
|
||||
clw = null;
|
||||
@@ -103,7 +99,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
chunkUpdater = new ChronoLatch(3000);
|
||||
chunkDiscovery = new ChronoLatch(5000);
|
||||
cln = new ChronoLatch(60000);
|
||||
cl = new ChronoLatch(3000);
|
||||
ecl = new ChronoLatch(250);
|
||||
clw = new ChronoLatch(1000, true);
|
||||
id = engine.getCacheID();
|
||||
@@ -151,27 +146,12 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
engine.getEngineData().cleanup(getEngine());
|
||||
}
|
||||
|
||||
if (precount != null) {
|
||||
entityCount = 0;
|
||||
for (Entity i : precount) {
|
||||
if (i instanceof LivingEntity) {
|
||||
if (!i.isDead()) {
|
||||
entityCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
precount = null;
|
||||
}
|
||||
|
||||
if (energy < 650) {
|
||||
if (ecl.flip()) {
|
||||
energy *= 1 + (0.02 * M.clip((1D - getEntitySaturation()), 0D, 1D));
|
||||
fixEnergy();
|
||||
}
|
||||
}
|
||||
|
||||
onAsyncTick();
|
||||
}
|
||||
|
||||
return IrisSettings.get().getWorld().getAsyncTickIntervalMS();
|
||||
@@ -214,7 +194,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
int finalZ = c.getZ() + z;
|
||||
J.a(() -> getMantle().raiseFlag(finalX, finalZ, MantleFlag.INITIAL_SPAWNED_MARKER,
|
||||
() -> {
|
||||
J.a(() -> spawnIn(cx, true), RNG.r.i(5, 200));
|
||||
J.a(() -> spawnIn(cx), RNG.r.i(5, 200));
|
||||
getSpawnersFromMarkers(cx).forEach((blockf, spawners) -> {
|
||||
if (spawners.isEmpty()) {
|
||||
return;
|
||||
@@ -222,7 +202,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
|
||||
IrisPosition block = new IrisPosition(blockf.getX(), blockf.getY() + getEngine().getWorld().minHeight(), blockf.getZ());
|
||||
IrisSpawner s = new KList<>(spawners).getRandom();
|
||||
spawn(block, s, true);
|
||||
spawn(block, s);
|
||||
});
|
||||
}));
|
||||
}
|
||||
@@ -232,73 +212,16 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onAsyncTick() {
|
||||
if (getEngine().isClosed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
actuallySpawned = 0;
|
||||
|
||||
if (energy < 100) {
|
||||
J.sleep(200);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!getEngine().getWorld().hasRealWorld()) {
|
||||
Iris.debug("Can't spawn. No real world");
|
||||
J.sleep(5000);
|
||||
return false;
|
||||
}
|
||||
|
||||
double epx = getEntitySaturation();
|
||||
if (epx > IrisSettings.get().getWorld().getTargetSpawnEntitiesPerChunk()) {
|
||||
Iris.debug("Can't spawn. The entity per chunk ratio is at " + Form.pc(epx, 2) + " > 100% (total entities " + entityCount + ")");
|
||||
J.sleep(5000);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cl.flip()) {
|
||||
try {
|
||||
J.s(() -> precount = getEngine().getWorld().realWorld().getEntities());
|
||||
} catch (Throwable e) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
int spawnBuffer = RNG.r.i(2, 12);
|
||||
|
||||
Chunk[] cc = getEngine().getWorld().realWorld().getLoadedChunks();
|
||||
while (spawnBuffer-- > 0) {
|
||||
if (cc.length == 0) {
|
||||
Iris.debug("Can't spawn. No chunks!");
|
||||
return false;
|
||||
}
|
||||
|
||||
Chunk c = cc[RNG.r.nextInt(cc.length)];
|
||||
|
||||
if (!c.isLoaded() || !Chunks.isSafe(c.getWorld(), c.getX(), c.getZ())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
spawnIn(c, false);
|
||||
}
|
||||
|
||||
energy -= (actuallySpawned / 2D);
|
||||
return actuallySpawned > 0;
|
||||
}
|
||||
|
||||
private void fixEnergy() {
|
||||
energy = M.clip(energy, 1D, getDimension().getMaximumEnergy());
|
||||
}
|
||||
|
||||
private void spawnIn(Chunk c, boolean initial) {
|
||||
private void spawnIn(Chunk c) {
|
||||
if (getEngine().isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (initial) {
|
||||
energy += 1.2;
|
||||
}
|
||||
energy += 1.2;
|
||||
|
||||
if (IrisSettings.get().getWorld().isMarkerEntitySpawningSystem()) {
|
||||
getSpawnersFromMarkers(c).forEach((blockf, spawners) -> {
|
||||
@@ -308,9 +231,8 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
|
||||
IrisPosition block = new IrisPosition(blockf.getX(), blockf.getY() + getEngine().getWorld().minHeight(), blockf.getZ());
|
||||
IrisSpawner s = new KList<>(spawners).getRandom();
|
||||
spawn(block, s, false);
|
||||
J.a(() -> getMantle().raiseFlag(c.getX(), c.getZ(), MantleFlag.INITIAL_SPAWNED_MARKER,
|
||||
() -> spawn(block, s, true)));
|
||||
() -> spawn(block, s)));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -341,7 +263,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
.stream()
|
||||
.filter(filter)))
|
||||
.filter(counter)
|
||||
.flatMap((i) -> stream(i, initial))
|
||||
.flatMap(this::stream)
|
||||
.collect(Collectors.toList()))
|
||||
.getRandom();
|
||||
//@done
|
||||
@@ -378,13 +300,13 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
}
|
||||
}
|
||||
|
||||
private Stream<IrisEntitySpawn> stream(IrisSpawner s, boolean initial) {
|
||||
for (IrisEntitySpawn i : initial ? s.getInitialSpawns() : s.getSpawns()) {
|
||||
private Stream<IrisEntitySpawn> stream(IrisSpawner s) {
|
||||
for (IrisEntitySpawn i : s.getInitialSpawns()) {
|
||||
i.setReferenceSpawner(s);
|
||||
i.setReferenceMarker(s.getReferenceMarker());
|
||||
}
|
||||
|
||||
return (initial ? s.getInitialSpawns() : s.getSpawns()).stream();
|
||||
return (s.getInitialSpawns()).stream();
|
||||
}
|
||||
|
||||
private KList<IrisEntitySpawn> spawnRandomly(List<IrisEntitySpawn> types) {
|
||||
@@ -431,7 +353,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
}
|
||||
}
|
||||
|
||||
private void spawn(IrisPosition block, IrisSpawner spawner, boolean initial) {
|
||||
private void spawn(IrisPosition block, IrisSpawner spawner) {
|
||||
if (getEngine().isClosed()) {
|
||||
return;
|
||||
}
|
||||
@@ -440,7 +362,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
|
||||
return;
|
||||
}
|
||||
|
||||
KList<IrisEntitySpawn> s = initial ? spawner.getInitialSpawns() : spawner.getSpawns();
|
||||
KList<IrisEntitySpawn> s = spawner.getInitialSpawns();
|
||||
if (s.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -75,7 +75,6 @@ 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;
|
||||
@@ -853,25 +852,6 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@BlockCoordinates
|
||||
default Position2 getNearestStronghold(Position2 pos) {
|
||||
KList<Position2> 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<String> regionKeys = getDimension()
|
||||
.getAllRegions(this).stream()
|
||||
@@ -892,10 +872,31 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat
|
||||
|
||||
default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) {
|
||||
if (s.getLoadKey().equals(getDimension().getStronghold())) {
|
||||
Position2 pr = getNearestStronghold(new Position2(player.getLocation().getBlockX(), player.getLocation().getBlockZ()));
|
||||
if (pr == null) {
|
||||
KList<Position2> p = getDimension().getStrongholds(getSeedManager().getMantle());
|
||||
|
||||
if (p.isEmpty()) {
|
||||
player.sendMessage(C.GOLD + "No strongholds in world.");
|
||||
} else {
|
||||
}
|
||||
|
||||
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) {
|
||||
Location ll = new Location(player.getWorld(), pr.getX(), 40, pr.getZ());
|
||||
J.s(() -> player.teleport(ll));
|
||||
}
|
||||
|
||||
@@ -97,6 +97,51 @@ 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<Position2> 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())) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.volmit.iris.engine.object;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public abstract class IrisEngineService implements Listener {
|
||||
protected final Engine engine;
|
||||
|
||||
public abstract void onEnable(boolean hotload);
|
||||
|
||||
public abstract void onDisable(boolean hotload);
|
||||
|
||||
public final void postShutdown(Runnable r) {
|
||||
Iris.instance.postShutdown(r);
|
||||
}
|
||||
}
|
||||
@@ -404,7 +404,7 @@ public class IrisEntity extends IrisRegistrant {
|
||||
});
|
||||
|
||||
|
||||
return e;
|
||||
return e.isValid() ? e : null;
|
||||
}
|
||||
|
||||
private int surfaceY(Location l) {
|
||||
|
||||
@@ -28,19 +28,12 @@ import com.volmit.iris.util.math.RNG;
|
||||
import com.volmit.iris.util.math.Vector3d;
|
||||
import com.volmit.iris.util.matter.MatterMarker;
|
||||
import com.volmit.iris.util.matter.slices.MarkerMatter;
|
||||
import io.lumine.mythic.bukkit.adapters.BukkitEntity;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.bukkit.Chunk;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
|
||||
@Snippet("entity-spawn")
|
||||
@Accessors(chain = true)
|
||||
@@ -69,6 +62,10 @@ public class IrisEntitySpawn implements IRare {
|
||||
private transient IrisSpawner referenceSpawner;
|
||||
private transient IrisMarker referenceMarker;
|
||||
|
||||
public boolean check(Engine eng, IrisPosition c, ChunkSnapshot snapshot) {
|
||||
return getRealEntity(eng).getSurface().matches(snapshot.getBlockData(c.getX() & 15, c.getY(), c.getZ() & 15));
|
||||
}
|
||||
|
||||
public int spawn(Engine gen, Chunk c, RNG rng) {
|
||||
int spawns = minSpawns == maxSpawns ? minSpawns : rng.i(Math.min(minSpawns, maxSpawns), Math.max(minSpawns, maxSpawns));
|
||||
int s = 0;
|
||||
@@ -168,7 +165,7 @@ public class IrisEntitySpawn implements IRare {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ignoreSurfaces && !irisEntity.getSurface().matches(at.clone().subtract(0, 1, 0).getBlock())) {
|
||||
if (!ignoreSurfaces && !irisEntity.getSurface().matches(at.clone().subtract(0, 1, 0).getBlock().getBlockData())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -183,6 +180,10 @@ public class IrisEntitySpawn implements IRare {
|
||||
Entity e = irisEntity.spawn(g, at.add(0.5, 0.5, 0.5), rng.aquire(() -> new RNG(g.getSeedManager().getEntity())));
|
||||
if (e != null) {
|
||||
Iris.debug("Spawned " + C.DARK_AQUA + "Entity<" + getEntity() + "> " + C.GREEN + e.getType() + C.LIGHT_PURPLE + " @ " + C.GRAY + e.getLocation().getX() + ", " + e.getLocation().getY() + ", " + e.getLocation().getZ());
|
||||
|
||||
if (referenceSpawner != null) {
|
||||
referenceSpawner.getConditions().apply(e);
|
||||
}
|
||||
}
|
||||
|
||||
return e;
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.volmit.iris.engine.object;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.engine.object.annotations.Desc;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
import com.volmit.iris.util.collection.KMap;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.SpawnCategory;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
@Accessors(chain = true)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Desc("Conditions for a spawner to be triggered")
|
||||
@Data
|
||||
public class IrisSpawnCondition {
|
||||
private static final NamespacedKey CATEGORY_KEY = new NamespacedKey(Iris.instance, "spawn_category");
|
||||
|
||||
private SpawnCategory category = SpawnCategory.AMBIENT;
|
||||
private int maxEntities = 60;
|
||||
|
||||
public boolean check(KMap<UUID, KMap<String, Boolean>> cache, KList<Entity> entities) {
|
||||
int entityCount = 0;
|
||||
for (Entity entity : entities) {
|
||||
var map = cache.computeIfAbsent(entity.getUniqueId(), k -> new KMap<>());
|
||||
if (check(map, "category_" + category.name(), () -> checkCategory(entity, category)) && ++entityCount >= maxEntities)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void apply(Entity entity) {
|
||||
var pdc = entity.getPersistentDataContainer();
|
||||
pdc.set(CATEGORY_KEY, PersistentDataType.STRING, category.name());
|
||||
}
|
||||
|
||||
private static boolean check(KMap<String, Boolean> cache, String key, BooleanSupplier predicate) {
|
||||
return cache.computeIfAbsent(key, k -> predicate.getAsBoolean()) == Boolean.TRUE;
|
||||
}
|
||||
|
||||
private static boolean checkCategory(Entity entity, SpawnCategory category) {
|
||||
if (entity.getSpawnCategory() == category)
|
||||
return true;
|
||||
|
||||
var pdc = entity.getPersistentDataContainer();
|
||||
if (!pdc.has(CATEGORY_KEY, PersistentDataType.STRING))
|
||||
return false;
|
||||
return category.name().equals(pdc.get(CATEGORY_KEY, PersistentDataType.STRING));
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,9 @@ public class IrisSpawner extends IrisRegistrant {
|
||||
@Desc("Where should these spawns be placed")
|
||||
private IrisSpawnGroup group = IrisSpawnGroup.NORMAL;
|
||||
|
||||
@Desc("Conditions for this spawner to be triggered")
|
||||
private IrisSpawnCondition conditions = new IrisSpawnCondition();
|
||||
|
||||
public boolean isValid(IrisBiome biome) {
|
||||
return switch (group) {
|
||||
case NORMAL -> switch (biome.getInferredType()) {
|
||||
|
||||
@@ -20,7 +20,7 @@ package com.volmit.iris.engine.object;
|
||||
|
||||
import com.volmit.iris.engine.object.annotations.Desc;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Waterlogged;
|
||||
|
||||
@Desc("The type of surface entities should spawn on")
|
||||
@@ -47,8 +47,8 @@ public enum IrisSurface {
|
||||
* @param state The blockstate
|
||||
* @return True if it matches
|
||||
*/
|
||||
public boolean matches(Block state) {
|
||||
Material type = state.getType();
|
||||
public boolean matches(BlockData state) {
|
||||
Material type = state.getMaterial();
|
||||
if (type.isSolid()) {
|
||||
return this == LAND || this == OVERWORLD || (this == ANIMAL
|
||||
&& (type == Material.GRASS_BLOCK || type == Material.DIRT
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
package com.volmit.iris.engine.service;
|
||||
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.core.loader.IrisData;
|
||||
import com.volmit.iris.core.nms.container.Pair;
|
||||
import com.volmit.iris.engine.IrisWorldManager;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.*;
|
||||
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.format.Form;
|
||||
import com.volmit.iris.util.mantle.Mantle;
|
||||
import com.volmit.iris.util.math.BlockPosition;
|
||||
import com.volmit.iris.util.math.Position2;
|
||||
import com.volmit.iris.util.math.RNG;
|
||||
import com.volmit.iris.util.matter.MatterMarker;
|
||||
import com.volmit.iris.util.parallel.Sync;
|
||||
import com.volmit.iris.util.scheduling.J;
|
||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
||||
import io.papermc.lib.PaperLib;
|
||||
import lombok.SneakyThrows;
|
||||
import org.bukkit.ChunkSnapshot;
|
||||
import org.bukkit.GameRule;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.player.PlayerChangedWorldEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class EngineMobHandlerSVC extends IrisEngineService {
|
||||
private static final List<String> CAVE_TAGS = List.of("cave_floor", "cave_ceiling");
|
||||
private static final int SAFE_RADIUS = 16;
|
||||
private static final int MAX_RADIUS = 128;
|
||||
|
||||
private final AtomicLong currentTick = new AtomicLong();
|
||||
private final Sync<Long> sync = new Sync<>();
|
||||
private final Set<Player> players = ConcurrentHashMap.newKeySet();
|
||||
private final AtomicBoolean running = new AtomicBoolean(false);
|
||||
private transient KList<Entity> entities = new KList<>();
|
||||
private transient Thread asyncTicker = null;
|
||||
private transient Thread entityCollector = null;
|
||||
private transient boolean charge = false;
|
||||
private transient int task = -1;
|
||||
|
||||
public EngineMobHandlerSVC(Engine engine) {
|
||||
super(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable(boolean hotload) {
|
||||
if (running.get()) {
|
||||
running.set(false);
|
||||
cancel(asyncTicker);
|
||||
cancel(entityCollector);
|
||||
}
|
||||
|
||||
running.set(true);
|
||||
charge = hotload;
|
||||
asyncTicker = Thread.ofPlatform()
|
||||
.name("Iris Async Mob Spawning - " + engine.getWorld().name())
|
||||
.priority(9)
|
||||
.start(() -> {
|
||||
while (mayLoop()) {
|
||||
try {
|
||||
asyncTick();
|
||||
} catch (Throwable e) {
|
||||
if (isInterrupted(e))
|
||||
return;
|
||||
Iris.error("Error in async tick for " + engine.getWorld().name());
|
||||
e.printStackTrace();
|
||||
|
||||
J.sleep(100);
|
||||
}
|
||||
}
|
||||
});
|
||||
entityCollector = Thread.ofVirtual()
|
||||
.name("Iris Async Entity Collector - " + engine.getWorld().name())
|
||||
.start(() -> {
|
||||
while (mayLoop()) {
|
||||
try {
|
||||
sync.next().get();
|
||||
var world = engine.getWorld().realWorld();
|
||||
if (world == null) continue;
|
||||
J.s(() -> entities = new KList<>(world.getEntities()));
|
||||
} catch (Throwable e) {
|
||||
if (isInterrupted(e))
|
||||
return;
|
||||
|
||||
Iris.error("Error in async tick for " + engine.getWorld().name());
|
||||
e.printStackTrace();
|
||||
|
||||
J.sleep(100);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (task != -1) J.csr(task);
|
||||
task = J.sr(() -> sync.advance(currentTick.getAndIncrement()), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(boolean hotload) {
|
||||
running.set(false);
|
||||
cancel(asyncTicker);
|
||||
cancel(entityCollector);
|
||||
if (!hotload) J.csr(task);
|
||||
}
|
||||
|
||||
private void asyncTick() throws Throwable {
|
||||
long tick = sync.next().get();
|
||||
var manager = (IrisWorldManager) engine.getWorldManager();
|
||||
if (charge) {
|
||||
manager.chargeEnergy();
|
||||
charge = false;
|
||||
}
|
||||
|
||||
var world = engine.getWorld().realWorld();
|
||||
if (world == null
|
||||
|| noSpawning()
|
||||
|| Boolean.FALSE.equals(world.getGameRuleValue(GameRule.DO_MOB_SPAWNING))
|
||||
|| players.isEmpty()
|
||||
|| manager.getEnergy() < 100)
|
||||
return;
|
||||
|
||||
var p = PrecisionStopwatch.start();
|
||||
var entities = new KList<>(this.entities);
|
||||
|
||||
var conditionCache = new KMap<UUID, KMap<String, Boolean>>();
|
||||
var data = engine.getData();
|
||||
var invalid = data.getSpawnerLoader()
|
||||
.streamAllPossible()
|
||||
.filter(Predicate.not(spawner -> spawner.canSpawn(engine)
|
||||
&& spawner.getConditions().check(conditionCache, entities)))
|
||||
.map(IrisSpawner::getLoadKey)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
var centers = players.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(Player::isOnline)
|
||||
.map(Player::getLocation)
|
||||
.map(BlockPosition::fromLocation)
|
||||
.collect(KList.collector())
|
||||
.shuffle();
|
||||
|
||||
if (centers.isEmpty())
|
||||
return;
|
||||
|
||||
AtomicDouble delta = new AtomicDouble();
|
||||
int actuallySpawned = 0;
|
||||
|
||||
KMap<Position2, Pair<Entity[], ChunkSnapshot>> cache = new KMap<>();
|
||||
while (centers.isNotEmpty()) {
|
||||
var center = centers.pop();
|
||||
var spawned = trySpawn(world, invalid, cache, center, delta);
|
||||
if (spawned == 0 && p.getMilliseconds() < 1000)
|
||||
centers.add(center);
|
||||
actuallySpawned += spawned;
|
||||
}
|
||||
manager.setEnergy(manager.getEnergy() - delta.get());
|
||||
if (actuallySpawned > 0) {
|
||||
Iris.info("Async Mob Spawning " + world.getName() + " used " + delta + " energy and took " + Form.duration((long) p.getMilliseconds()));
|
||||
}
|
||||
}
|
||||
|
||||
private int trySpawn(
|
||||
World world,
|
||||
Set<String> invalid,
|
||||
KMap<Position2, Pair<Entity[], ChunkSnapshot>> cache,
|
||||
BlockPosition center,
|
||||
AtomicDouble delta
|
||||
) {
|
||||
var pos = center.randomPoint(MAX_RADIUS, SAFE_RADIUS);
|
||||
if (pos.getY() < world.getMinHeight() || pos.getY() >= world.getMaxHeight())
|
||||
return 0;
|
||||
|
||||
var chunkPos = new Position2(center.getX() >> 4, center.getZ() >> 4);
|
||||
var pair = cache.computeIfAbsent(chunkPos, cPos -> {
|
||||
try {
|
||||
return PaperLib.getChunkAtAsync(world, cPos.getX(), cPos.getZ(), false)
|
||||
.thenApply(c -> c != null ? new Pair<>(c.getEntities(), c.getChunkSnapshot(false, false, false)) : null)
|
||||
.get();
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
if (pair == null)
|
||||
return 0;
|
||||
|
||||
var spawners = spawnersAt(pair.getB(), pos, invalid);
|
||||
spawners.removeIf(i -> !i.canSpawn(engine, chunkPos.getX(), chunkPos.getZ()));
|
||||
|
||||
if (spawners.isEmpty())
|
||||
return 0;
|
||||
|
||||
IrisPosition irisPos = new IrisPosition(pos.getX(), pos.getY(), pos.getZ());
|
||||
for (var spawner : spawners) {
|
||||
var spawns = spawner.getSpawns().copy();
|
||||
spawns.removeIf(spawn -> !spawn.check(engine, irisPos, pair.getB()));
|
||||
|
||||
var entity = IRare.pick(spawns, RNG.r.nextDouble());
|
||||
if (entity == null)
|
||||
continue;
|
||||
|
||||
entity.setReferenceSpawner(spawner);
|
||||
entity.setReferenceMarker(spawner.getReferenceMarker());
|
||||
int spawned = entity.spawn(engine, irisPos, RNG.r);
|
||||
if (spawned == 0)
|
||||
continue;
|
||||
|
||||
delta.addAndGet(spawned * ((entity.getEnergyMultiplier() * spawner.getEnergyMultiplier() * 1)));
|
||||
spawner.spawn(engine, chunkPos.getX(), chunkPos.getZ());
|
||||
if (!spawner.canSpawn(engine))
|
||||
invalid.add(spawner.getLoadKey());
|
||||
return spawned;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private KSet<IrisSpawner> spawnersAt(ChunkSnapshot chunk, BlockPosition pos, Set<String> invalid) {
|
||||
KSet<IrisSpawner> spawners = markerAt(chunk, pos, invalid);
|
||||
|
||||
var loader = engine.getData().getSpawnerLoader();
|
||||
int y = pos.getY() - engine.getWorld().minHeight();
|
||||
Stream.concat(engine.getRegion(pos.getX(), pos.getZ())
|
||||
.getEntitySpawners()
|
||||
.stream(),
|
||||
engine.getBiomeOrMantle(pos.getX(), y, pos.getZ())
|
||||
.getEntitySpawners()
|
||||
.stream())
|
||||
.filter(Predicate.not(invalid::contains))
|
||||
.map(loader::load)
|
||||
.forEach(spawners::add);
|
||||
|
||||
return spawners;
|
||||
}
|
||||
|
||||
private KSet<IrisSpawner> markerAt(ChunkSnapshot chunk, BlockPosition pos, Set<String> invalid) {
|
||||
if (!IrisSettings.get().getWorld().isMarkerEntitySpawningSystem())
|
||||
return new KSet<>();
|
||||
|
||||
int y = pos.getY() - engine.getWorld().minHeight();
|
||||
Mantle mantle = engine.getMantle().getMantle();
|
||||
MatterMarker matter = mantle.get(pos.getX(), y, pos.getZ(), MatterMarker.class);
|
||||
if (matter == null || CAVE_TAGS.contains(matter.getTag()))
|
||||
return new KSet<>();
|
||||
IrisData data = engine.getData();
|
||||
IrisMarker mark = data.getMarkerLoader().load(matter.getTag());
|
||||
if (mark == null)
|
||||
return new KSet<>();
|
||||
|
||||
if (mark.isEmptyAbove()) {
|
||||
int x = pos.getX() & 15, z = pos.getZ() & 15;
|
||||
boolean remove = chunk.getBlockData(x, pos.getY() + 1, z).getMaterial().isSolid() || chunk.getBlockData(x, pos.getY() + 2, z).getMaterial().isSolid();
|
||||
if (remove) {
|
||||
mantle.remove(pos.getX(), y, pos.getZ(), MatterMarker.class);
|
||||
return new KSet<>();
|
||||
}
|
||||
}
|
||||
|
||||
KSet<IrisSpawner> spawners = new KSet<>();
|
||||
for (String key : mark.getSpawners()) {
|
||||
if (invalid.contains(key))
|
||||
continue;
|
||||
|
||||
IrisSpawner spawner = data.getSpawnerLoader().load(key);
|
||||
if (spawner == null) {
|
||||
Iris.error("Cannot load spawner: " + key + " for marker " + matter.getTag());
|
||||
continue;
|
||||
}
|
||||
|
||||
spawner.setReferenceMarker(mark);
|
||||
spawners.add(spawner);
|
||||
}
|
||||
return spawners;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void on(PlayerJoinEvent event) {
|
||||
var player = event.getPlayer();
|
||||
if (player.getWorld() != engine.getWorld().realWorld())
|
||||
return;
|
||||
players.add(player);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void on(PlayerQuitEvent event) {
|
||||
players.remove(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void on(PlayerChangedWorldEvent event) {
|
||||
var player = event.getPlayer();
|
||||
if (player.getWorld() == engine.getWorld().realWorld())
|
||||
players.add(player);
|
||||
else
|
||||
players.remove(player);
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
private static void cancel(Thread thread) {
|
||||
if (thread == null || !thread.isAlive()) return;
|
||||
thread.interrupt();
|
||||
thread.join();
|
||||
}
|
||||
|
||||
private static boolean noSpawning() {
|
||||
var world = IrisSettings.get().getWorld();
|
||||
return !world.isMarkerEntitySpawningSystem() && !world.isAnbientEntitySpawningSystem();
|
||||
}
|
||||
|
||||
private boolean mayLoop() {
|
||||
return !engine.isClosed() && running.get() && !Thread.interrupted();
|
||||
}
|
||||
|
||||
private static boolean isInterrupted(Throwable e) {
|
||||
while (e != null) {
|
||||
if (e instanceof InterruptedException)
|
||||
return true;
|
||||
e = e.getCause();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.volmit.iris.engine.service;
|
||||
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisEngineService;
|
||||
import com.volmit.iris.util.collection.KList;
|
||||
|
||||
public class EngineStatusSVC extends IrisEngineService {
|
||||
private static final KList<EngineStatusSVC> INSTANCES = new KList<>();
|
||||
|
||||
public EngineStatusSVC(Engine engine) {
|
||||
super(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable(boolean hotload) {
|
||||
if (hotload) return;
|
||||
synchronized (INSTANCES) {
|
||||
INSTANCES.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(boolean hotload) {
|
||||
if (hotload) return;
|
||||
|
||||
synchronized (INSTANCES) {
|
||||
INSTANCES.remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getEngineCount() {
|
||||
return INSTANCES.size();
|
||||
}
|
||||
|
||||
public static Status getStatus() {
|
||||
synchronized (INSTANCES) {
|
||||
long loadedChunks = 0;
|
||||
long tectonicPlates = 0;
|
||||
long activeTectonicPlates = 0;
|
||||
long queuedTectonicPlates = 0;
|
||||
long minTectonicUnloadDuration = Long.MAX_VALUE;
|
||||
long maxTectonicUnloadDuration = Long.MIN_VALUE;
|
||||
|
||||
for (var service : INSTANCES) {
|
||||
var world = service.engine.getWorld();
|
||||
if (world.hasRealWorld())
|
||||
loadedChunks += world.realWorld().getLoadedChunks().length;
|
||||
|
||||
tectonicPlates += service.engine.getMantle().getLoadedRegionCount();
|
||||
activeTectonicPlates += service.engine.getMantle().getNotQueuedLoadedRegions();
|
||||
queuedTectonicPlates += service.engine.getMantle().getToUnload();
|
||||
minTectonicUnloadDuration = Math.min(minTectonicUnloadDuration, (long) service.engine.getMantle().getTectonicDuration());
|
||||
maxTectonicUnloadDuration = Math.max(maxTectonicUnloadDuration, (long) service.engine.getMantle().getTectonicDuration());
|
||||
}
|
||||
return new Status(INSTANCES.size(), loadedChunks, MantleCleanerSVC.getTectonicLimit(), tectonicPlates, activeTectonicPlates, queuedTectonicPlates, minTectonicUnloadDuration, maxTectonicUnloadDuration);
|
||||
}
|
||||
}
|
||||
|
||||
public record Status(int engineCount, long loadedChunks, int tectonicLimit,
|
||||
long tectonicPlates, long activeTectonicPlates,
|
||||
long queuedTectonicPlates,
|
||||
long minTectonicUnloadDuration,
|
||||
long maxTectonicUnloadDuration) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.volmit.iris.engine.service;
|
||||
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
import com.volmit.iris.engine.object.IrisEngineService;
|
||||
import com.volmit.iris.util.format.C;
|
||||
import com.volmit.iris.util.math.M;
|
||||
import com.volmit.iris.util.misc.getHardware;
|
||||
import com.volmit.iris.util.scheduling.Looper;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.LongSupplier;
|
||||
|
||||
public class MantleCleanerSVC extends IrisEngineService {
|
||||
private static final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
||||
|
||||
static {
|
||||
// todo: Redo this
|
||||
tectonicLimit.set(2);
|
||||
long t = getHardware.getProcessMemory();
|
||||
while (t > 200) {
|
||||
tectonicLimit.incrementAndGet();
|
||||
t = t - 200;
|
||||
}
|
||||
}
|
||||
|
||||
private Ticker trimmer;
|
||||
private Ticker unloader;
|
||||
|
||||
public MantleCleanerSVC(Engine engine) {
|
||||
super(engine);
|
||||
}
|
||||
|
||||
public static int getTectonicLimit() {
|
||||
return tectonicLimit.get();
|
||||
}
|
||||
|
||||
private static Ticker createTrimmer(Engine engine) {
|
||||
return new Ticker(() -> {
|
||||
if (engine.isClosed()) return -1;
|
||||
long start = M.ms();
|
||||
try {
|
||||
engine.getMantle().trim(tectonicLimit.get() / getEngineCount());
|
||||
} catch (Throwable e) {
|
||||
Iris.debug(C.RED + "Mantle: Failed to trim.");
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (engine.isClosed()) return -1;
|
||||
int size = getEngineCount();
|
||||
return Math.max(1000 / size - (M.ms() - start), 0);
|
||||
}, "Iris Mantle Trimmer - " + engine.getWorld().name());
|
||||
}
|
||||
|
||||
private static Ticker createUnloader(Engine engine) {
|
||||
return new Ticker(() -> {
|
||||
if (engine.isClosed()) return -1;
|
||||
long start = M.ms();
|
||||
try {
|
||||
engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / getEngineCount());
|
||||
} catch (Throwable e) {
|
||||
Iris.debug(C.RED + "Mantle: Failed to unload.");
|
||||
Iris.reportError(e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (engine.isClosed()) return -1;
|
||||
int size = getEngineCount();
|
||||
return Math.max(1000 / size - (M.ms() - start), 0);
|
||||
}, "Iris Mantle Unloader - " + engine.getWorld().name());
|
||||
}
|
||||
|
||||
private static int getEngineCount() {
|
||||
return Math.max(EngineStatusSVC.getEngineCount(), 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable(boolean hotload) {
|
||||
if (engine.isStudio() && !IrisSettings.get().getPerformance().trimMantleInStudio)
|
||||
return;
|
||||
if (trimmer == null || !trimmer.isAlive())
|
||||
trimmer = createTrimmer(engine);
|
||||
if (unloader == null || !unloader.isAlive())
|
||||
unloader = createUnloader(engine);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable(boolean hotload) {
|
||||
if (hotload) return;
|
||||
if (trimmer != null) trimmer.await();
|
||||
if (unloader != null) unloader.await();
|
||||
}
|
||||
|
||||
private static class Ticker extends Looper {
|
||||
private final LongSupplier supplier;
|
||||
|
||||
private Ticker(LongSupplier supplier, String name) {
|
||||
this.supplier = supplier;
|
||||
setPriority(Thread.MIN_PRIORITY);
|
||||
setName(name);
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected long loop() {
|
||||
try {
|
||||
return supplier.getAsLong();
|
||||
} catch (Throwable e) {
|
||||
Iris.error("Exception in Looper " + getName());
|
||||
StringWriter sw = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(sw));
|
||||
Iris.error(sw.toString());
|
||||
return 3000;
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public void await() {
|
||||
join();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ package com.volmit.iris.util.mantle;
|
||||
import com.google.common.util.concurrent.AtomicDouble;
|
||||
import com.volmit.iris.Iris;
|
||||
import com.volmit.iris.core.IrisSettings;
|
||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||
import com.volmit.iris.engine.data.cache.Cache;
|
||||
import com.volmit.iris.engine.framework.Engine;
|
||||
@@ -425,7 +424,7 @@ public class Mantle {
|
||||
ioTrim.set(true);
|
||||
unloadLock.lock();
|
||||
try {
|
||||
if (lastUse != null && IrisEngineSVC.instance != null) {
|
||||
if (lastUse != null) {
|
||||
if (!lastUse.isEmpty()) {
|
||||
Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0));
|
||||
for (long i : new ArrayList<>(lastUse.keySet())) {
|
||||
@@ -435,7 +434,6 @@ public class Mantle {
|
||||
if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) {
|
||||
toUnload.add(i);
|
||||
Iris.debug("Tectonic Region added to unload");
|
||||
IrisEngineSVC.instance.trimActiveAlive.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -454,53 +452,49 @@ public class Mantle {
|
||||
AtomicInteger i = new AtomicInteger();
|
||||
unloadLock.lock();
|
||||
BurstExecutor burst = null;
|
||||
if (IrisEngineSVC.instance != null) {
|
||||
try {
|
||||
KList<Long> copy = toUnload.copy();
|
||||
if (!disableClear) toUnload.clear();
|
||||
burst = MultiBurst.burst.burst(copy.size());
|
||||
burst.setMulticore(copy.size() > tectonicLimit);
|
||||
for (int j = 0; j < copy.size(); j++) {
|
||||
Long id = copy.get(j);
|
||||
if (id == null) {
|
||||
Iris.error("Null id in unloadTectonicPlate at index " + j);
|
||||
continue;
|
||||
}
|
||||
|
||||
burst.queue(() ->
|
||||
hyperLock.withLong(id, () -> {
|
||||
TectonicPlate m = loadedRegions.get(id);
|
||||
if (m != null) {
|
||||
if (m.inUse()) {
|
||||
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
|
||||
if (disableClear) toUnload.remove(id);
|
||||
lastUse.put(id, M.ms());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m.write(fileForRegion(dataFolder, id));
|
||||
loadedRegions.remove(id);
|
||||
lastUse.remove(id);
|
||||
if (disableClear) toUnload.remove(id);
|
||||
i.incrementAndGet();
|
||||
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||
IrisEngineSVC.instance.unloadActiveAlive.reset();
|
||||
} catch (IOException e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
}));
|
||||
try {
|
||||
KList<Long> copy = toUnload.copy();
|
||||
if (!disableClear) toUnload.clear();
|
||||
burst = MultiBurst.burst.burst(copy.size());
|
||||
burst.setMulticore(copy.size() > tectonicLimit);
|
||||
for (int j = 0; j < copy.size(); j++) {
|
||||
Long id = copy.get(j);
|
||||
if (id == null) {
|
||||
Iris.error("Null id in unloadTectonicPlate at index " + j);
|
||||
continue;
|
||||
}
|
||||
burst.complete();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
if (burst != null)
|
||||
burst.complete();
|
||||
} finally {
|
||||
unloadLock.unlock();
|
||||
ioTectonicUnload.set(true);
|
||||
|
||||
burst.queue(() ->
|
||||
hyperLock.withLong(id, () -> {
|
||||
TectonicPlate m = loadedRegions.get(id);
|
||||
if (m != null) {
|
||||
if (m.inUse()) {
|
||||
Iris.debug("Tectonic Plate was added to unload while in use " + C.DARK_GREEN + m.getX() + " " + m.getZ());
|
||||
if (disableClear) toUnload.remove(id);
|
||||
lastUse.put(id, M.ms());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
m.write(fileForRegion(dataFolder, id));
|
||||
loadedRegions.remove(id);
|
||||
lastUse.remove(id);
|
||||
if (disableClear) toUnload.remove(id);
|
||||
i.incrementAndGet();
|
||||
Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id));
|
||||
} catch (IOException e) {
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
return i.get();
|
||||
burst.complete();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
if (burst != null)
|
||||
burst.complete();
|
||||
} finally {
|
||||
unloadLock.unlock();
|
||||
ioTectonicUnload.set(true);
|
||||
}
|
||||
return i.get();
|
||||
}
|
||||
|
||||
@@ -19,11 +19,14 @@
|
||||
package com.volmit.iris.util.math;
|
||||
|
||||
import lombok.Data;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static com.volmit.iris.util.math.RNG.r;
|
||||
|
||||
@Data
|
||||
public class BlockPosition {
|
||||
//Magic numbers
|
||||
@@ -43,6 +46,10 @@ public class BlockPosition {
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public static BlockPosition fromLocation(Location loc) {
|
||||
return new BlockPosition(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
|
||||
}
|
||||
|
||||
public static long toLong(int x, int y, int z) {
|
||||
long var3 = 0L;
|
||||
var3 |= (x & m4) << m3;
|
||||
@@ -108,4 +115,18 @@ public class BlockPosition {
|
||||
setY(Math.max(i.getY(), getY()));
|
||||
setZ(Math.max(i.getZ(), getZ()));
|
||||
}
|
||||
|
||||
public BlockPosition randomPoint(int radius, int innerRadius) {
|
||||
int max = radius * radius;
|
||||
int min = innerRadius * innerRadius;
|
||||
|
||||
while (true) {
|
||||
int x = r.nextInt(-radius, radius + 1);
|
||||
int y = r.nextInt(-radius, radius + 1);
|
||||
int z = r.nextInt(-radius, radius + 1);
|
||||
double dist = x * x + y * y + z * z;
|
||||
if (dist < min || dist > max) continue;
|
||||
return add(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,12 @@ 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 implements ExecutorService {
|
||||
public class MultiBurst {
|
||||
public static final MultiBurst burst = new MultiBurst();
|
||||
private final AtomicLong last;
|
||||
private final String name;
|
||||
@@ -146,106 +144,29 @@ public class MultiBurst implements ExecutorService {
|
||||
return getService().submit(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
close();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<Runnable> 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 <T> Future<T> submit(@NotNull Callable<T> task) {
|
||||
return getService().submit(task);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> Future<T> 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 <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException {
|
||||
return getService().invokeAll(tasks);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> List<Future<T>> invokeAll(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException {
|
||||
return getService().invokeAll(tasks, timeout, unit);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
|
||||
return getService().invokeAny(tasks);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T invokeAny(@NotNull Collection<? extends Callable<T>> tasks, long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return getService().invokeAny(tasks, timeout, unit);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (service != null) {
|
||||
close(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...");
|
||||
|
||||
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) {
|
||||
|
||||
try {
|
||||
service.shutdownNow();
|
||||
} catch (Throwable e) {
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
Iris.reportError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.volmit.iris.util.parallel;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
public class Sync<T> {
|
||||
private final AtomicReference<CompletableFuture<T>> tick = new AtomicReference<>(new CompletableFuture<>());
|
||||
|
||||
public void cancel(boolean mayInterruptIfRunning) {
|
||||
tick.getAndSet(new CompletableFuture<>()).cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
public CompletableFuture<T> next() {
|
||||
return tick.get();
|
||||
}
|
||||
|
||||
public void advance(T value) {
|
||||
tick.getAndSet(new CompletableFuture<>()).complete(value);
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ 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;
|
||||
@@ -123,11 +122,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> 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;
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ 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;
|
||||
@@ -123,11 +122,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> 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;
|
||||
|
||||
|
||||
@@ -125,11 +125,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> 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;
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ 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,11 +120,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> 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;
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ 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,11 +121,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> 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;
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ 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,11 +120,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> 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;
|
||||
|
||||
|
||||
+1
-9
@@ -20,11 +20,9 @@ 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;
|
||||
@@ -33,7 +31,6 @@ 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;
|
||||
@@ -117,11 +114,6 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public @Nullable Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level, HolderSet<Structure> 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;
|
||||
|
||||
@@ -217,7 +209,7 @@ public class IrisChunkGenerator extends CustomChunkGenerator {
|
||||
|
||||
@Override
|
||||
public WeightedRandomList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> holder, StructureManager structuremanager, MobCategory enumcreaturetype, BlockPos blockposition) {
|
||||
return delegate.getMobsAt(holder, structuremanager, enumcreaturetype, blockposition);
|
||||
return WeightedRandomList.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user