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 5b69fa71f..60b691807 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -144,7 +144,6 @@ public class IrisSettings { public int tectonicUnloadThreads = -1; // -1 = Disabled and instead use the dynamic method public boolean AggressiveTectonicUnload = false; public int AggressiveTectonicThreshold = -1; // -1 = Disabled and instead uses the tectonicLimit - public int LazyPregenMaxCPM = -1; // -1 = no limit } @Data 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 b36b6e294..76c2af063 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 @@ -62,6 +62,7 @@ import static com.volmit.iris.core.safeguard.ServerBootSFG.incompatiblePlugins; public class CommandIris implements DecreeExecutor { private CommandStudio studio; private CommandPregen pregen; + private CommandLazyPregen lazyPregen; private CommandSettings settings; private CommandObject object; private CommandJigsaw jigsaw; @@ -369,77 +370,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..cc1c7f6aa --- /dev/null +++ b/core/src/main/java/com/volmit/iris/core/commands/CommandLazyPregen.java @@ -0,0 +1,99 @@ +/* + * 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.World; +import org.bukkit.util.Vector; + +import java.io.File; + +@Decree(name = "lazypregen", aliases = "lazy", description = "Pregenerate your Iris worlds!") +public class CommandLazyPregen implements DecreeExecutor { + @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 = "maxcpm", description = "Limit the chunks per minute the pregen will generate", defaultValue = "false") + boolean dummySilent + ) { + 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(cpm) + .radiusBlocks(radius) + .position(0) + .silent(dummySilent) + .build(); + + LazyPregenerator pregenerator = new LazyPregenerator(pregenJob, new File("plugins/Iris/lazygen.json")); + 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() { + if (PregeneratorJob.shutdownInstance()) { + Iris.info( C.BLUE + "Finishing up mca region..."); + } 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() { + if (PregeneratorJob.pauseResume()) { + sender().sendMessage(C.GREEN + "Paused/unpaused pregeneration task, now: " + (PregeneratorJob.isPaused() ? "Paused" : "Running") + "."); + } else { + sender().sendMessage(C.YELLOW + "No active pregeneration 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 9a888b7a8..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 @@ -44,74 +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."); - } - int cpm = 0; - if (IrisSettings.get().getPerformance().getLazyPregenMaxCPM() == -1) { - cpm = 999999999; - } else { - cpm = IrisSettings.get().getPerformance().getLazyPregenMaxCPM(); - } - - LazyPregenerator.LazyPregenJob pregenJob = LazyPregenerator.LazyPregenJob.builder() - .world(worldName) - .healingPosition(0) - .healing(false) - .chunksPerMinute(cpm) - .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/pregenerator/LazyPregenerator.java b/core/src/main/java/com/volmit/iris/core/pregenerator/LazyPregenerator.java index b53bfcc03..10d528c90 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 @@ -110,10 +110,11 @@ public class LazyPregenerator extends Thread implements Listener { generatedLast.set(lazyGeneratedChunks.get()); secondGenerated = secondGenerated / 6; 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)); + 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) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2)); + } //Iris.info("Debug: " + maxPosition); //Iris.info("Debug1: " + job.getPosition()); - // todo: Maxpos borked } @@ -216,5 +217,7 @@ public class LazyPregenerator extends Thread implements Listener { private int radiusBlocks = 5000; @Builder.Default private int position = 0; + @Builder.Default + boolean silent = false; } } 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 7df45363e..d053f17bc 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 @@ -398,7 +398,6 @@ public class Mantle { return numberOfEntries * bytesPerEntry; } - private final AtomicInteger oldFakeToUnload = new AtomicInteger((0)); @Getter private final AtomicDouble adjustedIdleDuration = new AtomicDouble(0); public static final AtomicInteger tectonicLimit = new AtomicInteger(30); @@ -409,7 +408,7 @@ public class Mantle { @Getter private final AtomicLong oldestTectonicPlate = new AtomicLong(0); @Getter - public Set toUnload = new HashSet<>(); + public final Set toUnload = new HashSet<>(); private int g = 0; /** @@ -429,34 +428,6 @@ public class Mantle { forceAggressiveThreshold.set(IrisSettings.get().getPerformance().getAggressiveTectonicThreshold()); } - // todo Repixel improve the logic - /* - int h = dynamicThreads.get() - 1; - if (toUnload.size() != 0) { - if (toUnload.size() > oldFakeToUnload.get()) { - g++; - if (g >= 2 && IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()) > h && IrisSettings.getThreadCount(IrisSettings.get().getConcurrency().getParallelism()) != h) { - dynamicThreads.addAndGet(1); - } - } else { - if (g > 0) { - g--; - } - } - } else { - if (dynamicThreads.get() >= 2) { - dynamicThreads.addAndGet(-1); - } - } - oldFakeToUnload.set(toUnload.size()); - - if(IrisSettings.get().getPerformance().getTectonicUnloadThreads() <= -1){ - dynamicThreads.set(1); - } else { - dynamicThreads.set(IrisSettings.get().getPerformance().getTectonicUnloadThreads()); - } - */ - adjustedIdleDuration.set(baseIdleDuration); if (loadedRegions != null) { @@ -525,11 +496,15 @@ public class Mantle { protected void unloadTectonicPlate() { ticker = new Looper() { protected long loop() { + long time = System.currentTimeMillis(); try { Iris.info(C.DARK_BLUE + "TECTONIC UNLOAD HAS RUN"); int threadCount = 1; ExecutorService executorService = Executors.newFixedThreadPool(threadCount); - List toUnloadList = new ArrayList<>(toUnload); + List toUnloadList; + synchronized (toUnload) { + toUnloadList = new ArrayList<>(toUnload); + } int chunkSize = (int) Math.ceil(toUnloadList.size() / (double) threadCount); @@ -561,16 +536,15 @@ public class Mantle { executorService.shutdown(); executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (Exception e) { - Iris.reportError(e); + e.printStackTrace(); return -1; } - return 1000; + return Math.max(0, 1000-(System.currentTimeMillis()-time)); } }; ioTectonicUnload.set(true); } - /** * This retreives a future of the Tectonic Plate at the given coordinates. * All methods accessing tectonic plates should go through this method