diff --git a/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java b/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java index 2528a6f..caa2bb8 100644 --- a/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java +++ b/SpigotCore_Main/src/com/comphenix/tinyprotocol/TinyProtocol.java @@ -34,7 +34,7 @@ public class TinyProtocol implements Listener { return getServerConnection.get(getMinecraftServer.get(getPlayerList.get(plugin.getServer()))); } private static final Class networkManager = Reflection.getClass("{nms.network}.NetworkManager"); - private static final FieldAccessor getConnections = Reflection.getField(serverConnection, List.class, 0, networkManager); + public static final FieldAccessor networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager); public static final TinyProtocol instance = new TinyProtocol(Core.getInstance()); private static int id = 0; @@ -54,7 +54,7 @@ public class TinyProtocol implements Listener { private TinyProtocol(final Plugin plugin) { this.plugin = plugin; this.handlerName = "tiny-" + plugin.getName() + "-" + ++id; - this.connections = getConnections.get(getServerConnection(plugin)); + this.connections = networkManagers.get(getServerConnection(plugin)); plugin.getServer().getPluginManager().registerEvents(this, plugin); diff --git a/SpigotCore_Main/src/de/steamwar/core/CheckpointUtils.java b/SpigotCore_Main/src/de/steamwar/core/CheckpointUtils.java index 8cedf02..13feea8 100644 --- a/SpigotCore_Main/src/de/steamwar/core/CheckpointUtils.java +++ b/SpigotCore_Main/src/de/steamwar/core/CheckpointUtils.java @@ -25,16 +25,17 @@ import com.viaversion.viaversion.api.Via; import de.steamwar.sql.internal.Statement; import io.netty.channel.ChannelFuture; import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.eclipse.openj9.criu.CRIUSupport; import org.eclipse.openj9.criu.JVMCRIUException; +import sun.misc.Signal; -import java.io.DataInputStream; -import java.io.File; -import java.io.IOException; +import java.io.*; import java.net.InetAddress; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.logging.Level; @@ -43,11 +44,27 @@ import java.util.stream.Stream; public class CheckpointUtils { private CheckpointUtils() {} - private static final Reflection.FieldAccessor channelFutures = Reflection.getField(TinyProtocol.serverConnection, List.class, 0, ChannelFuture.class); - private static final Reflection.MethodInvoker bind = Reflection.getMethod(TinyProtocol.serverConnection, null, InetAddress.class, int.class); + public static void signalHandler() { + Signal.handle(new Signal("USR1"), signal -> Bukkit.getScheduler().runTask(Core.getInstance(), CheckpointUtils::freeze)); + } public static void freeze() { - Path path = FileSystems.getDefault().getPath(System.getProperty("checkpoint")); + String checkpointFile = System.getProperty("checkpoint"); + if(!CRIUSupport.isCheckpointAllowed() || checkpointFile == null) { + Bukkit.shutdown(); + return; + } + + Bukkit.getOnlinePlayers().forEach(player -> player.kickPlayer(null)); + + List networkManagers = TinyProtocol.networkManagers.get(TinyProtocol.getServerConnection(Core.getInstance())); + if(!networkManagers.isEmpty()) { + Core.getInstance().getLogger().log(Level.INFO, "Waiting for players to disconnect for checkpointing"); + Bukkit.getScheduler().runTaskLater(Core.getInstance(), CheckpointUtils::freeze, 1); + return; + } + + Path path = FileSystems.getDefault().getPath(checkpointFile); try { freezeInternal(path); @@ -64,7 +81,10 @@ public class CheckpointUtils { } } + private static final Reflection.FieldAccessor channelFutures = Reflection.getField(TinyProtocol.serverConnection, List.class, 0, ChannelFuture.class); + private static final Reflection.MethodInvoker bind = Reflection.getMethod(TinyProtocol.serverConnection, null, InetAddress.class, int.class); private static void freezeInternal(Path path) throws Exception { + Bukkit.getWorlds().forEach(world -> Arrays.stream(world.getLoadedChunks()).forEach(Chunk::unload)); Statement.closeAll(); // Close socket @@ -87,6 +107,11 @@ public class CheckpointUtils { criu.checkpointJVM(); } catch (JVMCRIUException e) { Bukkit.shutdown(); + + Path logfile = path.resolve("criu.log"); + if(logfile.toFile().exists()) + throw new IllegalStateException("Could not create checkpoint, criu log:\n" + new String(Files.readAllBytes(logfile)), e); + throw e; } diff --git a/SpigotCore_Main/src/de/steamwar/core/Core.java b/SpigotCore_Main/src/de/steamwar/core/Core.java index 7d3a30d..7e0b371 100644 --- a/SpigotCore_Main/src/de/steamwar/core/Core.java +++ b/SpigotCore_Main/src/de/steamwar/core/Core.java @@ -97,6 +97,7 @@ public class Core extends JavaPlugin{ AuthlibInjector.inject(); TinyProtocol.init(); + CheckpointUtils.signalHandler(); new AntiNocom(); if(Core.getVersion() < 17 && Bukkit.getPluginManager().getPlugin("ViaVersion") != null)