Compare commits

...

6 commits
master ... dev

12 changed files with 390 additions and 278 deletions

2
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
# Default ignored files
/workspace.xml

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: jline:jline:2.12" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.21" level="project" />
</component>
</module>

View file

@ -6,11 +6,12 @@
<sourceOutputDir name="target/generated-sources/annotations" /> <sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" /> <outputRelativeToContentRoot value="true" />
<module name="ButtonServerRunner" /> <module name="MCServerRunner" />
</profile> </profile>
</annotationProcessing> </annotationProcessing>
<bytecodeTargetLevel> <bytecodeTargetLevel>
<module name="ButtonServerRunner" target="1.8" /> <module name="ButtonServerRunner" target="1.8" />
<module name="MCServerRunner" target="1.8" />
</bytecodeTargetLevel> </bytecodeTargetLevel>
</component> </component>
</project> </project>

4
.idea/encodings.xml Normal file
View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View file

@ -0,0 +1,11 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="WeakerAccess" enabled="true" level="WARNING" enabled_by_default="true">
<option name="SUGGEST_PACKAGE_LOCAL_FOR_MEMBERS" value="false" />
<option name="SUGGEST_PACKAGE_LOCAL_FOR_TOP_CLASSES" value="false" />
<option name="SUGGEST_PRIVATE_FOR_INNERS" value="false" />
<disabledExtension id="moduleInfo" />
</inspection_tool>
</profile>
</component>

View file

@ -0,0 +1,13 @@
<component name="libraryTable">
<library name="Maven: org.yaml:snakeyaml:1.21">
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.21/snakeyaml-1.21.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.21/snakeyaml-1.21-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/yaml/snakeyaml/1.21/snakeyaml-1.21-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,3 @@
<component name="MarkdownNavigator.ProfileManager">
<settings default="" pdf-export="" />
</component>

8
.idea/modules.xml Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/MCServerRunner.iml" filepath="$PROJECT_DIR$/MCServerRunner.iml" />
</modules>
</component>
</project>

15
MCServerRunner.iml Normal file
View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: jline:jline:2.12" level="project" />
<orderEntry type="library" name="Maven: org.yaml:snakeyaml:1.21" level="project" />
</component>
</module>

117
pom.xml
View file

@ -1,59 +1,60 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>ButtonDevTeam</groupId> <groupId>ButtonDevTeam</groupId>
<artifactId>MCServerRunner</artifactId> <artifactId>MCServerRunner</artifactId>
<version>0.0.1-SNAPSHOT</version> <version>0.0.1-SNAPSHOT</version>
<build> <build>
<sourceDirectory>src</sourceDirectory> <sourceDirectory>src</sourceDirectory>
<plugins> <finalName>ServerRunner</finalName>
<plugin> <plugins>
<artifactId>maven-compiler-plugin</artifactId> <plugin>
<version>3.5.1</version> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <version>3.5.1</version>
<source>1.8</source> <configuration>
<target>1.8</target> <source>1.8</source>
</configuration> <target>1.8</target>
</plugin> </configuration>
<plugin> </plugin>
<groupId>org.apache.maven.plugins</groupId> <plugin>
<artifactId>maven-jar-plugin</artifactId> <groupId>org.apache.maven.plugins</groupId>
<version>3.1.0</version> <artifactId>maven-jar-plugin</artifactId>
<configuration> <version>3.1.0</version>
<archive> <configuration>
<manifest> <archive>
<addClasspath>true</addClasspath> <manifest>
<mainClass>buttondevteam.serverrunner.ServerRunner</mainClass> <addClasspath>true</addClasspath>
<classpathPrefix>ServerRunner_lib/</classpathPrefix> <mainClass>buttondevteam.serverrunner.ServerRunner</mainClass>
</manifest> <classpathPrefix>ServerRunner_lib/</classpathPrefix>
</archive> </manifest>
</configuration> </archive>
</plugin> </configuration>
<plugin> </plugin>
<artifactId>maven-shade-plugin</artifactId> <plugin>
<version>3.1.1</version> <artifactId>maven-shade-plugin</artifactId>
<executions> <version>3.1.1</version>
<execution> <executions>
<phase>package</phase> <execution>
<goals> <phase>package</phase>
<goal>shade</goal> <goals>
</goals> <goal>shade</goal>
</execution> </goals>
</executions> </execution>
</plugin> </executions>
</plugins> </plugin>
</build> </plugins>
<dependencies> </build>
<dependency> <dependencies>
<groupId>jline</groupId> <dependency>
<artifactId>jline</artifactId> <groupId>jline</groupId>
<version>2.12</version> <artifactId>jline</artifactId>
</dependency> <version>2.12</version>
<!-- https://mvnrepository.com/artifact/org.yaml/snakeyaml --> </dependency>
<dependency> <!-- https://mvnrepository.com/artifact/org.yaml/snakeyaml -->
<groupId>org.yaml</groupId> <dependency>
<artifactId>snakeyaml</artifactId> <groupId>org.yaml</groupId>
<version>1.21</version> <artifactId>snakeyaml</artifactId>
</dependency> <version>1.21</version>
</dependencies> </dependency>
</dependencies>
</project> </project>

