diff --git a/src/de/steamwar/bungeecore/ServerStarter.java b/src/de/steamwar/bungeecore/ServerStarter.java index a2b4ea84..cee75e64 100644 --- a/src/de/steamwar/bungeecore/ServerStarter.java +++ b/src/de/steamwar/bungeecore/ServerStarter.java @@ -1,12 +1,14 @@ package de.steamwar.bungeecore; +import de.steamwar.messages.ChatSender; import de.steamwar.sql.EventFight; import de.steamwar.sql.SteamwarUser; import de.steamwar.sql.Team; import de.steamwar.sql.Tutorial; -import de.steamwar.messages.ChatSender; +import lombok.Getter; import net.md_5.bungee.api.connection.ProxiedPlayer; +import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; @@ -14,6 +16,7 @@ import java.nio.file.Files; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.function.BooleanSupplier; +import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -46,7 +49,9 @@ public class ServerStarter { private boolean allowMerge = false; private String fightMap = null; private String gameMode = null; - private ServerConstructor constructor = (serverName, port, builder, shutdownCallback) -> new Arenaserver(serverName, gameMode, fightMap, allowMerge, port, builder, shutdownCallback); + private boolean checkpoint = false; + private ServerConstructor constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Arenaserver(serverName, gameMode, fightMap, allowMerge, port, builder, shutdownCallback); + private Consumer callback = subserver -> {}; private final Set playersToSend = new HashSet<>(); private final Map arguments = new HashMap<>(); @@ -117,6 +122,7 @@ public class ServerStarter { serverJar = version.getServerJar(); worldDir = version.getWorldFolder(WORLDS_BASE_PATH); worldName = version != Version.SPIGOT_12 ? String.valueOf(SteamwarUser.get(owner).getId()) : owner.toString(); + checkpoint = true; build(owner); @@ -128,12 +134,11 @@ public class ServerStarter { // Send players to existing server startCondition = () -> { - for(Subserver subserver : Subserver.getServerList()) { - if(subserver.getType() == Servertype.BAUSERVER && ((Bauserver)subserver).getOwner().equals(owner)) { - for(ProxiedPlayer p : playersToSend) - SubserverSystem.sendPlayer(subserver, p); - return false; - } + Bauserver subserver = Bauserver.get(owner); + if(subserver != null) { + for(ProxiedPlayer p : playersToSend) + SubserverSystem.sendPlayer(subserver, p); + return false; } return true; }; @@ -163,19 +168,16 @@ public class ServerStarter { if(startingBau(owner)) return false; - for (Subserver subserver : Subserver.getServerList()) { - if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(owner.getUniqueId()) && subserver.hasStarted()) { - subserver.stop(); - break; - } - } + Bauserver subserver = Bauserver.get(owner.getUniqueId()); + if(subserver != null && subserver.isStarted()) + subserver.stop(); return !startingBau(owner); }; } private void build(UUID owner) { - constructor = (serverName, port, builder, shutdownCallback) -> new Bauserver(serverName, owner, port, builder, shutdownCallback); + constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Bauserver(serverName, owner, port, builder, shutdownCallback, failureCallback); serverNameProvider = port -> bauServerName(SteamwarUser.get(owner)); } @@ -185,16 +187,16 @@ public class ServerStarter { worldDir = version.getWorldFolder(BUILDER_BASE_PATH); worldName = map; serverNameProvider = port -> "⛏" + map; - constructor = (serverName, port, builder, shutdownCallback) -> new Builderserver(serverName, worldName, port, builder, shutdownCallback); + checkpoint = true; + constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Builderserver(serverName, worldName, port, builder, shutdownCallback, failureCallback); // Send players to existing server startCondition = () -> { - for(Subserver subserver : Subserver.getServerList()) { - if(subserver.getType() == Servertype.BUILDER && ((Builderserver)subserver).getMap().equals(worldName)) { - for(ProxiedPlayer p : playersToSend) - SubserverSystem.sendPlayer(subserver, p); - return false; - } + Builderserver subserver = Builderserver.get(worldName); + if(subserver != null) { + for(ProxiedPlayer p : playersToSend) + SubserverSystem.sendPlayer(subserver, p); + return false; } return true; }; @@ -218,9 +220,14 @@ public class ServerStarter { return this; } - public Subserver start() { + public ServerStarter callback(Consumer callback) { + this.callback = callback; + return this; + } + + public boolean start() { if(!startCondition.getAsBoolean()) - return null; + return false; int port = portrange.freePort(); String serverName = serverNameProvider.apply(port); @@ -231,7 +238,7 @@ public class ServerStarter { for (ProxiedPlayer p : playersToSend) ChatSender.of(p).system("SERVER_START_OVERLOAD"); - return null; + return false; } } if(worldName == null) @@ -240,22 +247,45 @@ public class ServerStarter { worldSetup.run(); arguments.put("logPath", worldName); - Subserver subserver = constructor.construct(serverName, port, node.startServer( - serverJar, directory, worldDir, worldName, port, xmx, arguments.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).toArray(String[]::new) - ), worldCleanup); + File checkpointDir = new File("/tmp/" + System.getProperty("user.name") + ".checkpoints/" + directory.getName() + "/" + worldName); + if(checkpoint) + arguments.put("checkpoint", checkpointDir.getPath()); + if(checkpoint && checkpointDir.exists()) { + try { + new DataOutputStream(Files.newOutputStream(new File(checkpointDir, "port").toPath())).writeInt(port); + } catch (IOException e) { + throw new SecurityException(e); + } + + postStart(constructor.construct(serverName, port, node.prepareExecution( + "criu", "restore", "-D", checkpointDir.getPath(), "--auto-dedup", "--shell-job", "-v" + ), worldCleanup, e -> regularStart(serverName, port))); + } else { + regularStart(serverName, port); + } + + return true; + } + + private void regularStart(String serverName, int port) { + postStart(constructor.construct(serverName, port, node.startServer( + serverJar, directory, worldDir, worldName, port, xmx, arguments.entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).toArray(String[]::new) + ), worldCleanup, null)); + } + + private void postStart(Subserver subserver) { for(ProxiedPlayer p : playersToSend) SubserverSystem.sendPlayer(subserver, p); - return subserver; + callback.accept(subserver); } private static boolean startingBau(ProxiedPlayer p) { - for (Subserver subserver : Subserver.getServerList()) { - if (subserver.getType() == Servertype.BAUSERVER && ((Bauserver) subserver).getOwner().equals(p.getUniqueId()) && !subserver.hasStarted()) { - Message.send("BAU_START_ALREADY", p); - return true; - } + Bauserver subserver = Bauserver.get(p.getUniqueId()); + if(subserver != null && !subserver.isStarted()) { + ChatSender.of(p).system("BAU_START_ALREADY"); + return true; } return false; } @@ -273,7 +303,7 @@ public class ServerStarter { } private interface ServerConstructor { - Subserver construct(String serverName, int port, ProcessBuilder builder, Runnable shutdownCallback); + Subserver construct(String serverName, int port, ProcessBuilder builder, Runnable shutdownCallback, Consumer failureCallback); } private static class Portrange { @@ -312,6 +342,7 @@ public class ServerStarter { } } + @Getter public enum Version { SPIGOT_12("spigot-1.12.2.jar", 12), SPIGOT_15("spigot-1.15.2.jar", 15), @@ -326,14 +357,6 @@ public class ServerStarter { this.versionSuffix = versionSuffix; } - public String getServerJar() { - return serverJar; - } - - public int getVersionSuffix() { - return versionSuffix; - } - public String getWorldFolder(String base) { return base + versionSuffix + "/"; }