mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-26 14:00:26 +00:00
implement engine services
This commit is contained in:
6 files changed
+274
-364
No files matched your search
@@ -22,11 +22,11 @@ import com.volmit.iris.Iris;
|
|||||||
import com.volmit.iris.core.ServerConfigurator;
|
import com.volmit.iris.core.ServerConfigurator;
|
||||||
import com.volmit.iris.core.loader.IrisData;
|
import com.volmit.iris.core.loader.IrisData;
|
||||||
import com.volmit.iris.core.nms.datapack.DataVersion;
|
import com.volmit.iris.core.nms.datapack.DataVersion;
|
||||||
import com.volmit.iris.core.service.IrisEngineSVC;
|
|
||||||
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
import com.volmit.iris.core.tools.IrisPackBenchmarking;
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
import com.volmit.iris.core.tools.IrisToolbelt;
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
import com.volmit.iris.engine.object.IrisDimension;
|
import com.volmit.iris.engine.object.IrisDimension;
|
||||||
|
import com.volmit.iris.engine.service.EngineStatusSVC;
|
||||||
import com.volmit.iris.util.decree.DecreeExecutor;
|
import com.volmit.iris.util.decree.DecreeExecutor;
|
||||||
import com.volmit.iris.util.decree.DecreeOrigin;
|
import com.volmit.iris.util.decree.DecreeOrigin;
|
||||||
import com.volmit.iris.util.decree.annotations.Decree;
|
import com.volmit.iris.util.decree.annotations.Decree;
|
||||||
@@ -66,53 +66,20 @@ public class CommandDeveloper implements DecreeExecutor {
|
|||||||
|
|
||||||
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
|
@Decree(description = "Get Loaded TectonicPlates Count", origin = DecreeOrigin.BOTH, sync = true)
|
||||||
public void EngineStatus() {
|
public void EngineStatus() {
|
||||||
List<World> IrisWorlds = new ArrayList<>();
|
var status = EngineStatusSVC.getStatus();
|
||||||
int TotalLoadedChunks = 0;
|
|
||||||
int TotalQueuedTectonicPlates = 0;
|
|
||||||
int TotalNotQueuedTectonicPlates = 0;
|
|
||||||
int TotalTectonicPlates = 0;
|
|
||||||
|
|
||||||
long lowestUnloadDuration = 0;
|
sender().sendMessage("-------------------------");
|
||||||
long highestUnloadDuration = 0;
|
sender().sendMessage(C.DARK_PURPLE + "Engine Status");
|
||||||
|
sender().sendMessage(C.DARK_PURPLE + "Total Engines: " + C.LIGHT_PURPLE + status.engineCount());
|
||||||
for (World world : Bukkit.getWorlds()) {
|
sender().sendMessage(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + status.loadedChunks());
|
||||||
try {
|
sender().sendMessage(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + status.tectonicLimit());
|
||||||
if (IrisToolbelt.access(world).getEngine() != null) {
|
sender().sendMessage(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + status.tectonicPlates());
|
||||||
IrisWorlds.add(world);
|
sender().sendMessage(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + status.activeTectonicPlates());
|
||||||
}
|
sender().sendMessage(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + status.queuedTectonicPlates());
|
||||||
} catch (Exception e) {
|
sender().sendMessage(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(status.minTectonicUnloadDuration()));
|
||||||
// no
|
sender().sendMessage(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(status.maxTectonicUnloadDuration()));
|
||||||
}
|
sender().sendMessage(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
|
||||||
}
|
sender().sendMessage("-------------------------");
|
||||||
|
|
||||||
for (World world : IrisWorlds) {
|
|
||||||
Engine engine = IrisToolbelt.access(world).getEngine();
|
|
||||||
TotalQueuedTectonicPlates += (int) engine.getMantle().getToUnload();
|
|
||||||
TotalNotQueuedTectonicPlates += (int) engine.getMantle().getNotQueuedLoadedRegions();
|
|
||||||
TotalTectonicPlates += engine.getMantle().getLoadedRegionCount();
|
|
||||||
if (highestUnloadDuration <= (long) engine.getMantle().getTectonicDuration()) {
|
|
||||||
highestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
|
|
||||||
}
|
|
||||||
if (lowestUnloadDuration >= (long) engine.getMantle().getTectonicDuration()) {
|
|
||||||
lowestUnloadDuration = (long) engine.getMantle().getTectonicDuration();
|
|
||||||
}
|
|
||||||
for (Chunk chunk : world.getLoadedChunks()) {
|
|
||||||
if (chunk.isLoaded()) {
|
|
||||||
TotalLoadedChunks++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Iris.info("-------------------------");
|
|
||||||
Iris.info(C.DARK_PURPLE + "Engine Status");
|
|
||||||
Iris.info(C.DARK_PURPLE + "Total Loaded Chunks: " + C.LIGHT_PURPLE + TotalLoadedChunks);
|
|
||||||
Iris.info(C.DARK_PURPLE + "Tectonic Limit: " + C.LIGHT_PURPLE + IrisEngineSVC.getTectonicLimit());
|
|
||||||
Iris.info(C.DARK_PURPLE + "Tectonic Total Plates: " + C.LIGHT_PURPLE + TotalTectonicPlates);
|
|
||||||
Iris.info(C.DARK_PURPLE + "Tectonic Active Plates: " + C.LIGHT_PURPLE + TotalNotQueuedTectonicPlates);
|
|
||||||
Iris.info(C.DARK_PURPLE + "Tectonic ToUnload: " + C.LIGHT_PURPLE + TotalQueuedTectonicPlates);
|
|
||||||
Iris.info(C.DARK_PURPLE + "Lowest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(lowestUnloadDuration));
|
|
||||||
Iris.info(C.DARK_PURPLE + "Highest Tectonic Unload Duration: " + C.LIGHT_PURPLE + Form.duration(highestUnloadDuration));
|
|
||||||
Iris.info(C.DARK_PURPLE + "Cache Size: " + C.LIGHT_PURPLE + Form.f(IrisData.cacheSize()));
|
|
||||||
Iris.info("-------------------------");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Decree(description = "Test")
|
@Decree(description = "Test")
|
||||||
|
|||||||
@@ -1,317 +0,0 @@
|
|||||||
package com.volmit.iris.core.service;
|
|
||||||
|
|
||||||
import com.volmit.iris.Iris;
|
|
||||||
import com.volmit.iris.core.IrisSettings;
|
|
||||||
import com.volmit.iris.core.tools.IrisToolbelt;
|
|
||||||
import com.volmit.iris.engine.framework.Engine;
|
|
||||||
import com.volmit.iris.engine.platform.PlatformChunkGenerator;
|
|
||||||
import com.volmit.iris.util.collection.KList;
|
|
||||||
import com.volmit.iris.util.collection.KMap;
|
|
||||||
import com.volmit.iris.util.format.C;
|
|
||||||
import com.volmit.iris.util.format.Form;
|
|
||||||
import com.volmit.iris.util.mantle.TectonicPlate;
|
|
||||||
import com.volmit.iris.util.misc.getHardware;
|
|
||||||
import com.volmit.iris.util.plugin.IrisService;
|
|
||||||
import com.volmit.iris.util.scheduling.ChronoLatch;
|
|
||||||
import com.volmit.iris.util.scheduling.Looper;
|
|
||||||
import com.volmit.iris.util.scheduling.PrecisionStopwatch;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.server.PluginDisableEvent;
|
|
||||||
import org.bukkit.event.server.ServerLoadEvent;
|
|
||||||
import org.bukkit.event.world.WorldLoadEvent;
|
|
||||||
import org.bukkit.event.world.WorldUnloadEvent;
|
|
||||||
import org.checkerframework.checker.units.qual.A;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
public class IrisEngineSVC implements IrisService {
|
|
||||||
public static IrisEngineSVC instance;
|
|
||||||
public boolean isServerShuttingDown = false;
|
|
||||||
public boolean isServerLoaded = false;
|
|
||||||
private static final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
|
||||||
private ReentrantLock lastUseLock;
|
|
||||||
private KMap<World, Long> lastUse;
|
|
||||||
private List<World> IrisWorlds;
|
|
||||||
private Looper cacheTicker;
|
|
||||||
private Looper trimTicker;
|
|
||||||
private Looper unloadTicker;
|
|
||||||
private Looper updateTicker;
|
|
||||||
private PrecisionStopwatch trimAlive;
|
|
||||||
private PrecisionStopwatch unloadAlive;
|
|
||||||
public PrecisionStopwatch trimActiveAlive;
|
|
||||||
public PrecisionStopwatch unloadActiveAlive;
|
|
||||||
private AtomicInteger TotalTectonicPlates;
|
|
||||||
private AtomicInteger TotalQueuedTectonicPlates;
|
|
||||||
private AtomicInteger TotalNotQueuedTectonicPlates;
|
|
||||||
private AtomicBoolean IsUnloadAlive;
|
|
||||||
private AtomicBoolean IsTrimAlive;
|
|
||||||
ChronoLatch cl;
|
|
||||||
|
|
||||||
public List<World> corruptedIrisWorlds = new ArrayList<>();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onEnable() {
|
|
||||||
this.cl = new ChronoLatch(5000);
|
|
||||||
lastUse = new KMap<>();
|
|
||||||
lastUseLock = new ReentrantLock();
|
|
||||||
IrisWorlds = new ArrayList<>();
|
|
||||||
IsUnloadAlive = new AtomicBoolean(true);
|
|
||||||
IsTrimAlive = new AtomicBoolean(true);
|
|
||||||
trimActiveAlive = new PrecisionStopwatch();
|
|
||||||
unloadActiveAlive = new PrecisionStopwatch();
|
|
||||||
trimAlive = new PrecisionStopwatch();
|
|
||||||
unloadAlive = new PrecisionStopwatch();
|
|
||||||
TotalTectonicPlates = new AtomicInteger();
|
|
||||||
TotalQueuedTectonicPlates = new AtomicInteger();
|
|
||||||
TotalNotQueuedTectonicPlates = new AtomicInteger();
|
|
||||||
tectonicLimit.set(2);
|
|
||||||
long t = getHardware.getProcessMemory();
|
|
||||||
while (t > 200) {
|
|
||||||
tectonicLimit.getAndAdd(1);
|
|
||||||
t = t - 200;
|
|
||||||
}
|
|
||||||
this.setup();
|
|
||||||
this.TrimLogic();
|
|
||||||
this.UnloadLogic();
|
|
||||||
|
|
||||||
trimAlive.begin();
|
|
||||||
unloadAlive.begin();
|
|
||||||
trimActiveAlive.begin();
|
|
||||||
unloadActiveAlive.begin();
|
|
||||||
|
|
||||||
updateTicker.start();
|
|
||||||
cacheTicker.start();
|
|
||||||
//trimTicker.start();
|
|
||||||
//unloadTicker.start();
|
|
||||||
instance = this;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void engineStatus() {
|
|
||||||
boolean trimAlive = trimTicker.isAlive();
|
|
||||||
boolean unloadAlive = unloadTicker.isAlive();
|
|
||||||
Iris.info("Status:");
|
|
||||||
Iris.info("- Trim: " + trimAlive);
|
|
||||||
Iris.info("- Unload: " + unloadAlive);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getTectonicLimit() {
|
|
||||||
return tectonicLimit.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onWorldUnload(WorldUnloadEvent event) {
|
|
||||||
updateWorlds();
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onWorldLoad(WorldLoadEvent event) {
|
|
||||||
updateWorlds();
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onServerBoot(ServerLoadEvent event) {
|
|
||||||
isServerLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginDisable(PluginDisableEvent event) {
|
|
||||||
if (event.getPlugin().equals(Iris.instance)) {
|
|
||||||
isServerShuttingDown = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateWorlds() {
|
|
||||||
for (World world : Bukkit.getWorlds()) {
|
|
||||||
try {
|
|
||||||
if (IrisToolbelt.access(world).getEngine() != null) {
|
|
||||||
IrisWorlds.add(world);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// no
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setup() {
|
|
||||||
cacheTicker = new Looper() {
|
|
||||||
@Override
|
|
||||||
protected long loop() {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
lastUseLock.lock();
|
|
||||||
try {
|
|
||||||
for (World key : new ArrayList<>(lastUse.keySet())) {
|
|
||||||
Long last = lastUse.get(key);
|
|
||||||
if (last == null)
|
|
||||||
continue;
|
|
||||||
if (now - last > 60000) {
|
|
||||||
lastUse.remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lastUseLock.unlock();
|
|
||||||
}
|
|
||||||
return 1000;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
updateTicker = new Looper() {
|
|
||||||
@Override
|
|
||||||
protected long loop() {
|
|
||||||
try {
|
|
||||||
TotalQueuedTectonicPlates.set(0);
|
|
||||||
TotalNotQueuedTectonicPlates.set(0);
|
|
||||||
TotalTectonicPlates.set(0);
|
|
||||||
for (World world : IrisWorlds) {
|
|
||||||
Engine engine = Objects.requireNonNull(IrisToolbelt.access(world)).getEngine();
|
|
||||||
TotalQueuedTectonicPlates.addAndGet((int) engine.getMantle().getToUnload());
|
|
||||||
TotalNotQueuedTectonicPlates.addAndGet((int) engine.getMantle().getNotQueuedLoadedRegions());
|
|
||||||
TotalTectonicPlates.addAndGet(engine.getMantle().getLoadedRegionCount());
|
|
||||||
}
|
|
||||||
if (!isServerShuttingDown && isServerLoaded) {
|
|
||||||
if (!trimTicker.isAlive()) {
|
|
||||||
Iris.info(C.RED + "TrimTicker found dead! Booting it up!");
|
|
||||||
try {
|
|
||||||
TrimLogic();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Iris.error("What happened?");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!unloadTicker.isAlive()) {
|
|
||||||
Iris.info(C.RED + "UnloadTicker found dead! Booting it up!");
|
|
||||||
try {
|
|
||||||
UnloadLogic();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Iris.error("What happened?");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 1000;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
public void TrimLogic() {
|
|
||||||
if (trimTicker == null || !trimTicker.isAlive()) {
|
|
||||||
trimTicker = new Looper() {
|
|
||||||
private final Supplier<Engine> supplier = createSupplier();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long loop() {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
trimAlive.reset();
|
|
||||||
try {
|
|
||||||
Engine engine = supplier.get();
|
|
||||||
if (engine != null) {
|
|
||||||
engine.getMantle().trim(tectonicLimit.get() / lastUse.size());
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
Iris.info(C.RED + "EngineSVC: Failed to trim.");
|
|
||||||
e.printStackTrace();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = lastUse.size();
|
|
||||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
|
||||||
if (time <= 0)
|
|
||||||
return 0;
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
trimTicker.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void UnloadLogic() {
|
|
||||||
if (unloadTicker == null || !unloadTicker.isAlive()) {
|
|
||||||
unloadTicker = new Looper() {
|
|
||||||
private final Supplier<Engine> supplier = createSupplier();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected long loop() {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
unloadAlive.reset();
|
|
||||||
try {
|
|
||||||
Engine engine = supplier.get();
|
|
||||||
if (engine != null) {
|
|
||||||
long unloadStart = System.currentTimeMillis();
|
|
||||||
int count = engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / lastUse.size());
|
|
||||||
if (count > 0) {
|
|
||||||
Iris.debug(C.GOLD + "Unloaded " + C.YELLOW + count + " TectonicPlates in " + C.RED + Form.duration(System.currentTimeMillis() - unloadStart, 2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.reportError(e);
|
|
||||||
Iris.info(C.RED + "EngineSVC: Failed to unload.");
|
|
||||||
e.printStackTrace();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = lastUse.size();
|
|
||||||
long time = (size > 0 ? 1000 / size : 1000) - (System.currentTimeMillis() - start);
|
|
||||||
if (time <= 0)
|
|
||||||
return 0;
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
unloadTicker.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Supplier<Engine> createSupplier() {
|
|
||||||
AtomicInteger i = new AtomicInteger();
|
|
||||||
return () -> {
|
|
||||||
List<World> worlds = Bukkit.getWorlds();
|
|
||||||
if (i.get() >= worlds.size()) {
|
|
||||||
i.set(0);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
for (int j = 0; j < worlds.size(); j++) {
|
|
||||||
World world = worlds.get(i.getAndIncrement());
|
|
||||||
PlatformChunkGenerator generator = IrisToolbelt.access(world);
|
|
||||||
if (i.get() >= worlds.size()) {
|
|
||||||
i.set(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (generator != null) {
|
|
||||||
Engine engine = generator.getEngine();
|
|
||||||
boolean closed = engine.getMantle().getData().isClosed();
|
|
||||||
if (engine != null && !engine.isStudio() && !closed) {
|
|
||||||
lastUseLock.lock();
|
|
||||||
lastUse.put(world, System.currentTimeMillis());
|
|
||||||
lastUseLock.unlock();
|
|
||||||
return engine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Iris.info(C.RED + "EngineSVC: Failed to create supplier.");
|
|
||||||
e.printStackTrace();
|
|
||||||
Iris.reportError(e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDisable() {
|
|
||||||
cacheTicker.interrupt();
|
|
||||||
trimTicker.interrupt();
|
|
||||||
unloadTicker.interrupt();
|
|
||||||
lastUse.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -43,6 +43,7 @@ import com.volmit.iris.util.format.C;
|
|||||||
import com.volmit.iris.util.format.Form;
|
import com.volmit.iris.util.format.Form;
|
||||||
import com.volmit.iris.util.hunk.Hunk;
|
import com.volmit.iris.util.hunk.Hunk;
|
||||||
import com.volmit.iris.util.io.IO;
|
import com.volmit.iris.util.io.IO;
|
||||||
|
import com.volmit.iris.util.io.JarScanner;
|
||||||
import com.volmit.iris.util.mantle.MantleFlag;
|
import com.volmit.iris.util.mantle.MantleFlag;
|
||||||
import com.volmit.iris.util.math.M;
|
import com.volmit.iris.util.math.M;
|
||||||
import com.volmit.iris.util.math.RNG;
|
import com.volmit.iris.util.math.RNG;
|
||||||
@@ -60,7 +61,11 @@ import org.bukkit.command.CommandSender;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@@ -71,6 +76,8 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
@EqualsAndHashCode(exclude = "context")
|
@EqualsAndHashCode(exclude = "context")
|
||||||
@ToString(exclude = "context")
|
@ToString(exclude = "context")
|
||||||
public class IrisEngine implements Engine {
|
public class IrisEngine implements Engine {
|
||||||
|
private static final Map<Class<? extends IrisEngineService>, Constructor<? extends IrisEngineService>> SERVICES = scanServices();
|
||||||
|
private final KMap<Class<? extends IrisEngineService>, IrisEngineService> services;
|
||||||
private final AtomicInteger bud;
|
private final AtomicInteger bud;
|
||||||
private final AtomicInteger buds;
|
private final AtomicInteger buds;
|
||||||
private final AtomicInteger generated;
|
private final AtomicInteger generated;
|
||||||
@@ -111,6 +118,7 @@ public class IrisEngine implements Engine {
|
|||||||
getEngineData();
|
getEngineData();
|
||||||
verifySeed();
|
verifySeed();
|
||||||
this.seedManager = new SeedManager(target.getWorld().getRawWorldSeed());
|
this.seedManager = new SeedManager(target.getWorld().getRawWorldSeed());
|
||||||
|
services = new KMap<>();
|
||||||
bud = new AtomicInteger(0);
|
bud = new AtomicInteger(0);
|
||||||
buds = new AtomicInteger(0);
|
buds = new AtomicInteger(0);
|
||||||
metrics = new EngineMetrics(32);
|
metrics = new EngineMetrics(32);
|
||||||
@@ -137,6 +145,26 @@ public class IrisEngine implements Engine {
|
|||||||
Iris.debug("Engine Initialized " + getCacheID());
|
Iris.debug("Engine Initialized " + getCacheID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Map<Class<? extends IrisEngineService>, Constructor<? extends IrisEngineService>> scanServices() {
|
||||||
|
JarScanner js = new JarScanner(Iris.instance.getJarFile(), "com.volmit.iris.engine.service");
|
||||||
|
J.attempt(js::scan);
|
||||||
|
KMap<Class<? extends IrisEngineService>, Constructor<? extends IrisEngineService>> map = new KMap<>();
|
||||||
|
js.getClasses()
|
||||||
|
.stream()
|
||||||
|
.filter(IrisEngineService.class::isAssignableFrom)
|
||||||
|
.map(c -> (Class<? extends IrisEngineService>) c)
|
||||||
|
.forEach(c -> {
|
||||||
|
try {
|
||||||
|
map.put(c, c.getConstructor(Engine.class));
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
Iris.warn("Failed to load service " + c.getName() + " due to missing constructor");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Collections.unmodifiableMap(map);
|
||||||
|
}
|
||||||
|
|
||||||
private void verifySeed() {
|
private void verifySeed() {
|
||||||
if (getEngineData().getSeed() != null && getEngineData().getSeed() != target.getWorld().getRawWorldSeed()) {
|
if (getEngineData().getSeed() != null && getEngineData().getSeed() != target.getWorld().getRawWorldSeed()) {
|
||||||
target.getWorld().setRawWorldSeed(getEngineData().getSeed());
|
target.getWorld().setRawWorldSeed(getEngineData().getSeed());
|
||||||
@@ -161,6 +189,8 @@ public class IrisEngine implements Engine {
|
|||||||
execution.close();
|
execution.close();
|
||||||
effects.close();
|
effects.close();
|
||||||
mode.close();
|
mode.close();
|
||||||
|
services.values().forEach(s -> s.onDisable(true));
|
||||||
|
services.values().forEach(Iris.instance::unregisterListener);
|
||||||
|
|
||||||
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
|
J.a(() -> new IrisProject(getData().getDataFolder()).updateWorkspace());
|
||||||
}
|
}
|
||||||
@@ -169,6 +199,24 @@ public class IrisEngine implements Engine {
|
|||||||
try {
|
try {
|
||||||
Iris.debug("Setup Engine " + getCacheID());
|
Iris.debug("Setup Engine " + getCacheID());
|
||||||
cacheId = RNG.r.nextInt();
|
cacheId = RNG.r.nextInt();
|
||||||
|
boolean hotload = true;
|
||||||
|
if (services.isEmpty()) {
|
||||||
|
SERVICES.forEach((s, c) -> {
|
||||||
|
try {
|
||||||
|
services.put(s, c.newInstance(this));
|
||||||
|
} catch (InstantiationException | IllegalAccessException |
|
||||||
|
InvocationTargetException e) {
|
||||||
|
Iris.error("Failed to create service " + s.getName());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hotload = false;
|
||||||
|
}
|
||||||
|
for (var service : services.values()) {
|
||||||
|
service.onEnable(hotload);
|
||||||
|
Iris.instance.registerListener(service);
|
||||||
|
}
|
||||||
|
|
||||||
worldManager = new IrisWorldManager(this);
|
worldManager = new IrisWorldManager(this);
|
||||||
complex = new IrisComplex(this);
|
complex = new IrisComplex(this);
|
||||||
execution = new IrisExecutionEnvironment(this);
|
execution = new IrisExecutionEnvironment(this);
|
||||||
@@ -418,6 +466,7 @@ public class IrisEngine implements Engine {
|
|||||||
PregeneratorJob.shutdownInstance();
|
PregeneratorJob.shutdownInstance();
|
||||||
closed = true;
|
closed = true;
|
||||||
J.car(art);
|
J.car(art);
|
||||||
|
services.values().forEach(s -> s.onDisable(false));
|
||||||
getWorldManager().close();
|
getWorldManager().close();
|
||||||
getTarget().close();
|
getTarget().close();
|
||||||
saveEngineData();
|
saveEngineData();
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.volmit.iris.engine.object;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public abstract class IrisEngineService implements Listener {
|
||||||
|
protected final Engine engine;
|
||||||
|
|
||||||
|
public abstract void onEnable(boolean hotload);
|
||||||
|
|
||||||
|
public abstract void onDisable(boolean hotload);
|
||||||
|
|
||||||
|
public final void postShutdown(Runnable r) {
|
||||||
|
Iris.instance.postShutdown(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.volmit.iris.engine.service;
|
||||||
|
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.object.IrisEngineService;
|
||||||
|
import com.volmit.iris.util.collection.KList;
|
||||||
|
|
||||||
|
public class EngineStatusSVC extends IrisEngineService {
|
||||||
|
private static final KList<EngineStatusSVC> INSTANCES = new KList<>();
|
||||||
|
|
||||||
|
public EngineStatusSVC(Engine engine) {
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable(boolean hotload) {
|
||||||
|
if (hotload) return;
|
||||||
|
synchronized (INSTANCES) {
|
||||||
|
INSTANCES.add(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable(boolean hotload) {
|
||||||
|
if (hotload) return;
|
||||||
|
|
||||||
|
synchronized (INSTANCES) {
|
||||||
|
INSTANCES.remove(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getEngineCount() {
|
||||||
|
return INSTANCES.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Status getStatus() {
|
||||||
|
synchronized (INSTANCES) {
|
||||||
|
long loadedChunks = 0;
|
||||||
|
long tectonicPlates = 0;
|
||||||
|
long activeTectonicPlates = 0;
|
||||||
|
long queuedTectonicPlates = 0;
|
||||||
|
long minTectonicUnloadDuration = Long.MAX_VALUE;
|
||||||
|
long maxTectonicUnloadDuration = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
for (var service : INSTANCES) {
|
||||||
|
var world = service.engine.getWorld();
|
||||||
|
if (world.hasRealWorld())
|
||||||
|
loadedChunks += world.realWorld().getLoadedChunks().length;
|
||||||
|
|
||||||
|
tectonicPlates += service.engine.getMantle().getLoadedRegionCount();
|
||||||
|
activeTectonicPlates += service.engine.getMantle().getNotQueuedLoadedRegions();
|
||||||
|
queuedTectonicPlates += service.engine.getMantle().getToUnload();
|
||||||
|
minTectonicUnloadDuration = Math.min(minTectonicUnloadDuration, (long) service.engine.getMantle().getTectonicDuration());
|
||||||
|
maxTectonicUnloadDuration = Math.max(maxTectonicUnloadDuration, (long) service.engine.getMantle().getTectonicDuration());
|
||||||
|
}
|
||||||
|
return new Status(INSTANCES.size(), loadedChunks, MantleCleanerSVC.getTectonicLimit(), tectonicPlates, activeTectonicPlates, queuedTectonicPlates, minTectonicUnloadDuration, maxTectonicUnloadDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record Status(int engineCount, long loadedChunks, int tectonicLimit,
|
||||||
|
long tectonicPlates, long activeTectonicPlates,
|
||||||
|
long queuedTectonicPlates,
|
||||||
|
long minTectonicUnloadDuration,
|
||||||
|
long maxTectonicUnloadDuration) {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package com.volmit.iris.engine.service;
|
||||||
|
|
||||||
|
import com.volmit.iris.Iris;
|
||||||
|
import com.volmit.iris.core.IrisSettings;
|
||||||
|
import com.volmit.iris.engine.framework.Engine;
|
||||||
|
import com.volmit.iris.engine.object.IrisEngineService;
|
||||||
|
import com.volmit.iris.util.format.C;
|
||||||
|
import com.volmit.iris.util.math.M;
|
||||||
|
import com.volmit.iris.util.misc.getHardware;
|
||||||
|
import com.volmit.iris.util.scheduling.Looper;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.LongSupplier;
|
||||||
|
|
||||||
|
public class MantleCleanerSVC extends IrisEngineService {
|
||||||
|
private static final AtomicInteger tectonicLimit = new AtomicInteger(30);
|
||||||
|
|
||||||
|
static {
|
||||||
|
// todo: Redo this
|
||||||
|
tectonicLimit.set(2);
|
||||||
|
long t = getHardware.getProcessMemory();
|
||||||
|
while (t > 200) {
|
||||||
|
tectonicLimit.incrementAndGet();
|
||||||
|
t = t - 200;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Ticker trimmer;
|
||||||
|
private Ticker unloader;
|
||||||
|
|
||||||
|
public MantleCleanerSVC(Engine engine) {
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getTectonicLimit() {
|
||||||
|
return tectonicLimit.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Ticker createTrimmer(Engine engine) {
|
||||||
|
return new Ticker(() -> {
|
||||||
|
if (engine.isClosed()) return -1;
|
||||||
|
long start = M.ms();
|
||||||
|
try {
|
||||||
|
engine.getMantle().trim(tectonicLimit.get() / getEngineCount());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.debug(C.RED + "Mantle: Failed to trim.");
|
||||||
|
Iris.reportError(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine.isClosed()) return -1;
|
||||||
|
int size = getEngineCount();
|
||||||
|
return Math.max(1000 / size - (M.ms() - start), 0);
|
||||||
|
}, "Iris Mantle Trimmer - " + engine.getWorld().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Ticker createUnloader(Engine engine) {
|
||||||
|
return new Ticker(() -> {
|
||||||
|
if (engine.isClosed()) return -1;
|
||||||
|
long start = M.ms();
|
||||||
|
try {
|
||||||
|
engine.getMantle().unloadTectonicPlate(tectonicLimit.get() / getEngineCount());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.debug(C.RED + "Mantle: Failed to unload.");
|
||||||
|
Iris.reportError(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (engine.isClosed()) return -1;
|
||||||
|
int size = getEngineCount();
|
||||||
|
return Math.max(1000 / size - (M.ms() - start), 0);
|
||||||
|
}, "Iris Mantle Unloader - " + engine.getWorld().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getEngineCount() {
|
||||||
|
return Math.max(EngineStatusSVC.getEngineCount(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEnable(boolean hotload) {
|
||||||
|
if (engine.isStudio() && !IrisSettings.get().getPerformance().trimMantleInStudio)
|
||||||
|
return;
|
||||||
|
if (trimmer == null || !trimmer.isAlive())
|
||||||
|
trimmer = createTrimmer(engine);
|
||||||
|
if (unloader == null || !unloader.isAlive())
|
||||||
|
unloader = createUnloader(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable(boolean hotload) {
|
||||||
|
if (hotload) return;
|
||||||
|
if (trimmer != null) trimmer.await();
|
||||||
|
if (unloader != null) unloader.await();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Ticker extends Looper {
|
||||||
|
private final LongSupplier supplier;
|
||||||
|
|
||||||
|
private Ticker(LongSupplier supplier, String name) {
|
||||||
|
this.supplier = supplier;
|
||||||
|
setPriority(Thread.MIN_PRIORITY);
|
||||||
|
setName(name);
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected long loop() {
|
||||||
|
try {
|
||||||
|
return supplier.getAsLong();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Iris.error("Exception in Looper " + getName());
|
||||||
|
StringWriter sw = new StringWriter();
|
||||||
|
e.printStackTrace(new PrintWriter(sw));
|
||||||
|
Iris.error(sw.toString());
|
||||||
|
return 3000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SneakyThrows
|
||||||
|
public void await() {
|
||||||
|
join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user