Use signal handler for checkpointing #254
@ -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<List> getConnections = Reflection.getField(serverConnection, List.class, 0, networkManager);
|
||||
public static final FieldAccessor<List> 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);
|
||||
|
||||
|
@ -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<List> 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<Path> 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));
|
||||
Lixfel markierte diese Unterhaltung als gelöst
Veraltet
|
||||
}
|
||||
|
||||
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()) {
|
||||
Lixfel markierte diese Unterhaltung als gelöst
Veraltet
YoyoNow
hat
Könnten wir dieses if bitte umdrehen? Fände ich schöner! Könnten wir dieses if bitte umdrehen? Fände ich schöner!
|
||||
Path path = FileSystems.getDefault().getPath(checkpointFile);
|
||||
|
||||
try {
|
||||
freezeInternal(path);
|
||||
} catch (Exception e) {
|
||||
Bukkit.shutdown();
|
||||
throw new SecurityException(e);
|
||||
} finally {
|
||||
// Delete checkpoint
|
||||
try (Stream<Path> 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<List> 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));
|
||||
Lixfel markierte diese Unterhaltung als gelöst
Veraltet
YoyoNow
hat
Ist Chunk.unload auch gleich ein World save? Also das die Welt gespeichert wird? Ist Chunk.unload auch gleich ein World save? Also das die Welt gespeichert wird?
Lixfel
hat
Ein World save ist async, deswegen nutzen wir den nicht. (Weil wir dann mitten im Speichern checkpointen, nicht unbedingt ideal). Wenn die Chunks entladen werden, werden sie auch gespeichert, d.h. wird defakto die Welt gespeichert. Ein World save ist async, deswegen nutzen wir den nicht. (Weil wir dann mitten im Speichern checkpointen, nicht unbedingt ideal).
Wenn die Chunks entladen werden, werden sie auch gespeichert, d.h. wird defakto die Welt gespeichert.
YoyoNow
hat
Ok gut zu wissen. Ok gut zu wissen.
|
||||
Statement.closeAll();
|
||||
|
||||
// Close socket
|
||||
|
@ -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)
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren
Ich habe das Gefühl, dass das hier einen Fehler werfen kann, alleine, weil es sun.misc.Signal ist vom package.
Warum sollte das Fehler werfen? Ich weiß, dass es deprecated ist und ich eigentlich jdk.internal.Signal nutzen müsste, aber wir haben auch noch Java 8-Server im Betrieb, die müssen das auch können, und da gibt es noch kein jdk.internal. War sun.misc zur Entfernung in einer festgelegten JDK-Version vorgesehen?
Ich glaube aktuell nichts bekannt, aber mit Sicherheit. Ich hatte eher gedacht, dass es in Java-8 nicht existiert deswegen.