diff --git a/SingleServer/pom.xml b/SingleServer/pom.xml new file mode 100644 index 0000000..8a956b8 --- /dev/null +++ b/SingleServer/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + ASys Single server + + + + DmitriyMX + mail@dmiriymx.ru + + + + + UTF-8 + 1.8 + + + asys + singleserver + 0.1 + bundle + + + + asys + api + 0.5 + + + org.osgi + org.osgi.core + 6.0.0 + + + org.apache.felix + org.apache.felix.gogo.runtime + 0.10.0 + + + + + ${project.groupId}.${project.artifactId}-${project.version} + + + org.apache.maven.plugins + maven-compiler-plugin + 3.5.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + org.apache.felix + maven-bundle-plugin + 3.0.1 + true + + + ${project.name} + ${project.groupId}.${project.artifactId} + asys.singleserver.Activator + asys.api, * + + + + + + \ No newline at end of file diff --git a/SingleServer/src/main/java/asys/singleserver/Activator.java b/SingleServer/src/main/java/asys/singleserver/Activator.java new file mode 100644 index 0000000..c75c5da --- /dev/null +++ b/SingleServer/src/main/java/asys/singleserver/Activator.java @@ -0,0 +1,81 @@ +/* + * DmitriyMX + * 2016-08-15 + */ +package asys.singleserver; + +import asys.api.ASysUtils; +import asys.api.BankObject; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.util.tracker.ServiceTracker; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +public class Activator implements BundleActivator { + private ServiceRegistration serviceCmd; + private ServiceRegistration serviceServerManager; + private ServiceTracker bankObjectTracker; + private SingleServer singleServer; + + public void start(BundleContext bundleContext) throws Exception { + singleServer = new SingleServer(loadProperties(ASysUtils.GetProperty(bundleContext, "asys.config.dir", "conf"))); + + bankObjectTracker = new ServiceTracker<>(bundleContext, BankObject.class.getName(), null); + bankObjectTracker.open(); + BankObject bankObject = bankObjectTracker.getService(); + if (bankObject != null) singleServer.loadState(bankObject); + + serviceServerManager = bundleContext.registerService(asys.api.ServerManager.class.getName(), singleServer, null); + serviceCmd = ASysUtils.RegisterCommands(bundleContext, new Commands(singleServer), "asys.server"); + } + + public void stop(BundleContext bundleContext) throws Exception { + serviceCmd.unregister(); + serviceServerManager.unregister(); + + BankObject bankObject = bankObjectTracker.getService(); + if (bankObject != null) singleServer.saveState(bankObject); + + bankObjectTracker.close(); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private Properties loadProperties(String confDir) { + final String propsFileName = "asys-singleserver.properties"; + Path propsPath = Paths.get(confDir).resolve(propsFileName); + if (Files.notExists(propsPath)) { + propsPath.toFile().getParentFile().mkdirs(); + + InputStream resourceAsStream = getClass().getResourceAsStream("/"+propsFileName); + try { + BufferedWriter bw = new BufferedWriter(new FileWriter(propsPath.toFile())); + BufferedReader br = new BufferedReader(new InputStreamReader(resourceAsStream)); + String line; + while ((line = br.readLine()) != null) { + bw.write(line); + bw.write("\n"); + } + bw.flush(); + bw.close(); + br.close(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + Properties properties = new Properties(); + try { + properties.load(new java.io.FileReader(propsPath.toFile())); + return properties; + } catch (IOException e) { + e.printStackTrace(); } + return null; + } +} diff --git a/SingleServer/src/main/java/asys/singleserver/Commands.java b/SingleServer/src/main/java/asys/singleserver/Commands.java new file mode 100644 index 0000000..9593c55 --- /dev/null +++ b/SingleServer/src/main/java/asys/singleserver/Commands.java @@ -0,0 +1,65 @@ +/* + * DmitriyMX + * 2016-08-15 + */ +package asys.singleserver; + +import asys.api.Command; +import asys.api.MinecraftServer; +import asys.api.ServerManager; +import org.apache.felix.service.command.Descriptor; + +import java.util.Arrays; +import java.util.StringJoiner; + +public class Commands { + private ServerManager serverManager; + + public Commands(ServerManager serverManager) { + this.serverManager = serverManager; + } + + @Command + @Descriptor("Server start") + public void start() { + serverManager.getServer().start(); + } + + @Command + @Descriptor("Server stop") + public void stop() { + serverManager.getServer().stop(); + serverManager.removeServer(); + } + + @Command + @Descriptor("Server force stop") + public void forceStop() { + serverManager.getServer().forceStop(); + serverManager.removeServer(); + } + + @Command + @Descriptor("Server restart") + public void restart() { + MinecraftServer server = serverManager.getServer(); + server.stop(); + server.start(); + } + + @Command + @Descriptor("Server is alive?") + public void isAlive() { + boolean alive = serverManager.getServer().isAlive(); + System.out.println(String.format("Server is alive: %s", alive)); + } + + @Command + @Descriptor("Send server command") + public void cmd(String... command) { + StringJoiner sj = new StringJoiner(" "); + Arrays.asList(command).forEach(sj::add); + serverManager.getServer().sendCommand(sj.toString()); + } +} + diff --git a/SingleServer/src/main/java/asys/singleserver/SingleServer.java b/SingleServer/src/main/java/asys/singleserver/SingleServer.java new file mode 100644 index 0000000..4f29a5a --- /dev/null +++ b/SingleServer/src/main/java/asys/singleserver/SingleServer.java @@ -0,0 +1,70 @@ +/* + * DmitriyMX + * 2016-08-15 + */ +package asys.singleserver; + +import asys.api.BankObject; +import asys.api.MinecraftServer; +import asys.api.ServerManager; +import asys.singleserver.mcserver.MinecraftProcessServer; +import asys.singleserver.mcserver.MinecraftScreenServer; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +public class SingleServer + implements ServerManager +{ + private MinecraftServer mcServer; + private Properties configuration; + + public SingleServer(Properties configuration) + { + this.configuration = configuration; + } + + private void prepareMcServer() { + Path serverPath = Paths.get(configuration.getProperty("server.path")); + if (Files.notExists(serverPath)) { + throw new RuntimeException(String.format("Server path \"%s\" not found", serverPath.toString())); + } + + Path mainJar = serverPath.resolve(configuration.getProperty("server.mainjar")); + if (Files.notExists(mainJar)) { + throw new RuntimeException(String.format("Main jar \"%s\" not found", mainJar.toString())); + } + + if (configuration.getProperty("server.implement", "process").equalsIgnoreCase("screen")) { + System.out.println("McServer implements: screen"); + mcServer = new MinecraftScreenServer("mcserver", serverPath.toFile(), mainJar.toString(), + configuration.getProperty("server.jvm.args", null), + configuration.getProperty("server.params", null)); + } else { + System.out.println("McServer implements: process"); + mcServer = new MinecraftProcessServer("mcserver", serverPath.toFile(), mainJar.toString(), + configuration.getProperty("server.jvm.args", null), + configuration.getProperty("server.params", null)); + } + } + + public MinecraftServer getServer() { + if (mcServer == null) prepareMcServer(); + return mcServer; + } + + public void removeServer() { + mcServer = null; + } + + public void saveState(BankObject bankObject) { + bankObject.save(SingleServer.class.getName() + "#mcServer", mcServer); + } + + public void loadState(BankObject bankObject) { + mcServer = (MinecraftServer) bankObject.get(SingleServer.class.getName() + "#mcServer"); + } +} + diff --git a/SingleServer/src/main/java/asys/singleserver/mcserver/MinecraftProcessServer.java b/SingleServer/src/main/java/asys/singleserver/mcserver/MinecraftProcessServer.java new file mode 100644 index 0000000..aeaca0d --- /dev/null +++ b/SingleServer/src/main/java/asys/singleserver/mcserver/MinecraftProcessServer.java @@ -0,0 +1,75 @@ +/* + * DmitriyMX + * 2016-08-15 + */ +package asys.singleserver.mcserver; + +import asys.api.MinecraftServer; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Vector; + +public class MinecraftProcessServer implements MinecraftServer { + private ProcessBuilder processBuilder; + private Process process; + private final String name; + private final byte[] newLine = "\n".getBytes(); + + public MinecraftProcessServer(String name, File directory, String mainJar, String jvmArgs, String params) { + processBuilder = new ProcessBuilder(); + processBuilder.directory(directory); + List commandLine = new Vector<>(); + commandLine.add("java"); + commandLine.add("-Dasys.server.name=\"" + name + "\""); + if ((jvmArgs != null) && (!jvmArgs.trim().isEmpty())) { + commandLine.add(jvmArgs); + } + commandLine.add("-jar"); + commandLine.add(mainJar); + if ((params != null) && (!params.trim().isEmpty())) { + commandLine.add(params); + } + processBuilder.command(commandLine); + + this.name = name; + } + + public String getName() { + return name; + } + + public void start() { + try { + process = processBuilder.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void stop() { + sendCommand("stop"); + } + + public void forceStop() { + process.destroy(); + process = null; + } + + public void sendCommand(String command) { + if (!isAlive()) return; + try { + process.getOutputStream().write(command.getBytes()); + process.getOutputStream().write(newLine); + process.getOutputStream().flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public boolean isAlive() { + return process != null && process.isAlive(); + } +} + diff --git a/SingleServer/src/main/java/asys/singleserver/mcserver/MinecraftScreenServer.java b/SingleServer/src/main/java/asys/singleserver/mcserver/MinecraftScreenServer.java new file mode 100644 index 0000000..f190be8 --- /dev/null +++ b/SingleServer/src/main/java/asys/singleserver/mcserver/MinecraftScreenServer.java @@ -0,0 +1,99 @@ +/* + * DmitriyMX + * 2016-08-15 + */ +package asys.singleserver.mcserver; + +import asys.api.MinecraftServer; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.List; +import java.util.Vector; + +public class MinecraftScreenServer implements MinecraftServer { + private ProcessBuilder startPB; + private String name; + private String serverPID; + + public MinecraftScreenServer(String name, File directory, String mainJar, String jvmArgs, String params) { + startPB = new ProcessBuilder(); + startPB.directory(directory); + List commandLine = new Vector<>(Arrays.asList(new String[] { "screen", "-dmS", name, "java", "-Dasys.server.name=\"" + name + "\"" })); + if ((jvmArgs != null) && (!jvmArgs.trim().isEmpty())) { + commandLine.add(jvmArgs); + } + commandLine.add("-jar"); + commandLine.add(mainJar); + if ((params != null) && (!params.trim().isEmpty())) { + commandLine.add(params); + } + this.startPB.command(commandLine); + this.name = name; + } + + public String getName() { + return name; + } + + public void start() { + try { + startPB.start(); + + Thread.sleep(500L); + + Process process = Runtime.getRuntime().exec("bash -c \"screen -ls | sed 's/\\t//g' | grep '\\." + name + "(' | cut -f1 -d.\""); + BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); + String out = br.readLine(); + br.close(); + + process = Runtime.getRuntime().exec("pgrep -P " + out); + br = new BufferedReader(new InputStreamReader(process.getInputStream())); + serverPID = br.readLine(); + br.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void stop() { + sendCommand("stop"); + } + + public void forceStop() { + ProcessBuilder sendCmdPB = new ProcessBuilder(); + sendCmdPB.command("screen", "-S", this.name, "-X", "quit"); + try { + sendCmdPB.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void sendCommand(String command) { + ProcessBuilder sendCmdPB = new ProcessBuilder(); + sendCmdPB.command("screen", "-S", this.name, "-p", "0", "-X", "stuff", command + "\\r"); + try { + sendCmdPB.start(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public boolean isAlive() { + try { + Process process = Runtime.getRuntime().exec("ps -p " + this.serverPID); + BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream())); + br.readLine(); + String line = br.readLine(); + br.close(); + return line != null; + } catch (Exception e) { + e.printStackTrace(); } + return false; + } +} + diff --git a/SingleServer/src/main/resources/asys-singleserver.properties b/SingleServer/src/main/resources/asys-singleserver.properties new file mode 100644 index 0000000..866c22a --- /dev/null +++ b/SingleServer/src/main/resources/asys-singleserver.properties @@ -0,0 +1,6 @@ +server.path=server +server.mainjar=spigot.jar +server.params= +server.jvm.args= +# process, screen +server.implement=process \ No newline at end of file