View file

@ -4,6 +4,7 @@ public class Config {
public String serverVersion; public String serverVersion;
public String serverParams; public String serverParams;
public int restartAt; public int restartAt;
public String customJar;
public Config(String serverVersion, String serverParams, int restartAt) { public Config(String serverVersion, String serverParams, int restartAt) {
this.serverVersion = serverVersion; this.serverVersion = serverVersion;
@ -14,5 +15,6 @@ public class Config {
this.serverVersion = "1.12.2"; this.serverVersion = "1.12.2";
this.serverParams = "-Djline.terminal=jline.UnixTerminal -Xms4G -Xmx6G"; this.serverParams = "-Djline.terminal=jline.UnixTerminal -Xms4G -Xmx6G";
this.restartAt = 12; this.restartAt = 12;
this.customJar = "";
} }
} }

View file

@ -1,219 +1,256 @@
package buttondevteam.serverrunner; package buttondevteam.serverrunner;
import jline.console.ConsoleReader; import jline.Terminal;
import jline.console.CursorBuffer; import jline.console.ConsoleReader;
import org.yaml.snakeyaml.Yaml; import jline.console.CursorBuffer;
import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.nio.charset.StandardCharsets; import java.io.*;
import java.nio.file.Files; import java.nio.charset.StandardCharsets;
import java.util.Calendar; import java.nio.file.Files;
import java.util.Collections; import java.util.Calendar;
import java.util.TimeZone; import java.util.Collections;
import java.util.regex.Pattern; import java.util.TimeZone;
import java.util.regex.Pattern;
public class ServerRunner {
private static final int RESTART_MESSAGE_COUNT = 60; public class ServerRunner {
private static final int RESTART_MESSAGE_COUNT = 60;
private static final int interval = 24; // hours
private static final int interval = 24; // hours
private static volatile boolean stop = false;
private static int restartcounter = RESTART_MESSAGE_COUNT; private static volatile boolean stop = false;
private static volatile Process serverprocess; private static int restartcounter = RESTART_MESSAGE_COUNT;
private static volatile PrintWriter serveroutput; private static volatile Process serverprocess;
private static volatile Thread rt; private static volatile PrintWriter serveroutput;
private static volatile ConsoleReader reader; private static volatile Thread rt;
private static volatile PrintWriter runnerout; private static volatile ConsoleReader reader;
private static volatile PrintWriter runnerout;
private static volatile boolean customrestartfailed = false; private static volatile Thread it;
public static void main(String[] args) throws IOException { private static volatile boolean customrestartfailed = false;
Yaml yaml = new Yaml();
File f=new File("plugins/ServerRunner/config.yml"); public static void main(String[] args) throws IOException {
f.getParentFile().mkdirs(); Yaml yaml = new Yaml();
final Config config; File f = new File("plugins/ServerRunner/config.yml");
if(!f.exists()) f.getParentFile().mkdirs();
Files.write(f.toPath(), Collections.singleton(yaml.dump(config = new Config()))); final Config config;
else if (!f.exists())
config=yaml.load(new FileInputStream(f)); Files.write(f.toPath(), Collections.singleton(yaml.dump(config = new Config())));
if (!new File("spigot-" + config.serverVersion + ".jar").exists()) { else
System.out.println("The server JAR for " + config.serverVersion + " cannot be found!"); config = yaml.load(new FileInputStream(f));
return; final File serverJar = new File(config.customJar == null || config.customJar.length() == 0
} ? "spigot-" + config.serverVersion + ".jar"
reader = new ConsoleReader(); : config.customJar);
reader.setPrompt("Runner>"); if (!serverJar.exists()) {
runnerout = new PrintWriter(reader.getOutput()); System.out.println("The server JAR for " + config.serverVersion + " cannot be found!");
writeToScreen("Starting server..."); return;
serverprocess = startServer(config); }
serveroutput = new PrintWriter(serverprocess.getOutputStream()); reader = new ConsoleReader();
rt = Thread.currentThread(); reader.setPrompt("Runner>");
final Thread it = new Thread() { runnerout = new PrintWriter(reader.getOutput());
@Override writeToScreen("Starting server...");
public void run() { serverprocess = startServer(config, serverJar);
try { serveroutput = new PrintWriter(serverprocess.getOutputStream());
String readLine; rt = Thread.currentThread();
while (!stop && (readLine = reader.readLine()) != null) { it = new Thread() {
if (readLine.equalsIgnoreCase("stop")) @Override
ServerRunner.stop(); public void run() {
serveroutput.println(readLine); try {
serveroutput.flush(); String readLine;
} while (!stop || serveroutput.) { //TODO: Store if stream got closed (enum for state)?
} catch (IOException e) { try {
e.printStackTrace(); if ((readLine = reader.readLine()) == null)
} continue; //Keep going until we stop
ServerRunner.stop(); if (readLine.equalsIgnoreCase("stop"))
writeToScreen("Stopped " + Thread.currentThread().getName()); ServerRunner.stop();
} serveroutput.println(readLine);
}; serveroutput.flush();
it.setName("InputThread"); } catch (Exception e) {
it.start(); e.printStackTrace();
final Thread ot = new Thread() { Thread.sleep(100); //Sleep a bit and keep going
@Override }
public void run() { }
try { } catch (InterruptedException e) {
BufferedReader serverinput = new BufferedReader( e.printStackTrace();
new InputStreamReader(serverprocess.getInputStream(), StandardCharsets.UTF_8)); }
String line; ServerRunner.stop();
while (true) { writeToScreen("Stopped " + Thread.currentThread().getName());
if ((line = serverinput.readLine()) != null) { }
writeToScreen(line); };
if (line.contains("FAILED TO BIND TO PORT")) { it.setName("InputThread");
ServerRunner.stop(); it.start();
writeToScreen("A server is already running!"); final Thread ot = new Thread() {
} @Override
if (Pattern.matches( public void run() {
"\\[\\d\\d:\\d\\d:\\d\\d INFO]: Unknown command. Type \"/help\" for help.\\s+", line)) try {
customrestartfailed = true; BufferedReader serverinput = new BufferedReader(
new InputStreamReader(serverprocess.getInputStream(), StandardCharsets.UTF_8));
} else if (!stop) { String line;
try { while (true) {
serverinput.close(); if ((line = serverinput.readLine()) != null) {
} catch (Exception e) { writeToScreen(line);
e.printStackTrace(); if (line.contains("FAILED TO BIND TO PORT")) {
} ServerRunner.stop();
try { writeToScreen("A server is already running!");
serveroutput.close(); }
} catch (Exception e) { if (Pattern.matches(
e.printStackTrace(); "\\[\\d\\d:\\d\\d:\\d\\d INFO]: Unknown command. Type \"/help\" for help.\\s+", line))
} customrestartfailed = true;
writeToScreen("Server stopped! Restarting...");
serverprocess = startServer(config); } else if (!stop) {
serverinput = new BufferedReader(new InputStreamReader(serverprocess.getInputStream())); try {
serveroutput = new PrintWriter(serverprocess.getOutputStream()); serverinput.close();
restartcounter = RESTART_MESSAGE_COUNT; } catch (Exception e) {
} else e.printStackTrace();
break; }
} try {
serverinput.close(); serveroutput.close();
} catch (IOException e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
ServerRunner.stop(); writeToScreen("Server stopped! Restarting...");
writeToScreen("Stopped " + Thread.currentThread().getName()); serverprocess = startServer(config, serverJar);
} serverinput = new BufferedReader(new InputStreamReader(serverprocess.getInputStream()));
}; serveroutput = new PrintWriter(serverprocess.getOutputStream());
ot.setName("OutputThread"); restartcounter = RESTART_MESSAGE_COUNT;
ot.start(); } else
Thread.currentThread().setName("RestarterThread"); break;
long starttime = syncStart(config.restartAt); }
System.out.println("Restart scheduled in " + starttime / 3600000f); serverinput.close();
boolean firstrun = true; } catch (IOException e) {
while (!stop) { e.printStackTrace();
try { }
if (restartcounter >= 0) { ServerRunner.stop();
if (restartcounter == RESTART_MESSAGE_COUNT) { writeToScreen("Stopped " + Thread.currentThread().getName());
if (firstrun) { }
// writeToScreen("Sleeping for " + starttime); };
Thread.sleep(starttime); ot.setName("OutputThread");
firstrun = false; ot.start();
} else final Thread errt = new Thread() {
Thread.sleep(interval * 3600000); @Override
customrestartfailed = false; public void run() {
serveroutput.println("schrestart"); try {
serveroutput.flush(); BufferedReader serverinput = new BufferedReader(
} else if (restartcounter > 0) { new InputStreamReader(serverprocess.getErrorStream(), StandardCharsets.UTF_8));
if (customrestartfailed) { String line;
if (restartcounter % 10 == 0) while (true) {
sendMessage(serveroutput, "red", if ((line = serverinput.readLine()) != null) {
"-- Server restarting in " + restartcounter + " seconds!"); writeToScreen(line);
Thread.sleep(1000); } else if (stop)
} else { break;
restartcounter = RESTART_MESSAGE_COUNT; }
continue; // Don't decrement the counter so it will sleep the full time serverinput.close();
} } catch (IOException e) {
} else { e.printStackTrace();
Thread.sleep(500); }
if (customrestartfailed) { ServerRunner.stop();
writeToScreen("Stopping server for restart..."); writeToScreen("Stopped " + Thread.currentThread().getName());
serveroutput.println("restart"); }
serveroutput.flush(); };
customrestartfailed = false; errt.setName("ErrorThread");
} errt.start();
Thread.sleep(5000); // Don't run needless cycles Thread.currentThread().setName("RestarterThread");
} long starttime = syncStart(config.restartAt);
restartcounter--; writeToScreen("Restart scheduled in " + starttime / 3600000f);
} boolean firstrun = true;
} catch (InterruptedException e) { // The while checks if stop is true and then stops while (!stop) {
} try {
} if (restartcounter >= 0) {
writeToScreen("Stopped " + Thread.currentThread().getName()); if (restartcounter == RESTART_MESSAGE_COUNT) {
} if (firstrun) {
// writeToScreen("Sleeping for " + starttime);
private static Process startServer(Config config) throws IOException { Thread.sleep(starttime);
return Runtime.getRuntime().exec(("java "+config.serverParams+" -jar spigot-" + config.serverVersion + ".jar").split(" ")); firstrun = false;
} } else
Thread.sleep(interval * 3600000);
private static void sendMessage(PrintWriter output, String color, String text) { customrestartfailed = false;
output.println("tellraw @a {\"text\":\"" + text + "\",\"color\":\"" + color + "\"}"); serveroutput.println("schrestart");
output.flush(); serveroutput.flush();
writeToScreen(text); } else if (restartcounter > 0) {
} if (customrestartfailed) {
if (restartcounter % 10 == 0)
private static void stop() { sendMessage(serveroutput, "red",
stop = true; "-- Server restarting in " + restartcounter + " seconds!");
rt.interrupt(); // The restarter thread sleeps for a long time and keeps the program running Thread.sleep(1000);
} } else {
restartcounter = RESTART_MESSAGE_COUNT;
private static void writeToScreen(String line) { continue; // Don't decrement the counter so it will sleep the full time
stashLine(); }
runnerout.println(line); } else {
unstashLine(); Thread.sleep(500);
} if (customrestartfailed) {
writeToScreen("Stopping server for restart...");
private static CursorBuffer stashed; serveroutput.println("restart");
serveroutput.flush();
private static void stashLine() { customrestartfailed = false;
stashed = reader.getCursorBuffer().copy(); }
try { Thread.sleep(5000); // Don't run needless cycles
reader.getOutput().write("\u001b[1G\u001b[K"); }
reader.flush(); restartcounter--;
} catch (IOException e) { }
// ignore } catch (InterruptedException e) { // The while checks if stop is true and then stops
} }
} }
writeToScreen("Stopped " + Thread.currentThread().getName());
private static void unstashLine() { }
try {
reader.resetPromptLine(reader.getPrompt(), stashed.toString(), stashed.cursor); private static Process startServer(Config config, File serverJar) throws IOException {
} catch (IOException e) { ProcessBuilder pb = new ProcessBuilder(("java " + config.serverParams + " -jar " + serverJar.getPath()).split(" ")); //Need to use split() because of the supplied params
// ignore return pb.start();
} }
}
private static void sendMessage(PrintWriter output, String color, String text) {
private static double hoursOf(Calendar parsedTime) { output.println("tellraw @a {\"text\":\"" + text + "\",\"color\":\"" + color + "\"}");
return parsedTime.get(Calendar.HOUR_OF_DAY) + parsedTime.get(Calendar.MINUTE) / 60. output.flush();
+ parsedTime.get(Calendar.SECOND) / 3600.; writeToScreen(text);
} }
private static long syncStart(double startHour) { // Copied original code from SimpleBackup private static void stop() {
double now = hoursOf(Calendar.getInstance(TimeZone.getTimeZone("GMT"))); stop = true;
double diff = now - startHour; rt.interrupt(); // The restarter thread sleeps for a long time and keeps the program running
if (diff < 0) { it.interrupt(); // The input thread will listen until it's stopped
diff += 24; }
}
double intervalPart = diff - Math.floor(diff / interval) * interval; private static void writeToScreen(String line) {
double remaining = interval - intervalPart; stashLine();
return (long) (remaining * 3600000); runnerout.println(line);
} unstashLine();
}
}
private static CursorBuffer stashed;
private static void stashLine() {
stashed = reader.getCursorBuffer().copy();
try {
reader.getOutput().write("\u001b[1G\u001b[K");
reader.flush();
} catch (IOException e) {
// ignore
}
}
private static void unstashLine() {
try {
reader.resetPromptLine(reader.getPrompt(), stashed.toString(), stashed.cursor);
} catch (IOException e) {
// ignore
}
}
private static double hoursOf(Calendar parsedTime) {
return parsedTime.get(Calendar.HOUR_OF_DAY) + parsedTime.get(Calendar.MINUTE) / 60.
+ parsedTime.get(Calendar.SECOND) / 3600.;
}
private static long syncStart(double startHour) { // Copied original code from SimpleBackup
double now = hoursOf(Calendar.getInstance(TimeZone.getTimeZone("GMT")));
double diff = now - startHour;
if (diff < 0) {
diff += 24;
}
double intervalPart = diff - Math.floor(diff / interval) * interval;
double remaining = interval - intervalPart;
return (long) (remaining * 3600000);
}
}