Archived
0
This commit is contained in:
2016-08-15 09:15:47 +03:00
parent f845d82a79
commit 92d8cc76c1
21 changed files with 743 additions and 71 deletions

View File

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>
<name>ASys Bridge Protocol</name>
<url>http://dmitriymx.ru/</url>
<developers>
<developer>
<name>DmitriyMX</name>
<email>mail@dmiriymx.ru</email>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
</properties>
<groupId>asys</groupId>
<artifactId>bridge-protocol</artifactId>
<packaging>jar</packaging>
<version>0.1</version>
<dependencies>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>**</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.15</version>
<configuration>
<argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
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>
<name>ASys Bridge</name>
<url>http://dmitriymx.ru/</url>
<developers>
<developer>
<name>DmitriyMX</name>
<email>mail@dmiriymx.ru</email>
</developer>
</developers>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<bukkit.version>1.8.8-R0.1-SNAPSHOT</bukkit.version>
<bukkit.mainclass>asys.bridge.ASysBukkitBridge</bukkit.mainclass>
</properties>
<groupId>asys</groupId>
<artifactId>bridge</artifactId>
<packaging>jar</packaging>
<version>1.0</version>
<repositories>
<repository>
<id>spigot</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
<version>${bukkit.version}</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.23.Final</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>asysbridge-${project.version}</finalName>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
<includes>
<include>**</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.15</version>
<configuration>
<argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,35 @@
/*
* DmitriyMX <mail@dmitriymx.ru>
* 2016-08-08
*/
package asys.bridge;
import org.bukkit.plugin.java.JavaPlugin;
/*
* Инфо:
* - ID сервера
* - кол-во игроков
* - максимальный онлайн
* - политика присоединения
*/
public class ASysBukkitBridge extends JavaPlugin {
private String idServer;
@Override
public void onEnable() {
idServer = System.getProperty("asys.server.name");
if (idServer == null || idServer.trim().isEmpty()) {
getLogger().severe("Don't get property \"asys.server.name\"");
getLogger().severe("Server shutdown!");
getServer().shutdown();
} else {
getLogger().info(String.format("Server ID: %s", idServer));
}
}
@Override
public void onDisable() {
getLogger().info("Bye");
}
}

View File

@@ -0,0 +1,5 @@
name: ASysBridge
main: ${bukkit.mainclass}
version: ${project.version}
description: 'Plugin-connector for ASys'
author: DmitriyMX

View File

@@ -7,7 +7,7 @@
<name>Single Server</name>
<artifactId>singleserver</artifactId>
<version>0.16</version>
<version>0.26</version>
<packaging>bundle</packaging>
<parent>
@@ -20,7 +20,7 @@
<dependency>
<groupId>asys</groupId>
<artifactId>core</artifactId>
<version>0.18</version>
<version>RELEASE</version>
</dependency>
</dependencies>

View File

@@ -0,0 +1,15 @@
/*
* DmitriyMX <mail@dmitriymx.ru>
* 2016-08-14
*/
package asys.singleserver;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Command {
}

View File

