9
0
mirror of https://github.com/VolmitSoftware/Iris.git synced 2025-12-30 12:29:20 +00:00

- Fixed LazyPregenerator.java and improved it. ( Speed decrease on server restart when lazy starts the job up dont know why)

- Mem leak fix only works when there is 1 iris world for now ( working on it ) + A capped performance limit still very alpha like seems stable tho.

- Disabled HotDropWorldSVC acts a bit weird sometimes.
This commit is contained in:
RePixelatedMC
2023-12-15 19:06:53 +01:00
parent 57c647fa0e
commit 54ae026c2b
6 changed files with 215 additions and 113 deletions

View File

@@ -34,6 +34,7 @@ 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 {
@@ -53,6 +54,14 @@ public class CommandLazyPregen implements DecreeExecutor {
) {
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!");
@@ -69,7 +78,6 @@ public class CommandLazyPregen implements DecreeExecutor {
.silent(silent)
.build();
File worldDirectory = new File(Bukkit.getWorldContainer(), worldName);
File lazyGenFile = new File(worldDirectory, "lazygen.json");
LazyPregenerator pregenerator = new LazyPregenerator(pregenJob, lazyGenFile);
pregenerator.start();
@@ -85,20 +93,26 @@ public class CommandLazyPregen implements DecreeExecutor {
}
@Decree(description = "Stop the active pregeneration task", aliases = "x")
public void stop() {
public void stop(
@Param(aliases = "world", description = "The world to pause")
World world
) throws IOException {
if (LazyPregenerator.getInstance() != null) {
LazyPregenerator.getInstance().shutdownInstance();
Iris.info( C.BLUE + "Shutting down all Lazy Pregens");
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() {
public void pause(
@Param(aliases = "world", description = "The world to pause")
World world
) {
if (LazyPregenerator.getInstance() != null) {
LazyPregenerator.getInstance().setPausedLazy();
sender().sendMessage(C.GREEN + "Paused/unpaused Lazy Pregen, now: " + (LazyPregenerator.getInstance().isPausedLazy() ? "Paused" : "Running") + ".");
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.");

View File

@@ -2,8 +2,6 @@ package com.volmit.iris.core.pregenerator;
import com.google.gson.Gson;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.gui.PregeneratorJob;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.io.IO;
@@ -11,11 +9,8 @@ import com.volmit.iris.util.math.M;
import com.volmit.iris.util.math.Position2;
import com.volmit.iris.util.math.RollingSequence;
import com.volmit.iris.util.math.Spiraler;
import com.volmit.iris.util.parallel.BurstExecutor;
import com.volmit.iris.util.parallel.MultiBurst;
import com.volmit.iris.util.scheduling.ChronoLatch;
import com.volmit.iris.util.scheduling.J;
import io.lumine.mythic.bukkit.utils.lib.jooq.False;
import io.papermc.lib.PaperLib;
import lombok.Builder;
import lombok.Data;
@@ -25,23 +20,26 @@ 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.AtomicBoolean;
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;
@@ -49,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<String, LazyPregenJob> jobs = new HashMap<>();
public LazyPregenerator(LazyPregenJob job, File destination) {
this.job = job;
@@ -56,13 +58,15 @@ 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) Math.ceil(Math.pow((2.0 * job.getRadiusBlocks()) / 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;
}
@@ -73,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);
@@ -107,15 +110,17 @@ public class LazyPregenerator extends Thread implements Listener {
}
public void tick() {
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);
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) chunksPerSecond.getAverage()) + "/s ETA: " + Form.duration((double) eta, 2));
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));
}
}
@@ -138,12 +143,8 @@ public class LazyPregenerator extends Thread implements Listener {
}
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();
@@ -154,7 +155,10 @@ public class LazyPregenerator extends Thread implements Listener {
if (PaperLib.isPaper()) {
PaperLib.getChunkAtAsync(world, chunk.getX(), chunk.getZ(), true)
.thenAccept((i) -> {
Iris.verbose("Generated Async " + chunk);
LazyPregenJob j = jobs.get(world.getName());
if (!j.paused) {
Iris.verbose("Generated Async " + chunk);
}
latch.countDown();
});
} else {
@@ -201,24 +205,65 @@ public class LazyPregenerator extends Thread implements Listener {
}
});
}
public void setPausedLazy(){
if (!job.paused) {
save();
Iris.info(C.BLUE + "LazyGen: " + C.IRIS + world + C.BLUE + " Paused");
job.setPaused(true);
public static void setPausedLazy(World world) {
// todo: doesnt actually pause
LazyPregenJob job = jobs.get(world.getName());
if (isPausedLazy(world)){
job.paused = false;
} else {
Iris.info(C.BLUE + "LazyGen: " + C.IRIS + world + C.BLUE + " Resumes");
job.setPaused(false);
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 boolean isPausedLazy(){
return job.isPaused();
public static boolean isPausedLazy(World world) {
LazyPregenJob job = jobs.get(world.getName());
return job != null && job.isPaused();
}
public void shutdownInstance() {
save();
interrupt();
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));
}
@@ -232,7 +277,7 @@ 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
@@ -243,3 +288,4 @@ public class LazyPregenerator extends Thread implements Listener {
boolean paused = false;
}
}

View File

@@ -30,7 +30,7 @@ public class HotDropWorldSVC implements IrisService {
@Override
public void onEnable() {
this.plugin = Iris.instance;
initializeWatchService();
// initializeWatchService();
}
@Override

View File

@@ -1,68 +1,120 @@
package com.volmit.iris.core.service;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
import com.volmit.iris.util.collection.KSet;
import com.volmit.iris.util.format.C;
import com.volmit.iris.util.format.Form;
import com.volmit.iris.util.mantle.TectonicPlate;
import com.volmit.iris.util.math.M;
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.plugin.IrisService;
import com.volmit.iris.util.scheduling.J;
import com.volmit.iris.util.scheduling.Looper;
import io.papermc.lib.PaperLib;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static com.volmit.iris.util.mantle.Mantle.tectonicLimit;
public class IrisEngineSVC implements IrisService {
private JavaPlugin plugin;
public Looper ticker;
public Looper ticker1;
public Looper ticker2;
public Looper engineTicker;
public World selectedWorld;
public List<World> IrisWorlds = new ArrayList<>();
public List<World> corruptedIrisWorlds = new ArrayList<>();
@Override
public void onEnable() {
this.plugin = Iris.instance;
this.IrisStartup();
tectonicLimit.set(2);
long t = getHardware.getProcessMemory();
for (; t > 250; ) {
tectonicLimit.getAndAdd(1);
t = t - 250;
}
tectonicLimit.set(10); // DEBUG CODE
this.IrisEngine();
ticker.start();
engineTicker.start();
ticker1.start();
ticker2.start();
}
private final AtomicReference<World> selectedWorldRef = new AtomicReference<>();
public CompletableFuture<World> initializeAsync() {
return CompletableFuture.supplyAsync(() -> {
World selectedWorld = null;
while (selectedWorld == null) {
synchronized (this) {
IrisWorlds.clear();
for (World w : Bukkit.getServer().getWorlds()) {
if (IrisToolbelt.access(w) != null) {
IrisWorlds.add(w);
}
}
if (!IrisWorlds.isEmpty()) {
Random rand = new Random();
int randomIndex = rand.nextInt(IrisWorlds.size());
selectedWorld = IrisWorlds.get(randomIndex);
}
}
if (selectedWorld == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
}
return selectedWorld;
});
}
public void IrisEngine(){
ticker = new Looper() {
engineTicker = new Looper() {
@Override
protected long loop() {
try {
for (World world : IrisWorlds) {
Engine engine = IrisToolbelt.access(world).getEngine();
}
PlatformChunkGenerator generator = IrisToolbelt.access(Bukkit.getWorld("localmemtest"));
if (generator != null && generator.getEngine() != null) {
Engine engine = generator.getEngine();
engine.getMantle().trim();
World world = selectedWorldRef.get();
PlatformChunkGenerator generator = IrisToolbelt.access(world);
if(generator == null) {
initializeAsync().thenAcceptAsync(foundWorld -> selectedWorldRef.set(foundWorld));
} else {
Iris.info("localmemtest is nullmem");
selectedWorld = world;
}
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
return -1;
}
return 1000;
}
};
ticker1 = new Looper() {
@Override
protected long loop() {
try {
World world = selectedWorld;
PlatformChunkGenerator generator = IrisToolbelt.access(world);
if(generator != null) {
Engine engine = IrisToolbelt.access(world).getEngine();
if (generator != null && generator.getEngine() != null) {
engine.getMantle().trim();
} else {
Iris.info("something is null 1");
}
}
} catch (Throwable e) {
Iris.reportError(e);
@@ -73,38 +125,36 @@ public class IrisEngineSVC implements IrisService {
return 1000;
}
};
}
public void IrisStartup(){
tectonicLimit.set(2);
long t = getHardware.getProcessMemory();
for (; t > 250; ) {
tectonicLimit.getAndAdd(1);
t = t - 250;
}
tectonicLimit.set(10); // DEBUG CODE
for (World w : Bukkit.getServer().getWorlds()) {
File container = Bukkit.getWorldContainer();
Bukkit.getWorldContainer();
if(IrisToolbelt.access(w) != null){
IrisWorlds.add(w);
} else {
File worldDirectory = new File(container, w.getName());
File IrisWorldTest = new File(worldDirectory, "Iris");
if (IrisWorldTest.exists()){
if(IrisToolbelt.access(w) == null){
corruptedIrisWorlds.add(w);
ticker2 = new Looper() {
@Override
protected long loop() {
try {
World world = selectedWorld;
PlatformChunkGenerator generator = IrisToolbelt.access(world);
if(generator != null) {
Engine engine = IrisToolbelt.access(world).getEngine();
if (generator != null && generator.getEngine() != null) {
engine.getMantle().unloadTectonicPlate();
} else {
Iris.info("something is null 2");
}
}
} catch (Throwable e) {
Iris.reportError(e);
e.printStackTrace();
return -1;
}
return 1000;
}
}
};
}
@Override
public void onDisable() {
ticker.interrupt();
ticker1.interrupt();
ticker2.interrupt();
engineTicker.interrupt();
}
}

View File

@@ -178,6 +178,9 @@ public interface EngineMantle extends IObjectPlacer {
default void trim() {
getMantle().trim(TimeUnit.SECONDS.toMillis(IrisSettings.get().getPerformance().getMantleKeepAlive()));
}
default void unloadTectonicPlate(){
getMantle().unloadTectonicPlate();
}
default MultiBurst burst() {
return getEngine().burst();

View File

@@ -91,12 +91,6 @@ public class Mantle {
loadedRegions = new KMap<>();
lastUse = new KMap<>();
ioBurst = MultiBurst.burst;
if (!ioTectonicUnload.get()) {
this.unloadTectonicPlate();
if (!ticker.isAlive()) {
ticker.start();
}
}
Iris.debug("Opened The Mantle " + C.DARK_AQUA + dataFolder.getAbsolutePath());
}
@@ -421,7 +415,7 @@ public class Mantle {
if (closed.get()) {
throw new RuntimeException("The Mantle is closed");
}
Iris.info(C.BLUE + "TECTONIC TRIM HAS RUN");
Iris.debug(C.BLUE + "TECTONIC TRIM HAS RUN");
if (IrisSettings.get().getPerformance().getAggressiveTectonicThreshold() == -1) {
forceAggressiveThreshold.set(tectonicLimit.get());
} else {
@@ -493,12 +487,10 @@ public class Mantle {
}
}
protected void unloadTectonicPlate() {
ticker = new Looper() {
protected long loop() {
public synchronized void unloadTectonicPlate() {
long time = System.currentTimeMillis();
try {
Iris.info(C.DARK_BLUE + "TECTONIC UNLOAD HAS RUN");
Iris.debug(C.DARK_BLUE + "TECTONIC UNLOAD HAS RUN");
int threadCount = 1;
ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
List<Long> toUnloadList;
@@ -537,11 +529,8 @@ public class Mantle {
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (Exception e) {
e.printStackTrace();
return -1;
}
return Math.max(0, 1000-(System.currentTimeMillis()-time));
}
};
ioTectonicUnload.set(true);
}