diff --git a/core/build.gradle b/core/build.gradle index b9d53330e..76d773096 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -53,6 +53,7 @@ dependencies { compileOnly 'commons-io:commons-io:2.13.0' compileOnly 'commons-lang:commons-lang:2.6' compileOnly 'com.github.oshi:oshi-core:5.8.5' + compileOnly 'org.lz4:lz4-java:1.8.0' // Third Party Integrations compileOnly 'com.ticxo.playeranimator:PlayerAnimator:R1.2.7' diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 4b48913fe..7c21da0a0 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -30,7 +30,6 @@ import com.volmit.iris.core.loader.IrisData; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.pregenerator.LazyPregenerator; -import com.volmit.iris.core.safeguard.ServerBootSFG; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.EnginePanic; @@ -64,6 +63,7 @@ import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.ShurikenQueue; import io.papermc.lib.PaperLib; +import lombok.Getter; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.serializer.ComponentSerializer; import org.bukkit.Bukkit; @@ -95,10 +95,9 @@ import java.net.URL; import java.util.Date; import java.util.Map; -import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; +import static com.volmit.iris.core.safeguard.IrisSafeguard.*; import static com.volmit.iris.core.safeguard.ServerBootSFG.passedserversoftware; import static com.volmit.iris.util.misc.getHardware.getCPUModel; -import static com.volmit.iris.util.misc.getHardware.getCPUThreads; @SuppressWarnings("CanBeFinal") public class Iris extends VolmitPlugin implements Listener { @@ -350,7 +349,7 @@ public class Iris extends VolmitPlugin implements Listener { } } - private static int getJavaVersion() { + public static int getJavaVersion() { String version = System.getProperty("java.version"); if (version.startsWith("1.")) { version = version.substring(2, 3); @@ -469,13 +468,9 @@ public class Iris extends VolmitPlugin implements Listener { J.s(this::setupPapi); J.a(ServerConfigurator::configure, 20); splash(); - UtilsSFG.UnstableMode(); - UtilsSFG.SupportedServerSoftware(); - UtilsSFG.printIncompatibleWarnings(); - UtilsSFG.unstablePrompt(); + UtilsSFG.splash(); autoStartStudio(); - ServerBootSFG.CheckIrisWorlds(); checkForBukkitWorlds(); IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); @@ -596,9 +591,11 @@ public class Iris extends VolmitPlugin implements Listener { if (unstablemode) { return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.RED + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; } - else { - return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.IRIS + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; + if (warningmode) { + return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.GOLD + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; } + return C.BOLD + "" + C.DARK_GRAY + "[" + C.BOLD + "" + C.IRIS + "Iris" + C.BOLD + C.DARK_GRAY + "]" + C.RESET + "" + C.GRAY + ": "; + } private boolean setupChecks() { @@ -728,7 +725,7 @@ public class Iris extends VolmitPlugin implements Listener { File ff = new File(w.worldFolder(), "iris/pack"); if (!ff.exists() || ff.listFiles().length == 0) { ff.mkdirs(); - service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), ff.getParentFile()); + service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), w.worldFolder()); } return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); @@ -745,6 +742,9 @@ public class Iris extends VolmitPlugin implements Listener { if (unstablemode) { info = new String[]{"", "", "", "", "", padd2 + C.RED + " Iris", padd2 + C.GRAY + " by " + C.DARK_RED + "Volmit Software", padd2 + C.GRAY + " v" + C.RED + getDescription().getVersion()}; } + if (warningmode) { + info = new String[]{"", "", "", "", "", padd2 + C.GOLD + " Iris", padd2 + C.GRAY + " by " + C.GOLD + "Volmit Software", padd2 + C.GRAY + " v" + C.GOLD + getDescription().getVersion()}; + } String[] splashstable = { padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@", @@ -773,9 +773,28 @@ public class Iris extends VolmitPlugin implements Listener { padd + C.GRAY + "" + C.RED + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@", padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@" }; - String[] splash = unstablemode ? splashunstable : splashstable; // Choose the appropriate splash array based on unstablemode - - + String[] splashwarning = { + padd + C.GRAY + " @@@@@@@@@@@@@@" + C.DARK_GRAY + "@@@", + padd + C.GRAY + " @@&&&&&&&&&" + C.DARK_GRAY + "&&&&&&" + C.GOLD + " .(((()))). ", + padd + C.GRAY + "@@@&&&&&&&&" + C.DARK_GRAY + "&&&&&" + C.GOLD + " .((((((())))))). ", + padd + C.GRAY + "@@@&&&&&" + C.DARK_GRAY + "&&&&&&&" + C.GOLD + " ((((((((())))))))) " + C.GRAY + " @", + padd + C.GRAY + "@@@&&&&" + C.DARK_GRAY + "@@@@@&" + C.GOLD + " ((((((((-))))))))) " + C.GRAY + " @@", + padd + C.GRAY + "@@@&&" + C.GOLD + " ((((((({ })))))))) " + C.GRAY + " &&@@@", + padd + C.GRAY + "@@" + C.GOLD + " ((((((((-))))))))) " + C.DARK_GRAY + "&@@@@@" + C.GRAY + "&&&&@@@", + padd + C.GRAY + "@" + C.GOLD + " ((((((((())))))))) " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&@@@", + padd + C.GRAY + "" + C.GOLD + " '((((((()))))))' " + C.DARK_GRAY + "&&&&&" + C.GRAY + "&&&&&&&&@@@", + padd + C.GRAY + "" + C.GOLD + " '(((())))' " + C.DARK_GRAY + "&&&&&&&&" + C.GRAY + "&&&&&&&@@", + padd + C.GRAY + " " + C.DARK_GRAY + "@@@" + C.GRAY + "@@@@@@@@@@@@@@" + }; + String[] splash; + File freeSpace = new File(Bukkit.getWorldContainer() + "."); + if (unstablemode) { + splash = splashunstable; + } else if (warningmode) { + splash = splashwarning; + } else { + splash = splashstable; + } OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); String osArch = osBean.getArch(); String osName = osBean.getName(); @@ -783,17 +802,40 @@ public class Iris extends VolmitPlugin implements Listener { if (!passedserversoftware) { Iris.info("Server type & version: " + C.RED + Bukkit.getVersion()); } else { Iris.info("Server type & version: " + Bukkit.getVersion()); } - + 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."); + } + } Iris.info("Server OS: " + osName + " (" + osArch + ")"); - if(unstablemode) Iris.info("Server Cpu: " + C.DARK_RED + getCPUModel()); + try { + if (warningmode){ + Iris.info("Server Cpu: " + C.GOLD + getCPUModel()); + } else { + if(unstablemode){ + Iris.info("Server Cpu: " + C.DARK_RED + getCPUModel()); + } else { + if (getCPUModel().contains("Intel")) { + Iris.info("Server Cpu: " + C.BLUE + getCPUModel()); + } + if (getCPUModel().contains("Ryzen")) { + Iris.info("Server Cpu: " + C.RED + getCPUModel()); + } + if (!getCPUModel().contains("Ryzen") && !getCPUModel().contains("Intel")) { + Iris.info("Server Cpu: " + C.GRAY + getCPUModel()); + } + } + } + } catch (Exception e){ + Iris.info("Server Cpu: " + C.DARK_RED + "Failed"); + } - if(getCPUModel().contains("Intel")) Iris.info("Server Cpu: " + C.BLUE + getCPUModel()); - if(getCPUModel().contains("Ryzen")) Iris.info("Server Cpu: " + C.RED + getCPUModel()); - if(!getCPUModel().contains("Intel") && !getCPUModel().contains("Ryzen")) Iris.info("Server Cpu: " + C.DARK_GRAY + getCPUModel()); - - Iris.info("Process Threads: " + getCPUThreads()); + Iris.info("Process Threads: " + Runtime.getRuntime().availableProcessors()); Iris.info("Process Memory: " + getHardware.getProcessMemory() + " MB"); + Iris.info("Free DiskSpace: " + Form.ofSize(freeSpace.getFreeSpace(), 1024)); if (getHardware.getProcessMemory() < 5999) { Iris.warn("6GB+ Ram is recommended"); } diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index d37dfb1b5..7d7b58256 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -141,12 +141,12 @@ public class IrisSettings { public int resourceLoaderCacheSize = 1_024; public int objectLoaderCacheSize = 4_096; public int scriptLoaderCacheSize = 512; - public boolean dynamicPerformanceMode = true; + public int tectonicUnloadThreads = -1; // -1 = Disabled and instead use the dynamic method } @Data public static class IrisSettingsGeneral { - public boolean bootUnstable = false; + public boolean ignoreBootMode = false; public boolean useIntegratedChunkHandler = false; public boolean commandSounds = true; public boolean debug = false; diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java index 807f700a7..85e6f1644 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandDeveloper.java @@ -20,20 +20,31 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; import com.volmit.iris.core.loader.IrisData; +import com.volmit.iris.core.service.IrisEngineSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; -import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; 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.format.Form; -import com.volmit.iris.util.mantle.Mantle; +import com.volmit.iris.util.io.IO; +import com.volmit.iris.util.mantle.TectonicPlate; +import com.volmit.iris.util.plugin.VolmitSender; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; +import net.jpountz.lz4.LZ4FrameInputStream; +import net.jpountz.lz4.LZ4FrameOutputStream; +import org.apache.commons.lang.RandomStringUtils; import org.bukkit.Bukkit; import org.bukkit.World; -import java.util.concurrent.atomic.AtomicInteger; +import java.io.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; @Decree(name = "Developer", origin = DecreeOrigin.BOTH, description = "Iris World Manager", aliases = {"dev"}) public class CommandDeveloper implements DecreeExecutor { @@ -51,13 +62,14 @@ public class CommandDeveloper implements DecreeExecutor { Engine engine = IrisToolbelt.access(world).getEngine(); if(engine != null) { long lastUseSize = engine.getMantle().getLastUseMapMemoryUsage(); - long outputToUnload = engine.getMantle().getToUnload(); Iris.info("-------------------------"); Iris.info(C.DARK_PURPLE + "Engine Status"); - Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + engine.getMantle().getTectonicLimit()); - Iris.info(C.DARK_PURPLE + "Tectonic Plates: " + C.LIGHT_PURPLE + engine.getMantle().getLoadedRegionCount()); - Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + outputToUnload); + Iris.info(C.DARK_PURPLE + "Tectonic Threads: " + C.LIGHT_PURPLE + engine.getMantle().getDynamicThreads()); + Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit()); + Iris.info(C.DARK_PURPLE + "Tectonic Loaded Plates: " + C.LIGHT_PURPLE + engine.getMantle().getLoadedRegionCount()); + Iris.info(C.DARK_PURPLE + "Tectonic Plates: " + C.LIGHT_PURPLE + engine.getMantle().getNotClearedLoadedRegions()); + Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + engine.getMantle().getToUnload()); Iris.info(C.DARK_PURPLE + "Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration((long) engine.getMantle().getTectonicDuration())); Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize())); Iris.info(C.DARK_PURPLE + "LastUse Size: " + C.LIGHT_PURPLE + Form.mem(lastUseSize)); @@ -66,6 +78,92 @@ public class CommandDeveloper implements DecreeExecutor { Iris.info(C.RED + "Engine is null!"); } } + @Decree(description = "Test", origin = DecreeOrigin.BOTH) + public void test() { + Iris.info("Test Developer CMD Executed"); + } + + @Decree(description = "Test the compression algorithms") + public void compression( + @Param(description = "base IrisWorld") World world, + @Param(description = "raw TectonicPlate File") String path, + @Param(description = "Algorithm to Test") String algorithm, + @Param(description = "Amount of Tests") int amount) { + if (!IrisToolbelt.isIrisWorld(world)) { + sender().sendMessage(C.RED + "This is not an Iris world. Iris worlds: " + String.join(", ", Bukkit.getServer().getWorlds().stream().filter(IrisToolbelt::isIrisWorld).map(World::getName).toList())); + return; + } + + File file = new File(path); + if (!file.exists()) return; + + Engine engine = IrisToolbelt.access(world).getEngine(); + if(engine != null) { + int height = engine.getTarget().getHeight(); + ExecutorService service = Executors.newFixedThreadPool(1); + VolmitSender sender = sender(); + service.submit(() -> { + try { + DataInputStream raw = new DataInputStream(new FileInputStream(file)); + TectonicPlate plate = new TectonicPlate(height, raw); + raw.close(); + + double d1 = 0; + double d2 = 0; + long size = 0; + File folder = new File("tmp"); + folder.mkdirs(); + for (int i = 0; i < amount; i++) { + File tmp = new File(folder, RandomStringUtils.randomAlphanumeric(10) + "." + algorithm + ".bin"); + DataOutputStream dos = createOutput(tmp, algorithm); + long start = System.currentTimeMillis(); + plate.write(dos); + dos.close(); + d1 += System.currentTimeMillis() - start; + if (size == 0) + size = tmp.length(); + start = System.currentTimeMillis(); + DataInputStream din = createInput(tmp, algorithm); + new TectonicPlate(height, din); + din.close(); + d2 += System.currentTimeMillis() - start; + tmp.delete(); + } + IO.delete(folder); + sender.sendMessage(algorithm + " is " + Form.fileSize(size) + " big after compression"); + sender.sendMessage(algorithm + " Took " + d2/amount + "ms to read"); + sender.sendMessage(algorithm + " Took " + d1/amount + "ms to write"); + } catch (Throwable e) { + e.printStackTrace(); + } + }); + service.shutdown(); + } else { + Iris.info(C.RED + "Engine is null!"); + } + } + + private DataInputStream createInput(File file, String algorithm) throws Throwable { + FileInputStream in = new FileInputStream(file); + + return new DataInputStream(switch (algorithm) { + case "gzip" -> new GZIPInputStream(in); + case "lz4f" -> new LZ4FrameInputStream(in); + case "lz4b" -> new LZ4BlockInputStream(in); + default -> throw new IllegalStateException("Unexpected value: " + algorithm); + }); + } + + private DataOutputStream createOutput(File file, String algorithm) throws Throwable { + FileOutputStream out = new FileOutputStream(file); + + return new DataOutputStream(switch (algorithm) { + case "gzip" -> new GZIPOutputStream(out); + case "lz4f" -> new LZ4FrameOutputStream(out); + case "lz4b" -> new LZ4BlockOutputStream(out); + default -> throw new IllegalStateException("Unexpected value: " + algorithm); + }); + } } diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandFind.java b/core/src/main/java/com/volmit/iris/core/commands/CommandFind.java index a85728803..d0bd97c68 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandFind.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandFind.java @@ -34,7 +34,9 @@ public class CommandFind implements DecreeExecutor { @Decree(description = "Find a biome") public void biome( @Param(description = "The biome to look for") - IrisBiome biome + IrisBiome biome, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport ) { Engine e = engine(); @@ -43,13 +45,15 @@ public class CommandFind implements DecreeExecutor { return; } - e.gotoBiome(biome, player()); + e.gotoBiome(biome, player(), teleport); } @Decree(description = "Find a region") public void region( @Param(description = "The region to look for") - IrisRegion region + IrisRegion region, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport ) { Engine e = engine(); @@ -58,13 +62,15 @@ public class CommandFind implements DecreeExecutor { return; } - e.gotoRegion(region, player()); + e.gotoRegion(region, player(), teleport); } @Decree(description = "Find a structure") public void structure( @Param(description = "The structure to look for") - IrisJigsawStructure structure + IrisJigsawStructure structure, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport ) { Engine e = engine(); @@ -73,13 +79,15 @@ public class CommandFind implements DecreeExecutor { return; } - e.gotoJigsaw(structure, player()); + e.gotoJigsaw(structure, player(), teleport); } @Decree(description = "Find a point of interest.") public void poi( @Param(description = "The type of PoI to look for.") - String type + String type, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport ) { Engine e = engine(); if (e == null) { @@ -87,13 +95,15 @@ public class CommandFind implements DecreeExecutor { return; } - e.gotoPOI(type, player()); + e.gotoPOI(type, player(), teleport); } @Decree(description = "Find an object") public void object( @Param(description = "The object to look for", customHandler = ObjectHandler.class) - String object + String object, + @Param(description = "Should you be teleported", defaultValue = "true") + boolean teleport ) { Engine e = engine(); @@ -102,6 +112,6 @@ public class CommandFind implements DecreeExecutor { return; } - e.gotoObject(object, player()); + e.gotoObject(object, player(), teleport); } } diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java index b80548c29..dda9d8d45 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandIris.java @@ -23,45 +23,35 @@ import com.volmit.iris.core.IrisSettings; 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; -import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.core.safeguard.UtilsSFG; -import com.volmit.iris.util.collection.KList; -import com.volmit.iris.util.decree.DecreeContext; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; import com.volmit.iris.util.decree.annotations.Decree; import com.volmit.iris.util.decree.annotations.Param; import com.volmit.iris.util.decree.specialhandlers.NullablePlayerHandler; import com.volmit.iris.util.format.C; -import com.volmit.iris.util.format.Form; -import com.volmit.iris.util.parallel.BurstExecutor; -import com.volmit.iris.util.parallel.MultiBurst; import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.scheduling.J; -import com.volmit.iris.util.scheduling.jobs.QueueJob; import lombok.Getter; import org.bukkit.Bukkit; -import org.bukkit.Chunk; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; import java.io.File; import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; 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.incompatiblePlugins; +import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatibilities; @Decree(name = "iris", aliases = {"ir", "irs"}, description = "Basic Command") public class CommandIris implements DecreeExecutor { private CommandStudio studio; private CommandPregen pregen; + private CommandLazyPregen lazyPregen; private CommandSettings settings; private CommandObject object; private CommandJigsaw jigsaw; @@ -72,6 +62,7 @@ public class CommandIris implements DecreeExecutor { private CommandDeveloper developer; public static @Getter String BenchDimension; + public static boolean worldCreation = false; @Decree(description = "Create a new world", aliases = {"+", "c"}) public void create( @@ -83,7 +74,7 @@ public class CommandIris implements DecreeExecutor { long seed ) { if(sender() instanceof Player) { - if (incompatiblePlugins.get("Multiverse-Core")) { + if (incompatibilities.get("Multiverse-Core")) { sender().sendMessage(C.RED + "Your server has an incompatibility that may corrupt all worlds on the server if not handled properly."); sender().sendMessage(C.RED + "it is strongly advised for you to take action. see log for full detail"); sender().sendMessage(C.RED + "----------------------------------------------------------------"); @@ -91,7 +82,7 @@ public class CommandIris implements DecreeExecutor { sender().sendMessage(C.RED + UtilsSFG.MSGIncompatibleWarnings()); sender().sendMessage(C.RED + "----------------------------------------------------------------"); } - if (unstablemode && !incompatiblePlugins.get("Multiverse-Core")) { + if (unstablemode && !incompatibilities.get("Multiverse-Core")) { sender().sendMessage(C.RED + "Your server is experiencing an incompatibility with the Iris plugin."); sender().sendMessage(C.RED + "Please rectify this problem to avoid further complications."); sender().sendMessage(C.RED + "----------------------------------------------------------------"); @@ -117,6 +108,7 @@ public class CommandIris implements DecreeExecutor { } try { + worldCreation = true; IrisToolbelt.createWorld() .dimension(type.getLoadKey()) .name(name) @@ -128,9 +120,10 @@ public class CommandIris implements DecreeExecutor { sender().sendMessage(C.RED + "Exception raised during creation. See the console for more details."); Iris.error("Exception raised during world creation: " + e.getMessage()); Iris.reportError(e); + worldCreation = false; return; } - + worldCreation = false; sender().sendMessage(C.GREEN + "Successfully created your world!"); } @@ -163,6 +156,8 @@ public class CommandIris implements DecreeExecutor { public void version() { 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) { @@ -171,6 +166,7 @@ public class CommandIris implements DecreeExecutor { Iris.info(C.RED + "Benchmark already is in progress."); } } + /* /todo Fix PREGEN @Decree(description = "Benchmark a pack", origin = DecreeOrigin.CONSOLE) @@ -239,7 +235,7 @@ public class CommandIris implements DecreeExecutor { IrisToolbelt.evacuate(world, "Deleting world"); deletingWorld = true; Bukkit.unloadWorld(world, false); - int retries = 10; + int retries = 12; if (delete) { if (deleteDirectory(world.getWorldFolder())) { sender().sendMessage(C.GREEN + "Successfully removed world folder"); @@ -249,13 +245,12 @@ public class CommandIris implements DecreeExecutor { sender().sendMessage(C.GREEN + "Successfully removed world folder"); break; } - sender().sendMessage(C.GREEN + "DEBUG1"); retries--; if (retries == 0){ sender().sendMessage(C.RED + "Failed to remove world folder"); break; } - J.sleep(2000); + J.sleep(3000); } } } @@ -364,77 +359,6 @@ public class CommandIris implements DecreeExecutor { sender().sendMessage(C.GREEN + "Hotloaded settings"); } - @Decree(name = "regen", description = "Regenerate nearby chunks.", aliases = "rg", sync = true, origin = DecreeOrigin.PLAYER) - public void regen( - @Param(name = "radius", description = "The radius of nearby cunks", defaultValue = "5") - int radius - ) { - if (IrisToolbelt.isIrisWorld(player().getWorld())) { - VolmitSender sender = sender(); - J.a(() -> { - DecreeContext.touch(sender); - PlatformChunkGenerator plat = IrisToolbelt.access(player().getWorld()); - Engine engine = plat.getEngine(); - try { - Chunk cx = player().getLocation().getChunk(); - KList js = new KList<>(); - BurstExecutor b = MultiBurst.burst.burst(); - b.setMulticore(false); - int rad = engine.getMantle().getRealRadius(); - for (int i = -(radius + rad); i <= radius + rad; i++) { - for (int j = -(radius + rad); j <= radius + rad; j++) { - engine.getMantle().getMantle().deleteChunk(i + cx.getX(), j + cx.getZ()); - } - } - - for (int i = -radius; i <= radius; i++) { - for (int j = -radius; j <= radius; j++) { - int finalJ = j; - int finalI = i; - b.queue(() -> plat.injectChunkReplacement(player().getWorld(), finalI + cx.getX(), finalJ + cx.getZ(), (f) -> { - synchronized (js) { - js.add(f); - } - })); - } - } - - b.complete(); - sender().sendMessage(C.GREEN + "Regenerating " + Form.f(js.size()) + " Sections"); - QueueJob r = new QueueJob<>() { - final KList> futures = new KList<>(); - - @Override - public void execute(Runnable runnable) { - futures.add(J.sfut(runnable)); - - if (futures.size() > 64) { - while (futures.isNotEmpty()) { - try { - futures.remove(0).get(); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - } - } - } - - @Override - public String getName() { - return "Regenerating"; - } - }; - r.queue(js); - r.execute(sender()); - } catch (Throwable e) { - sender().sendMessage("Unable to parse view-distance"); - } - }); - } else { - sender().sendMessage(C.RED + "You must be in an Iris World to use regen!"); - } - } - @Decree(description = "Update the pack of a world (UNSAFE!)", name = "^world", aliases = "update-world") public void updateWorld( @Param(description = "The world to update", contextual = true) diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandLazyPregen.java b/core/src/main/java/com/volmit/iris/core/commands/CommandLazyPregen.java new file mode 100644 index 000000000..a3d4c622c --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandLazyPregen.java @@ -0,0 +1,121 @@ +/* + * Iris is a World Generator for Minecraft Bukkit Servers + * Copyright (c) 2022 Arcane Arts (Volmit Software) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.volmit.iris.core.commands; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.core.gui.PregeneratorJob; +import com.volmit.iris.core.pregenerator.LazyPregenerator; +import com.volmit.iris.core.pregenerator.PregenTask; +import com.volmit.iris.core.tools.IrisToolbelt; +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 = "lazypregen", aliases = "lazy", description = "Pregenerate your Iris worlds!") +public class CommandLazyPregen implements DecreeExecutor { + public String worldName; + @Decree(description = "Pregenerate 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, + @Param(aliases = "maxcpm", description = "Limit the chunks per minute the pregen will generate", defaultValue = "999999999") + int cpm, + @Param(aliases = "silent", description = "Silent generation", defaultValue = "false") + boolean silent + ) { + + worldName = world.getName(); + File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName()); + File lazyFile = new File(worldDirectory, "lazygen.json"); + if (lazyFile.exists()) { + sender().sendMessage(C.BLUE + "Lazy pregen is already in progress"); + Iris.info(C.YELLOW + "Lazy pregen is already in progress"); + 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."); + } + + LazyPregenerator.LazyPregenJob pregenJob = LazyPregenerator.LazyPregenJob.builder() + .world(worldName) + .healingPosition(0) + .healing(false) + .chunksPerMinute(cpm) + .radiusBlocks(radius) + .position(0) + .silent(silent) + .build(); + + File lazyGenFile = new File(worldDirectory, "lazygen.json"); + LazyPregenerator pregenerator = new LazyPregenerator(pregenJob, lazyGenFile); + pregenerator.start(); + + String msg = C.GREEN + "LazyPregen 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 pregeneration task", aliases = "x") + public void stop( + @Param(aliases = "world", description = "The world to pause") + World world + ) throws IOException { + if (LazyPregenerator.getInstance() != null) { + LazyPregenerator.getInstance().shutdownInstance(world); + sender().sendMessage(C.LIGHT_PURPLE + "Closed lazygen instance for " + world.getName()); + } 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 (LazyPregenerator.getInstance() != null) { + LazyPregenerator.getInstance().setPausedLazy(world); + sender().sendMessage(C.GREEN + "Paused/unpaused Lazy Pregen, now: " + (LazyPregenerator.getInstance().isPausedLazy(world) ? "Paused" : "Running") + "."); + } else { + sender().sendMessage(C.YELLOW + "No active Lazy Pregen tasks to pause/unpause."); + + } + } +} diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java b/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java index a9bc9ff7a..73f93091d 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandPregen.java @@ -19,6 +19,7 @@ package com.volmit.iris.core.commands; import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.gui.PregeneratorJob; import com.volmit.iris.core.pregenerator.LazyPregenerator; import com.volmit.iris.core.pregenerator.PregenTask; @@ -43,68 +44,29 @@ 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(aliases = "method", description = "The pregen method that will get used. Lazy or Async", defaultValue = "async") - String method - ) { - if(method.equals("async") || method.equals("lazy")){ - if (method.equalsIgnoreCase("async")) { - 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."); - } - radius = Math.max(radius, 1024); - int w = (radius >> 9 + 1) * 2; - IrisToolbelt.pregenerate(PregenTask - .builder() - .center(new Position2(center.getBlockX() >> 9, center.getBlockZ() >> 9)) - .width(w) - .height(w) - .build(), world); - String msg = C.GREEN + "Pregen started in " + C.GOLD + world.getName() + 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(); - } + Vector center + ) { + 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."); } - if (method.equalsIgnoreCase("lazy")) { - String worldName = world.getName(); - 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."); - } - - LazyPregenerator.LazyPregenJob pregenJob = LazyPregenerator.LazyPregenJob.builder() - .world(worldName) - .healingPosition(0) - .healing(false) - .chunksPerMinute(999999999) - .radiusBlocks(radius) - .position(0) - .build(); - - LazyPregenerator pregenerator = new LazyPregenerator(pregenJob, new File("plugins/Iris/lazygen.json")); - pregenerator.start(); - - String msg = C.GREEN + "Pregen 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(); - } - } - } else { - sender().sendMessage(C.RED + "Please use a valid method."); - + radius = Math.max(radius, 1024); + int w = (radius >> 9 + 1) * 2; + IrisToolbelt.pregenerate(PregenTask + .builder() + .center(new Position2(center.getBlockX() >> 9, center.getBlockZ() >> 9)) + .width(w) + .height(w) + .build(), world); + String msg = C.GREEN + "Pregen started in " + C.GOLD + world.getName() + 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 pregeneration task", aliases = "x") diff --git a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java index 16405436c..41a271c57 100644 --- a/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandStudio.java @@ -29,9 +29,11 @@ import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.*; +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.collection.KSet; +import com.volmit.iris.util.decree.DecreeContext; import com.volmit.iris.util.decree.DecreeExecutor; import com.volmit.iris.util.decree.DecreeOrigin; import com.volmit.iris.util.decree.annotations.Decree; @@ -48,8 +50,13 @@ import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.RNG; import com.volmit.iris.util.math.Spiraler; import com.volmit.iris.util.noise.CNG; +import com.volmit.iris.util.parallel.BurstExecutor; +import com.volmit.iris.util.parallel.MultiBurst; +import com.volmit.iris.util.plugin.VolmitSender; +import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.O; import com.volmit.iris.util.scheduling.PrecisionStopwatch; +import com.volmit.iris.util.scheduling.jobs.QueueJob; import io.papermc.lib.PaperLib; import org.bukkit.*; import org.bukkit.event.inventory.InventoryType; @@ -67,6 +74,8 @@ import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.Objects; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; import java.util.function.Supplier; @Decree(name = "studio", aliases = {"std", "s"}, description = "Studio Commands", studio = true) @@ -143,6 +152,77 @@ public class CommandStudio implements DecreeExecutor { sender().sendMessage(C.GREEN + "The \"" + dimension.getName() + "\" pack has version: " + dimension.getVersion()); } + @Decree(name = "regen", description = "Regenerate nearby chunks.", aliases = "rg", sync = true, origin = DecreeOrigin.PLAYER) + public void regen( + @Param(name = "radius", description = "The radius of nearby cunks", defaultValue = "5") + int radius + ) { + if (IrisToolbelt.isIrisWorld(player().getWorld())) { + VolmitSender sender = sender(); + J.a(() -> { + DecreeContext.touch(sender); + PlatformChunkGenerator plat = IrisToolbelt.access(player().getWorld()); + Engine engine = plat.getEngine(); + try { + Chunk cx = player().getLocation().getChunk(); + KList js = new KList<>(); + BurstExecutor b = MultiBurst.burst.burst(); + b.setMulticore(false); + int rad = engine.getMantle().getRealRadius(); + for (int i = -(radius + rad); i <= radius + rad; i++) { + for (int j = -(radius + rad); j <= radius + rad; j++) { + engine.getMantle().getMantle().deleteChunk(i + cx.getX(), j + cx.getZ()); + } + } + + for (int i = -radius; i <= radius; i++) { + for (int j = -radius; j <= radius; j++) { + int finalJ = j; + int finalI = i; + b.queue(() -> plat.injectChunkReplacement(player().getWorld(), finalI + cx.getX(), finalJ + cx.getZ(), (f) -> { + synchronized (js) { + js.add(f); + } + })); + } + } + + b.complete(); + sender().sendMessage(C.GREEN + "Regenerating " + Form.f(js.size()) + " Sections"); + QueueJob r = new QueueJob<>() { + final KList> futures = new KList<>(); + + @Override + public void execute(Runnable runnable) { + futures.add(J.sfut(runnable)); + + if (futures.size() > 64) { + while (futures.isNotEmpty()) { + try { + futures.remove(0).get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + } + } + + @Override + public String getName() { + return "Regenerating"; + } + }; + r.queue(js); + r.execute(sender()); + } catch (Throwable e) { + sender().sendMessage("Unable to parse view-distance"); + } + }); + } else { + sender().sendMessage(C.RED + "You must be in an Iris World to use regen!"); + } + } + @Decree(description = "Convert objects in the \"convert\" folder") public void convert() { Iris.service(ConversionSVC.class).check(sender()); diff --git a/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java b/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java index 8b4900972..dd0a1b068 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java +++ b/core/src/main/java/com/volmit/iris/core/loader/ResourceLoader.java @@ -40,6 +40,8 @@ import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import java.io.*; import java.util.Locale; @@ -52,6 +54,8 @@ import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @Data +@EqualsAndHashCode(exclude = "manager") +@ToString(exclude = "manager") public class ResourceLoader implements MeteredCache { public static final AtomicDouble tlt = new AtomicDouble(0); private static final int CACHE_SIZE = 100000; diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java index 513600a95..11128d04a 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/IrisPregenerator.java @@ -167,7 +167,7 @@ public class IrisPregenerator { generator.close(); ticker.interrupt(); listener.onClose(); - getMantle().trim(0); + getMantle().trim(0, 0); } private void visitRegion(int x, int z, boolean regions) { diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/LazyPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/LazyPregenerator.java index 7deeb181e..d3ee26b54 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/LazyPregenerator.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/LazyPregenerator.java @@ -14,22 +14,32 @@ import com.volmit.iris.util.scheduling.J; import io.papermc.lib.PaperLib; import lombok.Builder; import lombok.Data; +import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.world.WorldUnloadEvent; +import org.bukkit.scheduler.BukkitRunnable; import java.io.File; import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.HashMap; +import java.util.Map; + public class LazyPregenerator extends Thread implements Listener { + @Getter + private static LazyPregenerator instance; private final LazyPregenJob job; private final File destination; private final int maxPosition; - private final World world; + private World world; private final long rate; private final ChronoLatch latch; private static AtomicInteger lazyGeneratedChunks; @@ -37,6 +47,10 @@ public class LazyPregenerator extends Thread implements Listener { private final AtomicInteger lazyTotalChunks; private final AtomicLong startTime; private final RollingSequence chunksPerSecond; + private final RollingSequence chunksPerMinute; + + // A map to keep track of jobs for each world + private static final Map jobs = new HashMap<>(); public LazyPregenerator(LazyPregenJob job, File destination) { this.job = job; @@ -44,16 +58,16 @@ public class LazyPregenerator extends Thread implements Listener { this.maxPosition = new Spiraler(job.getRadiusBlocks() * 2, job.getRadiusBlocks() * 2, (x, z) -> { }).count(); this.world = Bukkit.getWorld(job.getWorld()); - this.rate = Math.round((1D / (job.chunksPerMinute / 60D)) * 1000D); - this.latch = new ChronoLatch(6000); - startTime = new AtomicLong(M.ms()); - chunksPerSecond = new RollingSequence(10); + this.rate = Math.round((1D / (job.getChunksPerMinute() / 60D)) * 1000D); + this.latch = new ChronoLatch(15000); + this.startTime = new AtomicLong(M.ms()); + this.chunksPerSecond = new RollingSequence(10); + this.chunksPerMinute = new RollingSequence(10); lazyGeneratedChunks = new AtomicInteger(0); - generatedLast = new AtomicInteger(0); - lazyTotalChunks = new AtomicInteger(); - - int radius = job.getRadiusBlocks(); - lazyTotalChunks.set((int) Math.ceil(Math.pow((2.0 * radius) / 16, 2))); + this.generatedLast = new AtomicInteger(0); + this.lazyTotalChunks = new AtomicInteger((int) Math.ceil(Math.pow((2.0 * job.getRadiusBlocks()) / 16, 2))); + jobs.put(job.getWorld(), job); + LazyPregenerator.instance = this; } public LazyPregenerator(File file) throws IOException { @@ -63,7 +77,6 @@ public class LazyPregenerator extends Thread implements Listener { public static void loadLazyGenerators() { for (World i : Bukkit.getWorlds()) { File lazygen = new File(i.getWorldFolder(), "lazygen.json"); - if (lazygen.exists()) { try { LazyPregenerator p = new LazyPregenerator(lazygen); @@ -97,18 +110,18 @@ public class LazyPregenerator extends Thread implements Listener { } public void tick() { - if (latch.flip()) { + LazyPregenJob job = jobs.get(world.getName()); + if (latch.flip() && !job.paused) { long eta = computeETA(); save(); int secondGenerated = lazyGeneratedChunks.get() - generatedLast.get(); generatedLast.set(lazyGeneratedChunks.get()); - secondGenerated = secondGenerated / 6; + secondGenerated = secondGenerated / 15; chunksPerSecond.put(secondGenerated); - Iris.info("LazyGen: " + C.IRIS + world.getName() + C.RESET + " RTT: " + Form.f(lazyGeneratedChunks.get()) + " of " + Form.f(lazyTotalChunks.get()) + " " + Form.f((int) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2)); - //Iris.info("Debug: " + maxPosition); - //Iris.info("Debug1: " + job.getPosition()); - - // todo: Maxpos borked + chunksPerMinute.put(secondGenerated * 60); + if (!job.isSilent()) { + Iris.info("LazyGen: " + C.IRIS + world.getName() + C.RESET + " RTT: " + Form.f(lazyGeneratedChunks.get()) + " of " + Form.f(lazyTotalChunks.get()) + " " + Form.f((int) chunksPerMinute.getAverage()) + "/m ETA: " + Form.duration((double) eta, 2)); + } } if (lazyGeneratedChunks.get() >= lazyTotalChunks.get()) { @@ -123,27 +136,43 @@ public class LazyPregenerator extends Thread implements Listener { } else { int pos = job.getPosition() + 1; job.setPosition(pos); - tickGenerate(getChunk(pos)); + if (!job.paused) { + tickGenerate(getChunk(pos)); + } } } private long computeETA() { - return (long) (lazyTotalChunks.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) - ((lazyTotalChunks.get() - lazyGeneratedChunks.get()) * ((double) (M.ms() - startTime.get()) / (double) lazyGeneratedChunks.get())) : - // If no, use quick function (which is less accurate over time but responds better to the initial delay) - ((lazyTotalChunks.get() - lazyGeneratedChunks.get()) / chunksPerSecond.getAverage()) * 1000 // - ); + return (long) ((lazyTotalChunks.get() - lazyGeneratedChunks.get()) / chunksPerMinute.getAverage()) * 1000; + // todo broken } + private final ExecutorService executorService = Executors.newSingleThreadExecutor(); + private void tickGenerate(Position2 chunk) { - if (PaperLib.isPaper()) { - PaperLib.getChunkAtAsync(world, chunk.getX(), chunk.getZ(), true).thenAccept((i) -> Iris.verbose("Generated Async " + chunk)); - } else { - J.s(() -> world.getChunkAt(chunk.getX(), chunk.getZ())); - Iris.verbose("Generated " + chunk); - } - lazyGeneratedChunks.addAndGet(1); + executorService.submit(() -> { + CountDownLatch latch = new CountDownLatch(1); + if (PaperLib.isPaper()) { + PaperLib.getChunkAtAsync(world, chunk.getX(), chunk.getZ(), true) + .thenAccept((i) -> { + LazyPregenJob j = jobs.get(world.getName()); + if (!j.paused) { + Iris.verbose("Generated Async " + chunk); + } + latch.countDown(); + }); + } else { + J.s(() -> { + world.getChunkAt(chunk.getX(), chunk.getZ()); + Iris.verbose("Generated " + chunk); + latch.countDown(); + }); + } + try { + latch.await(); + } catch (InterruptedException ignored) {} + lazyGeneratedChunks.addAndGet(1); + }); } private void tickRegenerate(Position2 chunk) { @@ -177,6 +206,64 @@ public class LazyPregenerator extends Thread implements Listener { }); } + public static void setPausedLazy(World world) { + // todo: doesnt actually pause + LazyPregenJob job = jobs.get(world.getName()); + if (isPausedLazy(world)){ + job.paused = false; + } else { + job.paused = true; + } + + if ( job.paused) { + Iris.info(C.BLUE + "LazyGen: " + C.IRIS + world.getName() + C.BLUE + " Paused"); + } else { + Iris.info(C.BLUE + "LazyGen: " + C.IRIS + world.getName() + C.BLUE + " Resumed"); + } + } + + public static boolean isPausedLazy(World world) { + LazyPregenJob job = jobs.get(world.getName()); + return job != null && job.isPaused(); + } + + public void shutdownInstance(World world) throws IOException { + Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " Shutting down.."); + LazyPregenJob job = jobs.get(world.getName()); + File worldDirectory = new File(Bukkit.getWorldContainer(), world.getName()); + File lazyFile = new File(worldDirectory, "lazygen.json"); + + if (job == null) { + Iris.error("No Lazygen job found for world: " + world.getName()); + return; + } + + try { + if (!job.isPaused()) { + job.setPaused(true); + } + save(); + jobs.remove(world.getName()); + new BukkitRunnable() { + @Override + public void run() { + while (lazyFile.exists()){ + lazyFile.delete(); + J.sleep(1000); + } + Iris.info("LazyGen: " + C.IRIS + world.getName() + C.BLUE + " File deleted and instance closed."); + } + }.runTaskLater(Iris.instance, 20L); + } catch (Exception e) { + Iris.error("Failed to shutdown Lazygen for " + world.getName()); + e.printStackTrace(); + } finally { + saveNow(); + interrupt(); + } + } + + public void saveNow() throws IOException { IO.writeAll(this.destination, new Gson().toJson(job)); } @@ -190,10 +277,15 @@ public class LazyPregenerator extends Thread implements Listener { @Builder.Default private boolean healing = false; @Builder.Default - private int chunksPerMinute = 32; // 48 hours is roughly 5000 radius + private int chunksPerMinute = 32; @Builder.Default private int radiusBlocks = 5000; @Builder.Default private int position = 0; + @Builder.Default + boolean silent = false; + @Builder.Default + boolean paused = false; } } + diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java index a7d945dfc..4fb25371a 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java @@ -1,11 +1,12 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.util.format.C; public class IrisSafeguard { public static boolean unstablemode = false; + public static boolean warningmode = false; + public static boolean stablemode = false; + public static void IrisSafeguardSystem() { Iris.info("Enabled Iris SafeGuard"); ServerBootSFG.BootCheck(); diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/ModesSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/ModesSFG.java new file mode 100644 index 000000000..babfcfac8 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/safeguard/ModesSFG.java @@ -0,0 +1,81 @@ +package com.volmit.iris.core.safeguard; + +import com.volmit.iris.Iris; +import com.volmit.iris.core.IrisSettings; +import com.volmit.iris.util.format.C; + +public class ModesSFG { + public static void selectMode() { + if (IrisSafeguard.unstablemode) { + Iris.safeguard(C.DARK_RED + "Iris is running in Unstable Mode"); + unstable(); + } + if (IrisSafeguard.warningmode) { + Iris.safeguard(C.GOLD + "Iris is running in Warning Mode"); + warning(); + } + if (IrisSafeguard.stablemode) { + stable(); + } + } + + public static void stable() { + Iris.safeguard(C.BLUE + "Iris is running Stable"); + } + + public static void unstable() { + + UtilsSFG.printIncompatibleWarnings(); + + if (IrisSafeguard.unstablemode) { + Iris.info(""); + Iris.info(C.DARK_GRAY + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--"); + Iris.info(C.RED + "Iris is running in unstable mode which may cause the following issues:"); + Iris.info(C.DARK_RED + "Server Issues"); + Iris.info(C.RED + "- Server won't boot"); + Iris.info(C.RED + "- Data Loss"); + Iris.info(C.RED + "- Unexpected behavior."); + Iris.info(C.RED + "- And More..."); + Iris.info(C.DARK_RED + "World Issues"); + Iris.info(C.RED + "- Worlds can't load due to corruption."); + Iris.info(C.RED + "- Worlds may slowly corrupt until they can't load."); + Iris.info(C.RED + "- World data loss."); + Iris.info(C.RED + "- And More..."); + Iris.info(C.DARK_RED + "ATTENTION: " + C.RED + "While running Iris in unstable mode, you won't be eligible for support."); + Iris.info(C.DARK_RED + "CAUSE: " + C.RED + UtilsSFG.MSGIncompatibleWarnings()); + + if (IrisSettings.get().getGeneral().ignoreBootMode) { + Iris.info(C.DARK_RED + "Boot Unstable is set to true, continuing with the startup process."); + } else { + Iris.info(C.DARK_RED + "Go to plugins/iris/settings.json and set ignoreBootMode to true if you wish to proceed."); + while (true) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // no + } + } + } + Iris.info(""); + } + } + + public static void warning() { + + UtilsSFG.printIncompatibleWarnings(); + + if (IrisSafeguard.warningmode) { + Iris.info(""); + Iris.info(C.DARK_GRAY + "--==<" + C.GOLD + " IMPORTANT " + C.DARK_GRAY + ">==--"); + Iris.info(C.GOLD + "Iris is running in warning mode which may cause the following issues:"); + Iris.info(C.YELLOW + "- Data Loss"); + Iris.info(C.YELLOW + "- Errors"); + Iris.info(C.YELLOW + "- Broken worlds"); + Iris.info(C.YELLOW + "- Unexpected behavior."); + Iris.info(C.YELLOW + "- And perhaps further complications."); + Iris.info(C.GOLD + "ATTENTION: " + C.YELLOW + "While running Iris in unstable mode, you won't be eligible for support."); + Iris.info(C.GOLD + "CAUSE: " + C.YELLOW + UtilsSFG.MSGIncompatibleWarnings()); + Iris.info(""); + } + } +} diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/PerformanceSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/PerformanceSFG.java index aef67226e..90aab8cf5 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/PerformanceSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/PerformanceSFG.java @@ -1,13 +1,7 @@ package com.volmit.iris.core.safeguard; -import com.volmit.iris.core.IrisSettings; -import oshi.SystemInfo; -import oshi.hardware.GlobalMemory; - -import static com.volmit.iris.util.misc.getHardware.*; - public class PerformanceSFG { - public static void calculatePerformance(){ + public static void calculatePerformance() { } diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java index 4aa78c7c8..0a8b2983c 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/ServerBootSFG.java @@ -1,111 +1,170 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.nms.INMS; import com.volmit.iris.core.nms.v1X.NMSBinding1X; -import com.volmit.iris.util.SFG.WorldHandlerSFG; import org.bukkit.Bukkit; -import org.bukkit.World; import org.bukkit.plugin.Plugin; +import javax.print.attribute.standard.Severity; import java.io.File; -import java.util.*; +import java.util.HashMap; +import java.util.Map; +import java.util.StringJoiner; +import static com.volmit.iris.Iris.getJavaVersion; import static com.volmit.iris.Iris.instance; -import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; -import static com.volmit.iris.core.tools.IrisToolbelt.access; +import static com.volmit.iris.core.safeguard.IrisSafeguard.*; public class ServerBootSFG { - public static final Map incompatiblePlugins = new HashMap<>(); + public static final Map incompatibilities = new HashMap<>(); + public static boolean isJDK17 = true; + public static boolean hasEnoughDiskSpace = false; + public static boolean isJRE = false; + public static boolean hasPrivileges = false; public static boolean unsuportedversion = false; protected static boolean safeguardPassed; public static boolean passedserversoftware = true; - protected static byte count; - public static String allIncompatiblePlugins; + protected static int count; + protected static byte severityLow; + protected static byte severityMedium; + protected static byte severityHigh; + public static String allIncompatibilities; public static void BootCheck() { Iris.info("Checking for possible conflicts.."); org.bukkit.plugin.PluginManager pluginManager = Bukkit.getPluginManager(); Plugin[] plugins = pluginManager.getPlugins(); - incompatiblePlugins.clear(); - incompatiblePlugins.put("Multiverse-Core", false); - incompatiblePlugins.put("Dynmap", false); - incompatiblePlugins.put("TerraformGenerator", false); - incompatiblePlugins.put("Stratos", false); + incompatibilities.clear(); + incompatibilities.put("Multiverse-Core", false); + incompatibilities.put("Dynmap", false); + incompatibilities.put("TerraformGenerator", false); + incompatibilities.put("Stratos", false); String pluginName; for (Plugin plugin : plugins) { pluginName = plugin.getName(); - Boolean flag = incompatiblePlugins.get(pluginName); + Boolean flag = incompatibilities.get(pluginName); if (flag != null && !flag) { - count++; - incompatiblePlugins.put(pluginName, true); + severityHigh++; + incompatibilities.put(pluginName, true); } } StringJoiner joiner = new StringJoiner(", "); - for (Map.Entry entry : incompatiblePlugins.entrySet()) { + for (Map.Entry entry : incompatibilities.entrySet()) { if (entry.getValue()) { joiner.add(entry.getKey()); } } if ( - !instance.getServer().getVersion().contains("Purpur") && - !instance.getServer().getVersion().contains("Paper") && - !instance.getServer().getVersion().contains("Spigot") && - !instance.getServer().getVersion().contains("Pufferfish") && - !instance.getServer().getVersion().contains("Bukkit")) - { + !instance.getServer().getVersion().contains("Purpur") && + !instance.getServer().getVersion().contains("Paper") && + !instance.getServer().getVersion().contains("Spigot") && + !instance.getServer().getVersion().contains("Pufferfish") && + !instance.getServer().getVersion().contains("Bukkit")) { passedserversoftware = false; joiner.add("Server Software"); - count++; + severityHigh++; } + if (INMS.get() instanceof NMSBinding1X) { unsuportedversion = true; joiner.add("Unsupported Minecraft Version"); - count++; + severityHigh++; } - allIncompatiblePlugins = joiner.toString(); - - safeguardPassed = (count == 0); - if(!safeguardPassed){ - unstablemode = true; - Iris.safeguard("Unstable mode has been activated."); + if (getJavaVersion() != 17) { + isJDK17 = false; + joiner.add("Unsupported Java version"); + severityMedium++; + } + if (!isJDK()) { + isJRE = true; + joiner.add("Unsupported JDK"); + severityMedium++; + } + if (!hasPrivileges()){ + hasPrivileges = true; + joiner.add("Insufficient Privileges"); + severityHigh++; + } + if (!enoughDiskSpace()){ + hasEnoughDiskSpace = false; + joiner.add("Insufficient Disk Space"); + severityHigh++; } - } - public static void CheckIrisWorlds() { - StringJoiner joiner = new StringJoiner(", "); - // Get the main server folder - File serverFolder = Bukkit.getWorldContainer(); + allIncompatibilities = joiner.toString(); - // List all files in the server folder - File[] listOfFiles = serverFolder.listFiles(); - - if (listOfFiles != null) { - for (File file : listOfFiles) { - // Check if it is a directory (world folders are directories) - if (file.isDirectory()) { - // Check for an "iris" folder inside the world directory - File irisFolder = new File(file, "iris"); - if (irisFolder.exists() && irisFolder.isDirectory()) { - String worldName = file.getName(); - joiner.add(worldName); - - // Check if the world is already loaded - if (Bukkit.getWorld(worldName) == null) { - WorldHandlerSFG.LoadWorld(worldName); - } - } - } - } - } else { - Bukkit.getLogger().warning("No files found in the server folder."); + safeguardPassed = (severityHigh == 0 && severityMedium == 0 && severityLow == 0); + count = severityHigh + severityMedium + severityLow; + if (safeguardPassed) { + stablemode = true; + Iris.safeguard("Stable mode has been activated."); + } + if (!safeguardPassed) { + if (severityMedium >= 1 && severityHigh == 0) { + warningmode = true; + Iris.safeguard("Warning mode has been activated."); } - // No Idea what I should do with this - String worldsList = joiner.toString(); + if (severityHigh >= 1) { + unstablemode = true; + Iris.safeguard("Unstable mode has been activated."); + } + } } + + public static boolean isJDK() { + String path = System.getProperty("sun.boot.library.path"); + if (path != null) { + String javacPath = ""; + if (path.endsWith(File.separator + "bin")) { + javacPath = path; + } else { + int libIndex = path.lastIndexOf(File.separator + "lib"); + if (libIndex > 0) { + javacPath = path.substring(0, libIndex) + File.separator + "bin"; + } + } + if (checkJavac(javacPath)) + return true; + } + path = System.getProperty("java.home"); + return path != null && checkJavac(path + File.separator + "bin"); + } + + public static boolean hasPrivileges() { + File pv = new File(Bukkit.getWorldContainer() + "iristest.json"); + if (pv.exists()){ + pv.delete(); + } + try { + if (pv.createNewFile()){ + if (pv.canWrite() && pv.canRead()){ + pv.delete(); + return true; + } + } + } catch (Exception e){ + return false; + } + return false; + } + + public static boolean enoughDiskSpace() { + File freeSpace = new File(Bukkit.getWorldContainer() + "."); + double gigabytes = freeSpace.getFreeSpace() / (1024.0 * 1024.0 * 1024.0); + if (gigabytes > 3){ + return true; + } else { + return false; + } + } + + private static boolean checkJavac(String path) { + return !path.isEmpty() && (new File(path, "javac").exists() || new File(path, "javac.exe").exists()); + } + } diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java index 746512698..b4dac8a9d 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/UtilsSFG.java @@ -1,96 +1,68 @@ package com.volmit.iris.core.safeguard; import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; import com.volmit.iris.util.format.C; public class UtilsSFG { - public static void UnstableMode(){ - if (IrisSafeguard.unstablemode) { - Iris.safeguard(C.DARK_RED + "Iris is running in Unstable Mode"); - } else { - Iris.safeguard(C.BLUE + "Iris is running Stable"); - } - } - public static void SupportedServerSoftware(){ - if (!ServerBootSFG.passedserversoftware) { - Iris.safeguard(C.DARK_RED + "Server is running unsupported server software"); - Iris.safeguard(C.RED + "Supported: Purpur, Pufferfish, Paper, Spigot, Bukkit"); - } - } - public static void printIncompatibleWarnings(){ - // String SupportedIrisVersion = getDescription().getVersion(); //todo Automatic version - - if (ServerBootSFG.safeguardPassed) { - Iris.safeguard(C.BLUE + "0 Conflicts found"); - } else { - Iris.safeguard(C.DARK_RED + "" + ServerBootSFG.count + " Conflicts found"); - IrisSafeguard.unstablemode = true; - - if (ServerBootSFG.incompatiblePlugins.get("Multiverse-Core")) { - Iris.safeguard(C.RED + "Multiverse"); - Iris.safeguard(C.RED + "- The plugin Multiverse is not compatible with the server."); - Iris.safeguard(C.RED + "- If you want to have a world manager, consider using PhantomWorlds or MyWorlds instead."); - } - if (ServerBootSFG.incompatiblePlugins.get("Dynmap")) { - Iris.safeguard(C.RED + "Dynmap"); - Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server."); - Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap."); - } - if (ServerBootSFG.incompatiblePlugins.get("TerraformGenerator") || ServerBootSFG.incompatiblePlugins.get("Stratos")) { - Iris.safeguard(C.YELLOW + "Terraform Generator / Stratos"); - Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins."); - } - if (ServerBootSFG.unsuportedversion) { - Iris.safeguard(C.RED + "Server Version"); - Iris.safeguard(C.RED + "- Iris only supports 1.19.2 > 1.20.2"); - } - if (!ServerBootSFG.passedserversoftware) { - Iris.safeguard(C.RED + "Unsupported Server Software"); - Iris.safeguard(C.RED + "- Please consider using Paper or Purpur instead."); - - } - } - } - - public static String MSGIncompatibleWarnings() { - return ServerBootSFG.allIncompatiblePlugins; - } - - - public static void unstablePrompt() { - if (IrisSafeguard.unstablemode) { - Iris.info(""); - Iris.info(C.DARK_GRAY + "--==<" + C.RED + " IMPORTANT " + C.DARK_GRAY + ">==--"); - Iris.info(C.RED + "Iris is running in unstable mode what may cause the following issues."); - Iris.info(C.DARK_RED + "Server Issues"); - Iris.info(C.RED + "- Server wont boot"); - Iris.info(C.RED + "- Data Loss"); - Iris.info(C.RED + "- Unexpected behavior."); - Iris.info(C.RED + "- And More.."); - Iris.info(C.DARK_RED + "World Issues"); - Iris.info(C.RED + "- Worlds cant load due to corruption.."); - Iris.info(C.RED + "- Worlds may slowly corrupt till they wont be able to load."); - Iris.info(C.RED + "- World data loss."); - Iris.info(C.RED + "- And More.."); - Iris.info(C.DARK_RED + "ATTENTION:" + C.RED + " While running iris in unstable mode you wont be eligible for support."); - Iris.info(C.DARK_RED + "CAUSE: " + C.RED + MSGIncompatibleWarnings()); - Iris.info(""); - if (IrisSettings.get().getGeneral().bootUnstable) { - Iris.info(C.DARK_RED + "Boot Unstable is set to true, continuing with the startup process."); - } - - if (!IrisSettings.get().getGeneral().isBootUnstable()) { - Iris.info(C.DARK_RED + "Go to plugins/iris/settings.json and set ignoreUnstable to true if you wish to proceed."); - while (true) { - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // No - } + public static void splash() { + ModesSFG.selectMode(); + } + + public static void printIncompatibleWarnings() { + // String SupportedIrisVersion = getDescription().getVersion(); //todo Automatic version + + if (ServerBootSFG.safeguardPassed) { + Iris.safeguard(C.BLUE + "0 Conflicts found"); + } else { + if (IrisSafeguard.unstablemode) { + Iris.safeguard(C.DARK_RED + "" + ServerBootSFG.count + " Conflicts found"); + } + if (IrisSafeguard.warningmode) { + Iris.safeguard(C.YELLOW + "" + ServerBootSFG.count + " Conflicts found"); + } + + if (ServerBootSFG.incompatibilities.get("Multiverse-Core")) { + Iris.safeguard(C.RED + "Multiverse"); + Iris.safeguard(C.RED + "- The plugin Multiverse is not compatible with the server."); + Iris.safeguard(C.RED + "- If you want to have a world manager, consider using PhantomWorlds or MyWorlds instead."); + } + if (ServerBootSFG.incompatibilities.get("Dynmap")) { + Iris.safeguard(C.RED + "Dynmap"); + Iris.safeguard(C.RED + "- The plugin Dynmap is not compatible with the server."); + Iris.safeguard(C.RED + "- If you want to have a map plugin like Dynmap, consider Bluemap."); + } + if (ServerBootSFG.incompatibilities.get("TerraformGenerator") || ServerBootSFG.incompatibilities.get("Stratos")) { + Iris.safeguard(C.YELLOW + "Terraform Generator / Stratos"); + Iris.safeguard(C.YELLOW + "- Iris is not compatible with other worldgen plugins."); + } + if (ServerBootSFG.unsuportedversion) { + Iris.safeguard(C.RED + "Server Version"); + Iris.safeguard(C.RED + "- Iris only supports 1.19.2 > 1.20.2"); + } + if (!ServerBootSFG.passedserversoftware) { + Iris.safeguard(C.RED + "Unsupported Server Software"); + Iris.safeguard(C.RED + "- Please consider using Paper or Purpur instead."); + } + if (!ServerBootSFG.hasPrivileges) { + Iris.safeguard(C.RED + "Insufficient Privileges"); + Iris.safeguard(C.RED + "- The server has insufficient Privileges to run iris. Please contact support."); + } + if (!ServerBootSFG.hasEnoughDiskSpace) { + Iris.safeguard(C.RED + "Insufficient Disk Space"); + Iris.safeguard(C.RED + "- The server has insufficient Free DiskSpace to run iris required 3GB+."); + } + if (!ServerBootSFG.isJDK17) { + Iris.safeguard(C.YELLOW + "Unsupported java version"); + Iris.safeguard(C.YELLOW + "- Please consider using JDK 17 Instead of JDK " + Iris.getJavaVersion()); + } + if (!ServerBootSFG.isJRE) { + Iris.safeguard(C.YELLOW + "Unsupported Server JDK"); + Iris.safeguard(C.YELLOW + "- Please consider using JDK 17 Instead of JRE " + Iris.getJavaVersion()); + } + } + } + + public static String MSGIncompatibleWarnings() { + return ServerBootSFG.allIncompatibilities; } - } - Iris.info(""); - } - } } diff --git a/core/src/main/java/com/volmit/iris/core/service/HotDropWorldSVC.java b/core/src/main/java/com/volmit/iris/core/service/HotDropWorldSVC.java deleted file mode 100644 index 88bab482b..000000000 --- a/core/src/main/java/com/volmit/iris/core/service/HotDropWorldSVC.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.volmit.iris.core.service; - -import java.nio.file.*; -import static java.nio.file.StandardWatchEventKinds.*; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonParseException; -import com.volmit.iris.Iris; -import com.volmit.iris.util.SFG.WorldHandlerSFG; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.plugin.IrisService; -import com.volmit.iris.util.scheduling.Looper; -import org.bukkit.Bukkit; -import org.bukkit.plugin.java.JavaPlugin; - -public class HotDropWorldSVC extends Looper implements IrisService { - private WatchService watchService; - private JavaPlugin plugin; - - @Override - public void onEnable() { - this.plugin = Iris.instance; // Assuming Iris.instance is your plugin instance - initializeWatchService(); - - } - - @Override - public void onDisable() { - - } - - private void initializeWatchService() { - try { - this.watchService = FileSystems.getDefault().newWatchService(); - Path path = Paths.get(Bukkit.getWorldContainer().getAbsolutePath()); - path.register(watchService, ENTRY_CREATE); - } catch (Exception e) { - Iris.reportError(e); - e.printStackTrace(); - } - } - - @Override - protected long loop() { - WatchKey key; - try { - key = watchService.poll(); - if (key != null) { - for (WatchEvent event : key.pollEvents()) { - WatchEvent.Kind kind = event.kind(); - - if (kind == ENTRY_CREATE) { - WatchEvent ev = (WatchEvent) event; - Path filename = ev.context(); - - File newDir = new File(Bukkit.getWorldContainer(), filename.toString()); - File irisFolder = new File(newDir, "iris"); - if (irisFolder.exists() && irisFolder.isDirectory()) { - Iris.info("World HotDrop Detected!"); - String worldName = newDir.getName(); - String version = getVersionFromIrisFolder(irisFolder); - - if (Bukkit.getWorld(worldName) == null && isPackValid(worldName, version)) { - Bukkit.getScheduler().runTask(this.plugin, () -> WorldHandlerSFG.LoadWorld(worldName)); - } - } - } - } - key.reset(); - } - } catch (Throwable e) { - Iris.reportError(e); - e.printStackTrace(); - return -1; - } - - return 1000; - } - - private String getVersionFromIrisFolder(File irisFolder) { - File versionFile = new File(irisFolder, "some_version_file.json"); - - if (versionFile.exists() && versionFile.isFile()) { - try (FileReader reader = new FileReader(versionFile)) { - JsonObject jsonObject = JsonParser.parseReader(reader).getAsJsonObject(); - if (jsonObject.has("version")) { - return jsonObject.get("version").getAsString(); - } - } catch (IOException | JsonParseException e) { - Iris.reportError(e); - e.printStackTrace(); - } - } - - return "???"; - } - - private boolean isPackValid(String worldPackName, String version) { - try { - File packFolder = Iris.service(StudioSVC.class).getWorkspaceFolder(); - File[] serverPacks = packFolder.listFiles(File::isDirectory); - if (serverPacks != null) { - for (File serverPack : serverPacks) { - String serverPackName = serverPack.getName(); - String serverPackVersion = getPackVersion(serverPack); - - if (serverPackName.equals(worldPackName)) { - if (serverPackVersion.equals(version)) { - return true; - } else { - Iris.info("Version mismatch for pack '" + worldPackName + "'. Expected: " + serverPackVersion + ", Found: " + version); - Iris.info(C.GOLD + "Cant load the world!"); - return false; - } - } - } - Iris.info("Pack '" + worldPackName + "' not found on the server."); - Iris.info(C.GOLD + "Cant load the world!"); - } else { - Iris.info("No packs found in the server's workspace folder."); - } - } catch (Exception e) { - Iris.reportError(e); - e.printStackTrace(); - Iris.info("Error checking if pack is valid: " + e.getMessage()); - } - return false; - } - - private String getPackVersion(File pack) { - String version = "???"; - File dimensionFile = new File(pack, "dimensions/" + pack.getName() + ".json"); - if (dimensionFile.isFile()) { - try (FileReader reader = new FileReader(dimensionFile)) { - JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); - if (json.has("version")) { - version = json.get("version").getAsString(); - } - } catch (IOException | JsonParseException e) { - Iris.reportError(e); - e.printStackTrace(); - } - } - return version; - } -} - diff --git a/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java new file mode 100644 index 000000000..644d887e0 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/service/IrisEngineSVC.java @@ -0,0 +1,163 @@ +package com.volmit.iris.core.service; + +import com.volmit.iris.Iris; +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.KMap; +import com.volmit.iris.util.format.C; +import com.volmit.iris.util.format.Form; +import com.volmit.iris.util.misc.getHardware; +import com.volmit.iris.util.plugin.IrisService; +import com.volmit.iris.util.scheduling.Looper; +import org.bukkit.Bukkit; +import org.bukkit.World; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Supplier; + +public class IrisEngineSVC implements IrisService { + private static final AtomicInteger tectonicLimit = new AtomicInteger(30); + private final ReentrantLock lastUseLock = new ReentrantLock(); + private final KMap lastUse = new KMap<>(); + private Looper cacheTicker; + private Looper trimTicker; + private Looper unloadTicker; + public List corruptedIrisWorlds = new ArrayList<>(); + + // todo make this work with multiple worlds + + @Override + public void onEnable() { + tectonicLimit.set(2); + long t = getHardware.getProcessMemory(); + while (t > 250) { + tectonicLimit.getAndAdd(1); + t = t - 250; + } + tectonicLimit.set(10); // DEBUG CODE + this.setup(); + cacheTicker.start(); + trimTicker.start(); + unloadTicker.start(); + } + + public static int getTectonicLimit() { + return tectonicLimit.get(); + } + + private void setup() { + cacheTicker = new Looper() { + @Override + protected long loop() { + long now = System.currentTimeMillis(); + lastUseLock.lock(); + try { + for (Engine key : new ArrayList<>(lastUse.keySet())) { + Long last = lastUse.get(key); + if (last == null) + continue; + if (now - last > 60000) { // 1 minute + lastUse.remove(key); + } + } + } finally { + lastUseLock.unlock(); + } + return 1000; + } + }; + trimTicker = new Looper() { + private final Supplier supplier = createSupplier(); + @Override + protected long loop() { + long start = System.currentTimeMillis(); + try { + Engine engine = supplier.get(); + if (engine != null) { + engine.getMantle().trim(tectonicLimit.get() / lastUse.size()); + } + } catch (Throwable e) { + Iris.reportError(e); + return -1; + } + + int size = lastUse.size(); + long time = (size > 0 ? 1000/size : 1000) - (System.currentTimeMillis() - start); + if (time <= 0) + return 0; + return time; + } + }; + + unloadTicker = new Looper() { + private final Supplier supplier = createSupplier(); + + @Override + protected long loop() { + long start = System.currentTimeMillis(); + try { + Engine engine = supplier.get(); + if (engine != null) { + long unloadStart = System.currentTimeMillis(); + int count = engine.getMantle().unloadTectonicPlate(); + if (count > 0) { + Iris.info(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2)); + } + } + } catch (Throwable e) { + Iris.reportError(e); + return -1; + } + + int size = lastUse.size(); + long time = (size > 0 ? 1000/size : 1000) - (System.currentTimeMillis() - start); + if (time <= 0) + return 0; + return time; + } + }; + } + + private Supplier createSupplier() { + AtomicInteger i = new AtomicInteger(); + return () -> { + List worlds = Bukkit.getWorlds(); + if (i.get() >= worlds.size()) { + i.set(0); + } + try { + for (int j = 0; j < worlds.size(); j++) { + PlatformChunkGenerator generator = IrisToolbelt.access(worlds.get(i.getAndIncrement())); + if (i.get() >= worlds.size()) { + i.set(0); + } + + if (generator != null) { + Engine engine = generator.getEngine(); + if (engine != null) { + lastUseLock.lock(); + lastUse.put(engine, System.currentTimeMillis()); + lastUseLock.unlock(); + return engine; + } + } + } + } catch (Throwable e) { + Iris.reportError(e); + } + return null; + }; + } + + @Override + public void onDisable() { + cacheTicker.interrupt(); + trimTicker.interrupt(); + unloadTicker.interrupt(); + lastUse.clear(); + } +} diff --git a/core/src/main/java/com/volmit/iris/core/service/WorldLoadSFG.java b/core/src/main/java/com/volmit/iris/core/service/WorldLoadSFG.java new file mode 100644 index 000000000..845edf054 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/service/WorldLoadSFG.java @@ -0,0 +1,31 @@ +package com.volmit.iris.core.service; + +import com.volmit.iris.Iris; +import com.volmit.iris.util.plugin.IrisService; +import org.bukkit.World; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.plugin.java.JavaPlugin; + +import static java.lang.System.getLogger; + +public class WorldLoadSFG implements IrisService { + private JavaPlugin plugin; + @EventHandler + public void onWorldLoad(WorldLoadEvent event) { + World world = event.getWorld(); + + } + + @Override + public void onEnable() { + this.plugin = Iris.instance; + plugin.getServer().getPluginManager().registerEvents(this, plugin); + } + + @Override + public void onDisable() { + + } +} diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java b/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java index f9cb549c0..7e6285d07 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisCreator.java @@ -24,6 +24,7 @@ import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.ServerConfigurator; import com.volmit.iris.core.pregenerator.PregenTask; import com.volmit.iris.core.service.StudioSVC; +import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.platform.PlatformChunkGenerator; import com.volmit.iris.core.safeguard.UtilsSFG; @@ -45,6 +46,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static com.volmit.iris.core.tools.IrisPackBenchmarking.benchmark; import static com.volmit.iris.core.safeguard.IrisSafeguard.unstablemode; @@ -173,7 +176,6 @@ public class IrisCreator { } } } - //if (benchmark){loaded = true;} }); diff --git a/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java b/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java index 14d9a732f..cd6c17a91 100644 --- a/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java +++ b/core/src/main/java/com/volmit/iris/core/tools/IrisWorldCreator.java @@ -19,8 +19,7 @@ package com.volmit.iris.core.tools; import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.engine.object.IrisWorld; +import com.volmit.iris.engine.object.*; import com.volmit.iris.engine.platform.BukkitChunkGenerator; import org.bukkit.Bukkit; import org.bukkit.World; @@ -79,6 +78,7 @@ public class IrisWorldCreator { ? dim.getLoader().getDataFolder() : new File(w.worldFolder(), "iris/pack"), dimensionName); + return new WorldCreator(name) .environment(findEnvironment()) .generateStructures(true) diff --git a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java index 989aa0ef9..db92954a8 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisComplex.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisComplex.java @@ -35,6 +35,8 @@ import com.volmit.iris.util.noise.CNG; import com.volmit.iris.util.stream.ProceduralStream; import com.volmit.iris.util.stream.interpolation.Interpolated; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -42,6 +44,8 @@ import org.bukkit.block.data.BlockData; import java.util.UUID; @Data +@EqualsAndHashCode(exclude = "data") +@ToString(exclude = "data") public class IrisComplex implements DataProvider { private static final BlockData AIR = Material.AIR.createBlockData(); private RNG rng; diff --git a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java index ff9d712c3..1f70e8d54 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngine.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngine.java @@ -50,6 +50,9 @@ import com.volmit.iris.util.scheduling.ChronoLatch; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.data.BlockData; @@ -62,8 +65,12 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Data +@EqualsAndHashCode(exclude = "context") +@ToString(exclude = "context") public class IrisEngine implements Engine { private final AtomicInteger bud; private final AtomicInteger buds; @@ -250,7 +257,10 @@ public class IrisEngine implements Engine { if (!f.exists()) { try { f.getParentFile().mkdirs(); - IO.writeAll(f, new Gson().toJson(new IrisEngineData())); + IrisEngineData data = new IrisEngineData(); + data.getStatistics().setVersion(getIrisVersion()); + data.getStatistics().setMCVersion(getMCVersion()); + IO.writeAll(f, new Gson().toJson(data)); } catch (IOException e) { e.printStackTrace(); } @@ -418,7 +428,6 @@ public class IrisEngine implements Engine { J.a(() -> { try { - getMantle().trim(); getData().getObjectLoader().clean(); } catch (Throwable e) { Iris.reportError(e); @@ -518,4 +527,37 @@ public class IrisEngine implements Engine { public int getCacheID() { return cacheId; } + + private int getIrisVersion() { + String input = Iris.instance.getDescription().getVersion(); + int hyphenIndex = input.indexOf('-'); + if (hyphenIndex != -1) { + String result = input.substring(0, hyphenIndex); + result = result.replaceAll("\\.", ""); + return Integer.parseInt(result); + } + Iris.error("Failed to assign a Iris Version"); + return 0; + } + + private int getMCVersion() { + try { + String version = Bukkit.getVersion(); + Matcher matcher = Pattern.compile("\\(MC: ([\\d.]+)\\)").matcher(version); + if (matcher.find()) { + version = matcher.group(1).replaceAll("\\.", ""); + long versionNumber = Long.parseLong(version); + if (versionNumber > Integer.MAX_VALUE) { + return -1; + } + return (int) versionNumber; + } + Iris.error("Failed to assign a Minecraft Version"); + return -1; + } catch (Exception e) { + Iris.error("Failed to assign a Minecraft Version"); + return -1; + } + } + } diff --git a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java index 3a2cb9cdd..f61d90531 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisEngineMantle.java @@ -38,6 +38,8 @@ import com.volmit.iris.util.format.Form; import com.volmit.iris.util.mantle.Mantle; import com.volmit.iris.util.parallel.BurstExecutor; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import org.bukkit.util.BlockVector; import java.io.File; @@ -49,6 +51,8 @@ import static com.volmit.iris.core.tools.IrisPackBenchmarking.benchmark; import static com.volmit.iris.core.safeguard.PerformanceSFG.*; @Data +@EqualsAndHashCode(exclude = "engine") +@ToString(exclude = "engine") public class IrisEngineMantle implements EngineMantle { private final Engine engine; private final Mantle mantle; diff --git a/core/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java b/core/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java index 4420600a4..51ca6d1f9 100644 --- a/core/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java +++ b/core/src/main/java/com/volmit/iris/engine/IrisExecutionEnvironment.java @@ -25,11 +25,15 @@ import com.volmit.iris.engine.scripting.EngineExecutionEnvironment; import com.volmit.iris.engine.scripting.IrisScriptingAPI; import com.volmit.iris.util.format.C; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import org.apache.bsf.BSFException; import org.apache.bsf.BSFManager; import org.apache.bsf.engines.javascript.JavaScriptEngine; @Data +@EqualsAndHashCode(exclude = "engine") +@ToString(exclude = "engine") public class IrisExecutionEnvironment implements EngineExecutionEnvironment { private final BSFManager manager; private final Engine engine; diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java index 11249b1f6..4d57cdf83 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Engine.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Engine.java @@ -523,8 +523,9 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat return getTarget().getBurster(); } + @Deprecated default void clean() { - burst().lazy(() -> getMantle().trim()); + burst().lazy(() -> getMantle().trim(10)); } @BlockCoordinates @@ -803,7 +804,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat return getBiomeOrMantle(l.getBlockX(), l.getBlockY(), l.getBlockZ()); } - default void gotoBiome(IrisBiome biome, Player player) { + default void gotoBiome(IrisBiome biome, Player player, boolean teleport) { Set regionKeys = getDimension() .getAllRegions(this).stream() .filter((i) -> i.getAllBiomes(this).contains(biome)) @@ -815,13 +816,13 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat && lb.matches(engine, chunk); if (!regionKeys.isEmpty()) { - locator.find(player); + locator.find(player, teleport, "Biome " + biome.getName()); } else { player.sendMessage(C.RED + biome.getName() + " is not in any defined regions!"); } } - default void gotoJigsaw(IrisJigsawStructure s, Player player) { + default void gotoJigsaw(IrisJigsawStructure s, Player player, boolean teleport) { if (s.getLoadKey().equals(getDimension().getStronghold())) { KList p = getDimension().getStrongholds(getSeedManager().getSpawn()); @@ -858,7 +859,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat if (getDimension().getJigsawStructures().stream() .map(IrisJigsawStructurePlacement::getStructure) .collect(Collectors.toSet()).contains(s.getLoadKey())) { - Locator.jigsawStructure(s.getLoadKey()).find(player); + Locator.jigsawStructure(s.getLoadKey()).find(player, teleport, "Structure " + s.getLoadKey()); } else { Set biomeKeys = getDimension().getAllBiomes(this).stream() .filter((i) -> i.getJigsawStructures() @@ -885,7 +886,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat }; if (!regionKeys.isEmpty()) { - locator.find(player); + locator.find(player, teleport, "Structure " + s.getLoadKey()); } else { player.sendMessage(C.RED + s.getLoadKey() + " is not in any defined regions, biomes or dimensions!"); } @@ -893,7 +894,7 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat } - default void gotoObject(String s, Player player) { + default void gotoObject(String s, Player player, boolean teleport) { Set biomeKeys = getDimension().getAllBiomes(this).stream() .filter((i) -> i.getObjects().stream().anyMatch((f) -> f.getPlace().contains(s))) .map(IrisRegistrant::getLoadKey) @@ -916,23 +917,23 @@ public interface Engine extends DataProvider, Fallible, LootProvider, BlockUpdat }; if (!regionKeys.isEmpty()) { - locator.find(player); + locator.find(player, teleport, "Object " + s); } else { player.sendMessage(C.RED + s + " is not in any defined regions or biomes!"); } } - default void gotoRegion(IrisRegion r, Player player) { + default void gotoRegion(IrisRegion r, Player player, boolean teleport) { if (!getDimension().getAllRegions(this).contains(r)) { player.sendMessage(C.RED + r.getName() + " is not defined in the dimension!"); return; } - Locator.region(r.getLoadKey()).find(player); + Locator.region(r.getLoadKey()).find(player, teleport, "Region " + r.getName()); } - default void gotoPOI(String type, Player p) { - Locator.poi(type).find(p); + default void gotoPOI(String type, Player p, boolean teleport) { + Locator.poi(type).find(p, teleport, "POI " + type); } default void cleanupMantleChunk(int x, int z) { diff --git a/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedComponent.java b/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedComponent.java index a9e83e95c..b356eb722 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/EngineAssignedComponent.java @@ -21,8 +21,12 @@ package com.volmit.iris.engine.framework; import com.volmit.iris.Iris; import com.volmit.iris.util.math.RollingSequence; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; @Data +@EqualsAndHashCode(exclude = "engine") +@ToString(exclude = "engine") public class EngineAssignedComponent implements EngineComponent { private final Engine engine; private final RollingSequence metrics; diff --git a/core/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java b/core/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java index cd33cf998..7e9388ffb 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/EngineTarget.java @@ -23,8 +23,12 @@ import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisWorld; import com.volmit.iris.util.parallel.MultiBurst; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; @Data +@EqualsAndHashCode(exclude = "data") +@ToString(exclude = "data") public class EngineTarget { private final MultiBurst burster; private final IrisData data; diff --git a/core/src/main/java/com/volmit/iris/engine/framework/Locator.java b/core/src/main/java/com/volmit/iris/engine/framework/Locator.java index 8dc597605..0c05c068b 100644 --- a/core/src/main/java/com/volmit/iris/engine/framework/Locator.java +++ b/core/src/main/java/com/volmit/iris/engine/framework/Locator.java @@ -27,6 +27,7 @@ import com.volmit.iris.engine.object.IrisJigsawStructure; import com.volmit.iris.engine.object.IrisObject; import com.volmit.iris.engine.object.IrisRegion; import com.volmit.iris.util.context.ChunkContext; +import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.math.M; import com.volmit.iris.util.math.Position2; @@ -39,6 +40,7 @@ import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import com.volmit.iris.util.scheduling.jobs.SingleJob; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.entity.Player; import java.util.Set; @@ -109,23 +111,35 @@ public interface Locator { boolean matches(Engine engine, Position2 chunk); - default void find(Player player) { - find(player, 30_000); + default void find(Player player, boolean teleport, String message) { + find(player, location -> { + if (teleport) { + J.s(() -> player.teleport(location)); + } else { + player.sendMessage(C.GREEN + message + " at: " + location.getBlockX() + " " + location.getBlockY() + " " + location.getBlockZ()); + } + }); } - default void find(Player player, long timeout) { + default void find(Player player, Consumer consumer) { + find(player, 30_000, consumer); + } + + default void find(Player player, long timeout, Consumer consumer) { AtomicLong checks = new AtomicLong(); long ms = M.ms(); new SingleJob("Searching", () -> { try { - Position2 at = find(IrisToolbelt.access(player.getWorld()).getEngine(), new Position2(player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4), timeout, checks::set).get(); + World world = player.getWorld(); + Engine engine = IrisToolbelt.access(world).getEngine(); + Position2 at = find(engine, new Position2(player.getLocation().getBlockX() >> 4, player.getLocation().getBlockZ() >> 4), timeout, checks::set).get(); if (at != null) { - J.s(() -> player.teleport(new Location(player.getWorld(), (at.getX() << 4) + 8, - IrisToolbelt.access(player.getWorld()).getEngine().getHeight( + consumer.accept(new Location(world, (at.getX() << 4) + 8, + engine.getHeight( (at.getX() << 4) + 8, (at.getZ() << 4) + 8, false), - (at.getZ() << 4) + 8))); + (at.getZ() << 4) + 8)); } } catch (WrongEngineBroException | InterruptedException | ExecutionException e) { e.printStackTrace(); diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java index bef1cddcf..fe17b0655 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/EngineMantle.java @@ -143,8 +143,8 @@ public interface EngineMantle extends IObjectPlacer { return getEngine().getDimension().isDebugSmartBore(); } - default void trim(long dur) { - getMantle().trim(dur); + default void trim(long dur, int limit) { + getMantle().trim(dur, limit); } default IrisData getData() { @@ -175,8 +175,11 @@ public interface EngineMantle extends IObjectPlacer { } - default void trim() { - getMantle().trim(TimeUnit.SECONDS.toMillis(IrisSettings.get().getPerformance().getMantleKeepAlive())); + default void trim(int limit) { + getMantle().trim(TimeUnit.SECONDS.toMillis(IrisSettings.get().getPerformance().getMantleKeepAlive()), limit); + } + default int unloadTectonicPlate(){ + return getMantle().unloadTectonicPlate(); } default MultiBurst burst() { @@ -293,12 +296,15 @@ public interface EngineMantle extends IObjectPlacer { } default long getToUnload(){ - return getMantle().FakeToUnload.get(); + return getMantle().getToUnload().size(); } - default double getTectonicLimit(){ - return getMantle().tectonicLimit.get(); + default long getDynamicThreads(){ + return getMantle().getDynamicThreads().get(); + } + default long getNotClearedLoadedRegions(){ + return getMantle().getLoadedRegions().size() - getMantle().getToUnload().size(); } default double getTectonicDuration(){ - return getMantle().adjustedIdleDuration.get(); + return getMantle().getAdjustedIdleDuration().get(); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java b/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java index 16c71652d..48e33957a 100644 --- a/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java +++ b/core/src/main/java/com/volmit/iris/engine/mantle/IrisMantleComponent.java @@ -20,8 +20,12 @@ package com.volmit.iris.engine.mantle; import com.volmit.iris.util.mantle.MantleFlag; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; @Data +@EqualsAndHashCode(exclude = "engineMantle") +@ToString(exclude = "engineMantle") public abstract class IrisMantleComponent implements MantleComponent { private final EngineMantle engineMantle; private final MantleFlag flag; diff --git a/core/src/main/java/com/volmit/iris/engine/object/IrisEngineStatistics.java b/core/src/main/java/com/volmit/iris/engine/object/IrisEngineStatistics.java index f6a723cd5..552f27466 100644 --- a/core/src/main/java/com/volmit/iris/engine/object/IrisEngineStatistics.java +++ b/core/src/main/java/com/volmit/iris/engine/object/IrisEngineStatistics.java @@ -18,17 +18,41 @@ package com.volmit.iris.engine.object; +import com.volmit.iris.Iris; import lombok.Data; +import org.bukkit.Bukkit; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; @Data public class IrisEngineStatistics { private int totalHotloads = 0; private int chunksGenerated = 0; + private int IrisCreationVersion = 0; + private int MinecraftVersion = 0; public void generatedChunk() { chunksGenerated++; } + public void setVersion(int i) { + IrisCreationVersion = i; + } + + public void setMCVersion(int i) { + MinecraftVersion = i; + } + public int getMCVersion() { + return MinecraftVersion; + } + public int getVersion() { + return MinecraftVersion; + } + + + + public void hotloaded() { totalHotloads++; } diff --git a/core/src/main/java/com/volmit/iris/engine/scripting/IrisScriptingAPI.java b/core/src/main/java/com/volmit/iris/engine/scripting/IrisScriptingAPI.java index c0a3e35d9..ae613c48f 100644 --- a/core/src/main/java/com/volmit/iris/engine/scripting/IrisScriptingAPI.java +++ b/core/src/main/java/com/volmit/iris/engine/scripting/IrisScriptingAPI.java @@ -27,10 +27,14 @@ import com.volmit.iris.engine.object.IrisBiome; import com.volmit.iris.engine.object.IrisDimension; import com.volmit.iris.engine.object.IrisExpression; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import org.bukkit.Location; import org.bukkit.entity.Entity; @Data +@EqualsAndHashCode(exclude = "engine") +@ToString(exclude = "engine") public class IrisScriptingAPI { private final Engine engine; private IrisRegistrant preprocessorObject; diff --git a/core/src/main/java/com/volmit/iris/util/SFG/WorldHandlerSFG.java b/core/src/main/java/com/volmit/iris/util/SFG/WorldHandlerSFG.java deleted file mode 100644 index 8ff552d27..000000000 --- a/core/src/main/java/com/volmit/iris/util/SFG/WorldHandlerSFG.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.volmit.iris.util.SFG; - -import com.volmit.iris.Iris; -import com.volmit.iris.core.IrisSettings; -import com.volmit.iris.core.loader.IrisData; -import com.volmit.iris.core.service.StudioSVC; -import com.volmit.iris.engine.object.IrisDimension; -import com.volmit.iris.engine.object.IrisWorld; -import com.volmit.iris.engine.platform.BukkitChunkGenerator; -import com.volmit.iris.engine.platform.DummyChunkGenerator; -import com.volmit.iris.util.format.C; -import com.volmit.iris.util.plugin.VolmitSender; -import org.bukkit.Bukkit; -import org.bukkit.World; -import org.bukkit.WorldCreator; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.generator.ChunkGenerator; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Objects; - -import static com.volmit.iris.Iris.service; - -public class WorldHandlerSFG { - static String WorldToLoad; - static String WorldEngine; - static String worldNameToCheck = "YourWorldName"; - private static VolmitSender sender; - public static void LoadWorld(String selectedWorld){ - if(Objects.equals(selectedWorld, "Benchmark")){ - return; - } - worldNameToCheck = selectedWorld; - boolean worldExists = doesWorldExist(worldNameToCheck); - WorldEngine = selectedWorld; - - if (!worldExists) { - return; - } - WorldToLoad = selectedWorld; - File BUKKIT_YML = new File("bukkit.yml"); - String pathtodim = selectedWorld + "\\iris\\pack\\dimensions\\"; - File directory = new File(Bukkit.getWorldContainer(), pathtodim); - - String dimension = null; - if (directory.exists() && directory.isDirectory()) { - File[] files = directory.listFiles(); - if (files != null) { - for (File file : files) { - if (file.isFile()) { - String fileName = file.getName(); - if (fileName.endsWith(".json")) { - dimension = fileName.substring(0, fileName.length() - 5); - } - } - } - } - } else { - return; - } - - YamlConfiguration yml = YamlConfiguration.loadConfiguration(BUKKIT_YML); - String gen = "Iris:" + dimension; - ConfigurationSection section = yml.contains("worlds") ? yml.getConfigurationSection("worlds") : yml.createSection("worlds"); - if (!section.contains(selectedWorld)) { - section.createSection(selectedWorld).set("generator", gen); - try { - yml.save(BUKKIT_YML); - Iris.info("Registered \"" + selectedWorld + "\" in bukkit.yml"); - } catch (IOException e) { - Iris.error("Failed to update bukkit.yml!"); - e.printStackTrace(); - } - } - checkForBukkitWorlds(); - } - static boolean doesWorldExist(String worldName) { - File worldContainer = Bukkit.getWorldContainer(); - File worldDirectory = new File(worldContainer, worldName); - return worldDirectory.exists() && worldDirectory.isDirectory(); - } - private static void checkForBukkitWorlds() { - FileConfiguration fc = new YamlConfiguration(); - try { - fc.load(new File("bukkit.yml")); - ConfigurationSection section = fc.getConfigurationSection("worlds"); - if (section == null) { - return; - } - - List worldsToLoad = Collections.singletonList(WorldToLoad); - - for (String s : section.getKeys(false)) { - if (!worldsToLoad.contains(s)) { - continue; - } - ConfigurationSection entry = section.getConfigurationSection(s); - if (!entry.contains("generator", true)) { - continue; - } - String generator = entry.getString("generator"); - if (generator.startsWith("Iris:")) { - generator = generator.split("\\Q:\\E")[1]; - } else if (generator.equalsIgnoreCase("Iris")) { - generator = IrisSettings.get().getGenerator().getDefaultWorldType(); - } else { - continue; - } - Iris.info("2 World: %s | Generator: %s", s, generator); - if (Bukkit.getWorlds().stream().anyMatch(w -> w.getName().equals(s))) { - continue; - } - Iris.info(C.LIGHT_PURPLE + "Preparing Spawn for " + s + "' using Iris:" + generator + "..."); - new WorldCreator(s) - .generator(getDefaultWorldGenerator(s, generator)) - .environment(IrisData.loadAnyDimension(generator).getEnvironment()) - .createWorld(); - Iris.info(C.LIGHT_PURPLE + "Loaded " + s + "!"); - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - public static ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { - Iris.debug("Default World Generator Called for " + worldName + " using ID: " + id); - if (worldName.equals("test")) { - try { - throw new RuntimeException(); - } catch (Throwable e) { - Iris.info(e.getStackTrace()[1].getClassName()); - if (e.getStackTrace()[1].getClassName().contains("com.onarandombox.MultiverseCore")) { - Iris.debug("MVC Test detected, Quick! Send them the dummy!"); - return new DummyChunkGenerator(); - } - } - } - IrisDimension dim; - if (id == null || id.isEmpty()) { - dim = IrisData.loadAnyDimension(IrisSettings.get().getGenerator().getDefaultWorldType()); - } else { - dim = IrisData.loadAnyDimension(id); - } - Iris.debug("Generator ID: " + id + " requested by bukkit/plugin"); - - if (dim == null) { - Iris.warn("Unable to find dimension type " + id + " Looking for online packs..."); - - service(StudioSVC.class).downloadSearch(new VolmitSender(Bukkit.getConsoleSender()), id, true); - dim = IrisData.loadAnyDimension(id); - - if (dim == null) { - throw new RuntimeException("Can't find dimension " + id + "!"); - } else { - Iris.info("Resolved missing dimension, proceeding with generation."); - } - } - Iris.debug("Assuming IrisDimension: " + dim.getName()); - IrisWorld w = IrisWorld.builder() - .name(worldName) - .seed(1337) - .environment(dim.getEnvironment()) - .worldFolder(new File(Bukkit.getWorldContainer(), worldName)) - .minHeight(dim.getMinHeight()) - .maxHeight(dim.getMaxHeight()) - .build(); - Iris.debug("Generator Config: " + w.toString()); - File ff = new File(w.worldFolder(), "iris/pack"); - if (!ff.exists() || ff.listFiles().length == 0) { - ff.mkdirs(); - service(StudioSVC.class).installIntoWorld(sender, dim.getLoadKey(), ff.getParentFile()); - } - return new BukkitChunkGenerator(w, false, ff, dim.getLoadKey()); - } -} diff --git a/core/src/main/java/com/volmit/iris/util/format/Form.java b/core/src/main/java/com/volmit/iris/util/format/Form.java index a38867767..bb2e7ec27 100644 --- a/core/src/main/java/com/volmit/iris/util/format/Form.java +++ b/core/src/main/java/com/volmit/iris/util/format/Form.java @@ -683,23 +683,23 @@ public class Form { */ public static String ofSize(long s, int div) { double d = (double) s; - String sub = "Bytes"; + String sub = " Bytes"; if (d > div - 1) { d /= div; - sub = "KB"; + sub = " KB"; if (d > div - 1) { d /= div; - sub = "MB"; + sub = " MB"; if (d > div - 1) { d /= div; - sub = "GB"; + sub = " GB"; if (d > div - 1) { d /= div; - sub = "TB"; + sub = " TB"; } } } diff --git a/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java b/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java index 2afc2c696..c35c02acc 100644 --- a/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java +++ b/core/src/main/java/com/volmit/iris/util/hunk/bits/HashPalette.java @@ -23,8 +23,10 @@ import com.volmit.iris.util.function.Consumer2; import java.util.LinkedHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; public class HashPalette implements Palette { + private final ReentrantLock lock = new ReentrantLock(); private final LinkedHashMap palette; private final KMap lookup; private final AtomicInteger size; @@ -47,14 +49,18 @@ public class HashPalette implements Palette { @Override public int add(T t) { - int index = size.getAndIncrement(); - palette.put(t, index); + lock.lock(); + try { + int index = size.getAndIncrement(); + palette.put(t, index); - if (t != null) { - lookup.put(index, t); + if (t != null) { + lookup.put(index, t); + } + return index; + } finally { + lock.unlock(); } - - return index; } @Override @@ -74,12 +80,17 @@ public class HashPalette implements Palette { @Override public void iterate(Consumer2 c) { - for (T i : palette.keySet()) { - if (i == null) { - continue; - } + lock.lock(); + try { + for (T i : palette.keySet()) { + if (i == null) { + continue; + } - c.accept(i, id(i)); + c.accept(i, id(i)); + } + } finally { + lock.unlock(); } } } diff --git a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java index 34d2263d7..d4c04a189 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/Mantle.java @@ -27,7 +27,6 @@ import com.volmit.iris.engine.framework.Engine; import com.volmit.iris.engine.mantle.EngineMantle; import com.volmit.iris.engine.mantle.MantleWriter; import com.volmit.iris.util.collection.KMap; -import com.volmit.iris.util.collection.KSet; import com.volmit.iris.util.documentation.BlockCoordinates; import com.volmit.iris.util.documentation.ChunkCoordinates; import com.volmit.iris.util.documentation.RegionCoordinates; @@ -37,25 +36,21 @@ import com.volmit.iris.util.function.Consumer4; import com.volmit.iris.util.math.M; import com.volmit.iris.util.matter.Matter; import com.volmit.iris.util.matter.MatterSlice; -import com.volmit.iris.util.misc.getHardware; import com.volmit.iris.util.parallel.BurstExecutor; import com.volmit.iris.util.parallel.HyperLock; import com.volmit.iris.util.parallel.MultiBurst; -import com.volmit.iris.util.scheduling.Looper; import lombok.Getter; import org.bukkit.Chunk; -import org.checkerframework.checker.units.qual.A; import java.io.EOFException; import java.io.File; import java.io.IOException; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.ReentrantLock; /** * The mantle can store any type of data slice anywhere and manage regions & IO on it's own. @@ -64,19 +59,16 @@ import java.util.concurrent.atomic.AtomicLong; public class Mantle { private final File dataFolder; + @Getter private final int worldHeight; private final Map lastUse; @Getter private final Map loadedRegions; private final HyperLock hyperLock; - private final KSet unload; private final AtomicBoolean closed; private final MultiBurst ioBurst; - private final AtomicBoolean io; - private final Object gcMonitor = new Object(); - long apm = getHardware.getAvailableProcessMemory(); - private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); - int tectonicLimitBeforeOutMemory; + private final AtomicBoolean ioTrim; + private final AtomicBoolean ioTectonicUnload; /** * Create a new mantle @@ -90,9 +82,9 @@ public class Mantle { this.closed = new AtomicBoolean(false); this.dataFolder = dataFolder; this.worldHeight = worldHeight; - this.io = new AtomicBoolean(false); + this.ioTrim = new AtomicBoolean(false); + this.ioTectonicUnload = new AtomicBoolean(false); dataFolder.mkdirs(); - unload = new KSet<>(); loadedRegions = new KMap<>(); lastUse = new KMap<>(); ioBurst = MultiBurst.burst; @@ -119,7 +111,7 @@ public class Mantle { * @return the file */ public static File fileForRegion(File folder, Long key) { - File f = new File(folder, "p." + key + ".ttp"); + File f = new File(folder, "p." + key + ".ttp.lz4b"); if (!f.getParentFile().exists()) { f.getParentFile().mkdirs(); } @@ -397,87 +389,104 @@ public class Mantle { return numberOfEntries * bytesPerEntry; } + @Getter + private final AtomicDouble adjustedIdleDuration = new AtomicDouble(0); + @Getter + private final AtomicInteger dynamicThreads = new AtomicInteger(1); + @Getter + private final AtomicInteger forceAggressiveThreshold = new AtomicInteger(30); + @Getter + private final AtomicLong oldestTectonicPlate = new AtomicLong(0); + private final ReentrantLock unloadLock = new ReentrantLock(); + @Getter + private final Set toUnload = new HashSet<>(); + /** * Save & unload regions that have not been used for more than the * specified amount of milliseconds * * @param baseIdleDuration the duration */ - - public AtomicInteger FakeToUnload = new AtomicInteger(0); - public AtomicDouble adjustedIdleDuration = new AtomicDouble(0); - public AtomicInteger tectonicLimit = new AtomicInteger(30); - - - public synchronized void trim(long baseIdleDuration) { + public synchronized void trim(long baseIdleDuration, int tectonicLimit) { if (closed.get()) { throw new RuntimeException("The Mantle is closed"); } - - if (IrisSettings.get().getPerformance().dynamicPerformanceMode){ - tectonicLimit.set(2); - long t = getHardware.getProcessMemory(); - for (; t > 250;){ - tectonicLimit.getAndAdd(1); - t = t - 250; - } - } + Iris.debug(C.BLUE + "TECTONIC TRIM HAS RUN"); adjustedIdleDuration.set(baseIdleDuration); - if (loadedRegions.size() > tectonicLimit.get()) { - // todo update this correctly and maybe do something when its above a 100% - if (IrisSettings.get().getPerformance().dynamicPerformanceMode) { - int tectonicLimitValue = tectonicLimit.get(); - adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimitValue) / (double) tectonicLimitValue) * 100) * 0.4), 4000)); + if (loadedRegions != null) { + if (loadedRegions.size() > tectonicLimit) { + // todo update this correctly and maybe do something when its above a 100% + adjustedIdleDuration.set(Math.max(adjustedIdleDuration.get() - (1000 * (((loadedRegions.size() - tectonicLimit) / (double) tectonicLimit) * 100) * 0.4), 4000)); } } - io.set(true); - + ioTrim.set(true); + unloadLock.lock(); try { Iris.debug("Trimming Tectonic Plates older than " + Form.duration(adjustedIdleDuration.get(), 0)); - Set toUnload = new HashSet<>(); - - for (Long i : lastUse.keySet()) { - double finalAdjustedIdleDuration = adjustedIdleDuration.get(); - hyperLock.withLong(i, () -> { - if (M.ms() - lastUse.get(i) >= finalAdjustedIdleDuration) { - toUnload.add(i); - FakeToUnload.addAndGet(1); - Iris.debug("Tectonic Region added to unload"); - } - }); - } - - BurstExecutor burstExecutor = new BurstExecutor(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()), toUnload.size()); - - for (Long i : toUnload) { - burstExecutor.queue(() -> { + if (lastUse != null) { + for (Long i : new ArrayList<>(lastUse.keySet())) { + double finalAdjustedIdleDuration = adjustedIdleDuration.get(); hyperLock.withLong(i, () -> { - TectonicPlate m = loadedRegions.get(i); - if (m != null) { - try { - m.write(fileForRegion(dataFolder, i)); - loadedRegions.remove(i); - lastUse.remove(i); - Iris.debug("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(i) + " " + Cache.keyZ(i)); - FakeToUnload.addAndGet(-1); - } catch (IOException e) { - e.printStackTrace(); - } + Long lastUseTime = lastUse.get(i); + if (lastUseTime != null && M.ms() - lastUseTime >= finalAdjustedIdleDuration) { + toUnload.add(i); + Iris.debug("Tectonic Region added to unload"); } }); - }); + } } - burstExecutor.complete(); - } finally { - io.set(false); + ioTrim.set(false); + unloadLock.unlock(); } } + public int unloadTectonicPlate() { + AtomicInteger i = new AtomicInteger(); + unloadLock.lock(); + try { + List> futures = new ArrayList<>(); + ExecutorService service = Executors.newFixedThreadPool(dynamicThreads.get()); + for (long id : new ArrayList<>(toUnload)) { + futures.add(service.submit(() -> + hyperLock.withLong(id, () -> { + TectonicPlate m = loadedRegions.get(id); + if (m != null) { + try { + m.write(fileForRegion(dataFolder, id)); + loadedRegions.remove(id); + lastUse.remove(id); + toUnload.remove(id); + i.incrementAndGet(); + Iris.info("Unloaded Tectonic Plate " + C.DARK_GREEN + Cache.keyX(id) + " " + Cache.keyZ(id)); + } catch (IOException e) { + e.printStackTrace(); + } + } + }))); + } + + try { + while (!futures.isEmpty()) { + futures.remove(0).get(); + futures.removeIf(Future::isDone); + } + service.shutdown(); + } catch (InterruptedException ignored) {} + } catch (Exception e) { + e.printStackTrace(); + } finally { + unloadLock.unlock(); + ioTectonicUnload.set(true); + } + return i.get(); + } + + /** * This retreives a future of the Tectonic Plate at the given coordinates. * All methods accessing tectonic plates should go through this method @@ -488,7 +497,7 @@ public class Mantle { */ @RegionCoordinates private TectonicPlate get(int x, int z) { - if (io.get()) { + if (ioTrim.get()) { try { return getSafe(x, z).get(); } catch (InterruptedException e) { @@ -545,6 +554,8 @@ public class Mantle { } File file = fileForRegion(dataFolder, x, z); + if (!file.exists()) + file = new File(dataFolder, file.getName().substring(".lz4b".length())); if (file.exists()) { try { @@ -583,10 +594,6 @@ public class Mantle { } - public int getWorldHeight() { - return worldHeight; - } - public MantleChunk getChunk(Chunk e) { return getChunk(e.getX(), e.getZ()); } @@ -618,4 +625,4 @@ public class Mantle { public boolean shouldReduce(Engine engine) { return !engine.isStudio() || IrisSettings.get().getPerformance().isTrimMantleInStudio(); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java index bea18ba3a..b8e854c7d 100644 --- a/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java +++ b/core/src/main/java/com/volmit/iris/util/mantle/TectonicPlate.java @@ -26,6 +26,10 @@ import com.volmit.iris.util.format.C; import com.volmit.iris.util.format.Form; import com.volmit.iris.util.scheduling.PrecisionStopwatch; import lombok.Getter; +import net.jpountz.lz4.LZ4BlockInputStream; +import net.jpountz.lz4.LZ4BlockOutputStream; +import net.jpountz.lz4.LZ4FrameInputStream; +import net.jpountz.lz4.LZ4FrameOutputStream; import java.io.*; import java.util.concurrent.atomic.AtomicReferenceArray; @@ -79,8 +83,14 @@ public class TectonicPlate { public static TectonicPlate read(int worldHeight, File file) throws IOException, ClassNotFoundException { FileInputStream fin = new FileInputStream(file); - GZIPInputStream gzi = new GZIPInputStream(fin); - DataInputStream din = new DataInputStream(gzi); + DataInputStream din; + if (file.getName().endsWith("ttp.lz4")) { + GZIPInputStream gzi = new GZIPInputStream(fin); + din = new DataInputStream(gzi); + } else { + LZ4BlockInputStream lz4 = new LZ4BlockInputStream(fin); + din = new DataInputStream(lz4); + } TectonicPlate p = new TectonicPlate(worldHeight, din); din.close(); @@ -164,11 +174,17 @@ public class TectonicPlate { public void write(File file) throws IOException { PrecisionStopwatch p = PrecisionStopwatch.start(); FileOutputStream fos = new FileOutputStream(file); - GZIPOutputStream gzo = new GZIPOutputStream(fos); - DataOutputStream dos = new DataOutputStream(gzo); + DataOutputStream dos; + if (file.getName().endsWith("ttp")) { + GZIPOutputStream gzo = new GZIPOutputStream(fos); + dos = new DataOutputStream(gzo); + } else { + LZ4BlockOutputStream lz4 = new LZ4BlockOutputStream(fos); + dos = new DataOutputStream(lz4); + } write(dos); dos.close(); - Iris.debug("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); + Iris.info("Saved Tectonic Plate " + C.DARK_GREEN + file.getName().split("\\Q.\\E")[0] + C.RED + " in " + Form.duration(p.getMilliseconds(), 2)); } /** diff --git a/core/src/main/java/com/volmit/iris/util/misc/getHardware.java b/core/src/main/java/com/volmit/iris/util/misc/getHardware.java index 80221590d..0b9a52033 100644 --- a/core/src/main/java/com/volmit/iris/util/misc/getHardware.java +++ b/core/src/main/java/com/volmit/iris/util/misc/getHardware.java @@ -13,11 +13,6 @@ public class getHardware { OperatingSystem os = systemInfo.getOperatingSystem(); return os.toString(); } - public static int getCPUThreads(){ - SystemInfo systemInfo = new SystemInfo(); - CentralProcessor processor = systemInfo.getHardware().getProcessor(); - return processor.getLogicalProcessorCount(); - } public static long getProcessMemory(){ long maxMemory = Runtime.getRuntime().maxMemory() / (1024 * 1024); return maxMemory; diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index 126c4b961..83ad85017 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -18,6 +18,7 @@ libraries: - org.ow2.asm:asm:9.2 - rhino:js:1.7R2 - bsf:bsf:2.4.0 + - org.lz4:lz4-java:1.8.0 commands: iris: aliases: [ ir, irs ]