@@ -4,29 +4,84 @@
*/
package asys.singleserver;
import asys.core.api.IMinecraftServer;
import asys.core.api.IServerManager;
import org.apache.felix.service.command.Descriptor;
import org.apache.felix.service.command.Parameter;
import java.util.Dictionary;
import java.util.Hashtable;
import java.lang.reflect.Method;
import java.util.*;
public class Commands {
private IServerManager serverManager;
public static Dictionary<String, Object> getDictionary() {
Hashtable<String, Object> dictionary = new Hashtable<>();
dictionary.put("osgi.command.scope", "asys.singleserver");
dictionary.put("osgi.command.function", new String[] { "startserver" });
return dictionary;
public static Dictionary<String, Object> getProps(String group) {
List<String> listCommand = new ArrayList<>();
try {
Class clazz = Class.forName(Commands.class.getName());
Method[] methods = clazz.getMethods();
for(Method method : methods) {
if (method.isAnnotationPresent(Command.class)) {
listCommand.add(method.getName());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Hashtable<String, Object> props = new Hashtable<>();
if (listCommand.size() > 0) {
props.put("osgi.command.scope", group);
props.put("osgi.command.function", listCommand.toArray(new String[listCommand.size()]));
}
return props;
}
public Commands(IServerManager serverManager) {
this.serverManager = serverManager;
}
@Command
@Descriptor("Server start")
public void startserver() {
public void start() {
serverManager.getServer(null).start();
}
@Command
@Descriptor("Server stop")
public void stop() {
serverManager.getServer(null).stop();
serverManager.removeServer(null);
}
@Command
@Descriptor("Server force stop")
public void forceStop() {
serverManager.getServer(null).forceStop();
serverManager.removeServer(null);
}
@Command
@Descriptor("Server restart")
public void restart() {
IMinecraftServer server = serverManager.getServer(null);
server.stop();
server.start();
}
@Command
@Descriptor("Server is alive?")
public void isAlive() {
boolean alive = serverManager.getServer(null).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(null).sendCommand(sj.toString());
}
}

View File

@@ -11,25 +11,17 @@ import java.nio.file.Path;
import java.util.List;
import java.util.Vector;
public class MinecraftWinServer implements IMinecraftServer {
public class MinecraftProcessServer implements IMinecraftServer {
private ProcessBuilder processBuilder;
private Process process;
private String name;
/** запуск через скрипт */
public MinecraftWinServer(Path directory, Path script) {
processBuilder = new ProcessBuilder();
processBuilder.directory(directory.toFile());
processBuilder.command("cmd.exe", "/C", "start", script.toString());
}
/** запуск через asys */
public MinecraftWinServer(Path directory, Path mainJar, String jvmArgs, String params) {
public MinecraftProcessServer(String name, Path directory, Path mainJar, String jvmArgs, String params) {
processBuilder = new ProcessBuilder();
processBuilder.directory(directory.toFile());
List<String> commandLine = new Vector<>();
commandLine.add("cmd.exe");
commandLine.add("/C");
commandLine.add("start");
commandLine.add("java");
commandLine.add("-Dasys.server.name=\""+name+"\"");
if (jvmArgs != null && !jvmArgs.trim().isEmpty()) {
commandLine.add(jvmArgs);
}
@@ -39,12 +31,18 @@ public class MinecraftWinServer implements IMinecraftServer {
commandLine.add(params);
}
processBuilder.command(commandLine);
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void start() {
try {
processBuilder.start();
process = processBuilder.start();
} catch (IOException e) {
e.printStackTrace();
}
@@ -57,16 +55,23 @@ public class MinecraftWinServer implements IMinecraftServer {
@Override
public void forceStop() {
//TODO need code
}
@Override
public boolean isAlive() {
return false; //TODO need code
process.destroy();
process = null;
}
@Override
public void sendCommand(String command) {
//TODO need code
try {
process.getOutputStream().write(command.getBytes());
process.getOutputStream().write("\n".getBytes());
process.getOutputStream().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean isAlive() {
return process.isAlive();
}
}

View File

@@ -0,0 +1,105 @@
/*
* DmitriyMX <mail@dmitriymx.ru>
* 2016-08-11
*/
package asys.singleserver;
import asys.core.api.IMinecraftServer;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
public class MinecraftScreenServer implements IMinecraftServer {
private ProcessBuilder startPB;
private String name;
private String serverPID;
public MinecraftScreenServer(String name, Path directory, Path mainJar, String jvmArgs, String params) {
startPB = new ProcessBuilder();
startPB.directory(directory.toFile());
List<String> commandLine = new Vector<>(Arrays.asList("screen", "-dmS", name, "java", "-Dasys.server.name=\""+name+"\""));
if (jvmArgs != null && !jvmArgs.trim().isEmpty()) {
commandLine.add(jvmArgs);
}
commandLine.add("-jar");
commandLine.add(mainJar.toString());
if (params != null && !params.trim().isEmpty()) {
commandLine.add(params);
}
startPB.command(commandLine);
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void start() {
try {
startPB.start();
Thread.sleep(500);
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();
}
}
@Override
public void stop() {
sendCommand("stop");
}
@Override
public void forceStop() {
ProcessBuilder sendCmdPB = new ProcessBuilder();
sendCmdPB.command("screen", "-S", name, "-X", "quit");
try {
sendCmdPB.start();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void sendCommand(String command) {
ProcessBuilder sendCmdPB = new ProcessBuilder();
sendCmdPB.command("screen", "-S", name, "-p", "0", "-X", "stuff", command+"\\r");
try {
sendCmdPB.start();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public boolean isAlive() {
try {
Process process = Runtime.getRuntime().exec("ps -p " + 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;
}
}
}

View File

@@ -8,6 +8,8 @@ import asys.core.api.ICore;
import asys.core.api.IMinecraftServer;
import asys.core.api.IServerManager;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -16,36 +18,37 @@ import java.util.Properties;
public class SingleServer implements IServerManager {
private IMinecraftServer mcServer;
private BundleContext bundleContext;
private Logger logger = LoggerFactory.getLogger(SingleServer.class.getName());
private Properties configuration;
public SingleServer(BundleContext bundleContext) {
this.bundleContext = bundleContext;
public SingleServer(Properties configuration) {
this.configuration = configuration;
Path serverPath = Paths.get(getProperty("asys.singleserver.server.path"));
prepareMcServer();
}
private void prepareMcServer() {
Path serverPath = Paths.get(configuration.getProperty("asys.singleserver.server.path"));
if (Files.notExists(serverPath)) {
throw new RuntimeException(String.format("Server path \"%s\" not found", serverPath.toString()));
}
if (getProperty("asys.singleserver.runscript.use", "false").equalsIgnoreCase("true")) {
Path scriptFile = Paths.get(getProperty("asys.singleserver.runscript.file"));
if (Files.notExists(scriptFile)) {
throw new RuntimeException(String.format("Runscript \"%s\" not found", scriptFile.toString()));
}
Path mainJar = serverPath.resolve(configuration.getProperty("asys.singleserver.server.mainjar"));
if (Files.notExists(mainJar)) {
throw new RuntimeException(String.format("Main jar \"%s\" not found", mainJar.toString()));
}
if (!Files.isExecutable(scriptFile)) {
throw new RuntimeException(String.format("Runscript \"%s\" is not executable", scriptFile.toString()));
}
mcServer = new MinecraftWinServer(serverPath, scriptFile);
String typeServerImplement = configuration.getProperty("asys.singleserver.typeImplement", "process");
if (typeServerImplement.equalsIgnoreCase("screen")) {
logger.debug("Used server implement: screen");
mcServer = new MinecraftScreenServer("mcserver", serverPath, mainJar,
configuration.getProperty("asys.singleserver.server.jvm.args", null),
configuration.getProperty("asys.singleserver.server.params", null));
} else {
Path mainJar = serverPath.resolve(getProperty("asys.singleserver.server.mainjar"));
if (Files.notExists(mainJar)) {
throw new RuntimeException(String.format("Main jar \"%s\" not found", mainJar.toString()));
}
mcServer = new MinecraftWinServer(serverPath, mainJar,
getProperty("asys.singleserver.server.jvm.args", null),
getProperty("asys.singleserver.server.params", null));
logger.debug("Used server implement: process");
mcServer = new MinecraftProcessServer("mcserver", serverPath, mainJar,
configuration.getProperty("asys.singleserver.server.jvm.args", null),
configuration.getProperty("asys.singleserver.server.params", null));
}
}
@@ -56,9 +59,17 @@ public class SingleServer implements IServerManager {
@Override
public IMinecraftServer getServer(String serverName) {
if (mcServer == null) {
prepareMcServer();
}
return mcServer;
}
@Override
public void removeServer(String name) {
mcServer = null;
}
void loadState() {
ICore core = SingleServerActivator.instance.getCore();
IMinecraftServer testMcServer = (IMinecraftServer) core.loadObject(SingleServer.class.getName() + "#mcServer");
@@ -71,16 +82,4 @@ public class SingleServer implements IServerManager {
ICore core = SingleServerActivator.instance.getCore();
core.saveObject(SingleServer.class.getName() + "#mcServer", mcServer);
}
private String getProperty(String name) {
return getProperty(name, null);
}
private String getProperty(String name, String defValue) {
try {
return bundleContext.getProperty(name);
} catch (NullPointerException e) {
return defValue;
}
}
}

View File

@@ -6,11 +6,18 @@ package asys.singleserver;
import asys.core.api.ICore;
import asys.core.api.IServerManager;
import org.apache.commons.io.FileUtils;
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 SingleServerActivator implements BundleActivator {
static SingleServerActivator instance;
private ServiceRegistration<?> service, cmdService;
@@ -24,13 +31,13 @@ public class SingleServerActivator implements BundleActivator {
tracker = new ServiceTracker<>(bundleContext, ICore.class.getName(), null);
tracker.open();
singleServer = new SingleServer(bundleContext);
singleServer = new SingleServer(loadProperties(getProperty(bundleContext, "asys.core.config.dir", "conf")));
singleServer.loadState();
service = bundleContext.registerService(IServerManager.class.getName(), singleServer, null);
// Регистрация комманд для Gogo Shell
cmdService = bundleContext.registerService(
Commands.class.getName(), new Commands(singleServer), Commands.getDictionary());
Commands.class.getName(), new Commands(singleServer), Commands.getProps("asys.server"));
}
@Override
@@ -45,4 +52,51 @@ public class SingleServerActivator implements BundleActivator {
ICore getCore() {
return tracker.getService();
}
private Properties loadProperties(String confDir) {
Path propsPath = Paths.get(confDir).resolve("asys.singleserver.properties");
if (Files.notExists(propsPath)) {
propsPath.toFile().getParentFile().mkdirs();
InputStream resourceAsStream = getClass().getResourceAsStream("/asys.singleserver.properties");
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 FileReader(propsPath.toFile()));
return properties;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
private String getProperty(BundleContext bundleContext, String name) {
return getProperty(bundleContext, name, null);
}
private String getProperty(BundleContext bundleContext, String name, String defValue) {
try {
String result = bundleContext.getProperty(name);
if (result == null) return defValue;
else return result;
} catch (NullPointerException e) {
return defValue;
}
}
}

View File

@@ -0,0 +1,6 @@
asys.singleserver.server.path=server
asys.singleserver.typeImplement=process
asys.singleserver.server.mainjar=spigot.jar
asys.singleserver.server.jvm.args="-Dfile.encoding=UTF-8"
asys.singleserver.server.params=

View File

@@ -0,0 +1,31 @@
/*
* DmitriyMX <mail@dmitriymx.ru>
* 2016-08-11
*/
package asys.singleserver;
import asys.core.api.IMinecraftServer;
import java.nio.file.Paths;
public class TestMinectaftScreenServer {
public static void main(String[] args) {
IMinecraftServer server = new MinecraftScreenServer(
"test",
Paths.get("D:\\tmp\\spigot_test"),
Paths.get("D:\\tmp\\spigot_test\\spigot-1.8.8.jar"),
"", "");
server.start();
System.out.println(String.format("isAlive: %s", server.isAlive()));
try {
Thread.sleep(30000);
} catch (InterruptedException ignored) {
}
server.stop();
try {
Thread.sleep(5000);
} catch (InterruptedException ignored) {
}
System.out.println(String.format("isAlive: %s", server.isAlive()));
}
}

View File

@@ -0,0 +1,49 @@
/*
* DmitriyMX <mail@dmitriymx.ru>
* 2016-07-30
*/
package asys.singleserver;
import asys.core.api.IMinecraftServer;
import asys.core.api.IServerManager;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
import org.osgi.framework.BundleContext;
import java.util.Iterator;
import java.util.Properties;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class TestSingleServer {
@Test
public void testAutorun() throws InterruptedException {
BundleContext mockBundleContext = mock(BundleContext.class);
when(mockBundleContext.getProperty("asys.singleserver.server.path")).thenReturn("D:\\tmp\\spigot_test");
when(mockBundleContext.getProperty("asys.singleserver.server.mainjar")).thenReturn("spigot-1.8.8.jar");
when(mockBundleContext.getProperty("asys.singleserver.server.jvm.args")).thenReturn("-Dserver.name=\\\"Minecraft Server\\\" -Dfile.encoding=UTF-8");
IServerManager serverManager = new SingleServer(mockBundleContext);
IMinecraftServer server = serverManager.getServer(null);
server.start();
Assert.assertTrue(server.isAlive());
for (int i = 0; i < 7; i++) Thread.sleep(1000);
server.stop();
for (int i = 0; i < 5; i++) {
if (server.isAlive()) {
Thread.sleep(1000);
} else {
break;
}
}
if (server.isAlive()) {
server.forceStop();
for (int i = 0; i < 5; i++) Thread.sleep(1000);
}
Assert.assertFalse(server.isAlive());
}
}