diff --git a/core/src/main/java/art/arcane/iris/core/service/StudioSVC.java b/core/src/main/java/art/arcane/iris/core/service/StudioSVC.java index c8a4937b1..3ac1cc042 100644 --- a/core/src/main/java/art/arcane/iris/core/service/StudioSVC.java +++ b/core/src/main/java/art/arcane/iris/core/service/StudioSVC.java @@ -29,6 +29,7 @@ import art.arcane.iris.core.project.IrisProject; import art.arcane.iris.core.tools.IrisToolbelt; import art.arcane.iris.engine.data.cache.AtomicCache; import art.arcane.iris.engine.object.IrisDimension; +import art.arcane.iris.engine.platform.PlatformChunkGenerator; import art.arcane.volmlib.util.collection.KMap; import art.arcane.volmlib.util.exceptions.IrisException; import art.arcane.volmlib.util.format.Form; @@ -76,12 +77,19 @@ public class StudioSVC implements IrisService { @Override public void onDisable() { Iris.debug("Studio Mode Active: Closing Projects"); + boolean stopping = IrisToolbelt.isServerStopping(); for (World i : Bukkit.getWorlds()) { if (IrisToolbelt.isIrisWorld(i)) { if (IrisToolbelt.isStudio(i)) { - IrisToolbelt.evacuate(i); - IrisToolbelt.access(i).close(); + PlatformChunkGenerator generator = IrisToolbelt.access(i); + if (!stopping) { + IrisToolbelt.evacuate(i); + } + + if (generator != null) { + generator.close(); + } } } } diff --git a/core/src/main/java/art/arcane/iris/core/tools/IrisToolbelt.java b/core/src/main/java/art/arcane/iris/core/tools/IrisToolbelt.java index 098979a8f..99b7117dd 100644 --- a/core/src/main/java/art/arcane/iris/core/tools/IrisToolbelt.java +++ b/core/src/main/java/art/arcane/iris/core/tools/IrisToolbelt.java @@ -42,6 +42,8 @@ import org.jetbrains.annotations.ApiStatus; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -57,6 +59,7 @@ public class IrisToolbelt { public static Map toolbeltConfiguration = new HashMap<>(); private static final Map worldMaintenanceDepth = new ConcurrentHashMap<>(); private static final Map worldMaintenanceMantleBypassDepth = new ConcurrentHashMap<>(); + private static final Method BUKKIT_IS_STOPPING_METHOD = resolveBukkitIsStoppingMethod(); /** * Will find / download / search for the dimension or return null @@ -267,20 +270,16 @@ public class IrisToolbelt { * @param world the world to evac */ public static boolean evacuate(World world) { + if (world == null || isServerStopping()) { + return false; + } + for (World i : Bukkit.getWorlds()) { if (!i.getName().equals(world.getName())) { - for (Player j : world.getPlayers()) { + for (Player j : new ArrayList<>(world.getPlayers())) { new VolmitSender(j, Iris.instance.getTag()).sendMessage("You have been evacuated from this world."); Location target = i.getSpawnLocation(); - Runnable teleportTask = () -> { - CompletableFuture teleportFuture = PaperLib.teleportAsync(j, target); - if (teleportFuture != null) { - teleportFuture.exceptionally(throwable -> { - Iris.reportError(throwable); - return false; - }); - } - }; + Runnable teleportTask = () -> teleportAsyncSafely(j, target); if (!J.runEntity(j, teleportTask)) { teleportTask.run(); } @@ -301,20 +300,16 @@ public class IrisToolbelt { * @return true if it was evacuated. */ public static boolean evacuate(World world, String m) { + if (world == null || isServerStopping()) { + return false; + } + for (World i : Bukkit.getWorlds()) { if (!i.getName().equals(world.getName())) { - for (Player j : world.getPlayers()) { + for (Player j : new ArrayList<>(world.getPlayers())) { new VolmitSender(j, Iris.instance.getTag()).sendMessage("You have been evacuated from this world. " + m); Location target = i.getSpawnLocation(); - Runnable teleportTask = () -> { - CompletableFuture teleportFuture = PaperLib.teleportAsync(j, target); - if (teleportFuture != null) { - teleportFuture.exceptionally(throwable -> { - Iris.reportError(throwable); - return false; - }); - } - }; + Runnable teleportTask = () -> teleportAsyncSafely(j, target); if (!J.runEntity(j, teleportTask)) { teleportTask.run(); } @@ -327,7 +322,58 @@ public class IrisToolbelt { } public static boolean isStudio(World i) { - return isIrisWorld(i) && access(i).isStudio(); + if (!isIrisWorld(i)) { + return false; + } + + PlatformChunkGenerator generator = access(i); + return generator != null && generator.isStudio(); + } + + private static void teleportAsyncSafely(Player player, Location target) { + if (player == null || target == null || isServerStopping()) { + return; + } + + try { + CompletableFuture teleportFuture = PaperLib.teleportAsync(player, target); + if (teleportFuture != null) { + teleportFuture.exceptionally(throwable -> { + if (!isServerStopping()) { + Iris.reportError(throwable); + } + return false; + }); + } + } catch (Throwable throwable) { + if (!isServerStopping()) { + Iris.reportError(throwable); + } + } + } + + public static boolean isServerStopping() { + Method method = BUKKIT_IS_STOPPING_METHOD; + if (method != null) { + try { + Object value = method.invoke(null); + if (value instanceof Boolean) { + return (Boolean) value; + } + } catch (Throwable ignored) { + } + } + + Iris iris = Iris.instance; + return iris == null || !iris.isEnabled(); + } + + private static Method resolveBukkitIsStoppingMethod() { + try { + return Bukkit.class.getMethod("isStopping"); + } catch (Throwable ignored) { + return null; + } } public static void beginWorldMaintenance(World world, String reason) { diff --git a/core/src/main/kotlin/art/arcane/iris/core/safeguard/Mode.kt b/core/src/main/kotlin/art/arcane/iris/core/safeguard/Mode.kt index 6c3bd6604..f3ec3c395 100644 --- a/core/src/main/kotlin/art/arcane/iris/core/safeguard/Mode.kt +++ b/core/src/main/kotlin/art/arcane/iris/core/safeguard/Mode.kt @@ -59,7 +59,7 @@ enum class Mode(private val color: C) { val info = arrayOf( "", - padd2 + color + " Iris, " + C.AQUA + "Iris, Dimension Engine " + C.RED + "[" + releaseTrain + " RELEASE]", + padd2 + color + " Iris, " + C.AQUA + "Dimension Engine " + C.RED + "[" + releaseTrain + " RELEASE]", padd2 + C.GRAY + " Version: " + color + version, padd2 + C.GRAY + " By: " + color + "Volmit Software (Arcane Arts)", padd2 + C.GRAY + " Server: " + color + serverVersion,