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..771ea23 100644 --- a/SpigotCore_Main/src/de/steamwar/core/CheckpointUtils.java +++ b/SpigotCore_Main/src/de/steamwar/core/CheckpointUtils.java @@ -25,8 +25,10 @@ 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; @@ -35,6 +37,7 @@ 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,28 +46,47 @@ 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 freeze() { - Path path = FileSystems.getDefault().getPath(System.getProperty("checkpoint")); - - try { - freezeInternal(path); - } catch (Exception e) { - Bukkit.shutdown(); - throw new SecurityException(e); - } finally { - // Delete checkpoint - try (Stream stream = Files.walk(path)) { - stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); - } catch (IOException e) { - //ignore - } - } + public static void signalHandler() { + Signal.handle(new Signal("USR1"), signal -> Bukkit.getScheduler().runTask(Core.getInstance(), CheckpointUtils::freeze)); } + public static void freeze() { + 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()) { + Path path = FileSystems.getDefault().getPath(checkpointFile); + + try { + freezeInternal(path); + } catch (Exception e) { + Bukkit.shutdown(); + throw new SecurityException(e); + } finally { + // Delete checkpoint + try (Stream stream = Files.walk(path)) { + stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete); + } catch (IOException e) { + //ignore + } + } + return; + } + + Core.getInstance().getLogger().log(Level.INFO, "Waiting for players to disconnect for checkpointing"); + Bukkit.getScheduler().runTaskLater(Core.getInstance(), CheckpointUtils::freeze, 1); + } + + 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 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)