From ef11daee68e614160d743580e66501a469b353b8 Mon Sep 17 00:00:00 2001 From: ME1312 Date: Sun, 4 Dec 2016 22:21:04 -0500 Subject: [PATCH] SubServers.Bungee --- .gitignore | 32 ++ Artifacts/SubServers.Bungee.jar | Bin 0 -> 122175 bytes SubServers.Bungee/META-INF/MANIFEST.MF | 4 + .../Proxy/Event/SubCreateEvent.java | 7 + .../Proxy/Event/SubSendCommandEvent.java | 70 +++ .../SubServers/Proxy/Event/SubStartEvent.java | 50 ++ .../SubServers/Proxy/Event/SubStopEvent.java | 62 +++ .../Proxy/Event/SubStoppedEvent.java | 23 + .../SubServers/Proxy/Host/Executable.java | 64 +++ .../ME1312/SubServers/Proxy/Host/Host.java | 203 +++++++ .../Proxy/Host/Internal/InternalHost.java | 125 +++++ .../Host/Internal/InternalSubCreator.java | 36 ++ .../Host/Internal/InternalSubLogger.java | 66 +++ .../Host/Internal/InternalSubServer.java | 229 ++++++++ .../ME1312/SubServers/Proxy/Host/Server.java | 38 ++ .../SubServers/Proxy/Host/SubCreator.java | 31 ++ .../SubServers/Proxy/Host/SubServer.java | 172 ++++++ .../net/ME1312/SubServers/Proxy/Launch.java | 90 ++++ .../Proxy/Libraries/Config/YAMLConfig.java | 65 +++ .../Proxy/Libraries/Config/YAMLSection.java | 509 ++++++++++++++++++ .../Proxy/Libraries/Config/YAMLValue.java | 156 ++++++ .../SubServers/Proxy/Libraries/Container.java | 37 ++ .../Exception/IllegalPacketException.java | 8 + .../Exception/InvalidDriverException.java | 8 + .../Exception/InvalidHostException.java | 8 + .../Exception/InvalidServerException.java | 8 + .../SubServers/Proxy/Libraries/Files/build.sh | 156 ++++++ .../Proxy/Libraries/Files/config.yml | 30 ++ .../SubServers/Proxy/Libraries/Files/lang.yml | 3 + .../Proxy/Libraries/UniversalFile.java | 72 +++ .../Proxy/Libraries/Version/Version.java | 224 ++++++++ .../Libraries/Version/VersionTokenizer.java | 64 +++ .../SubServers/Proxy/Network/Client.java | 211 ++++++++ .../Proxy/Network/ClientHandler.java | 22 + .../Proxy/Network/NetworkManager.java | 250 +++++++++ .../Network/Packet/PacketAuthorization.java | 47 ++ .../Network/Packet/PacketLinkServer.java | 55 ++ .../Packet/PacketRequestServerInfo.java | 62 +++ .../Network/Packet/PacketRequestServers.java | 53 ++ .../SubServers/Proxy/Network/PacketIn.java | 27 + .../SubServers/Proxy/Network/PacketOut.java | 25 + .../net/ME1312/SubServers/Proxy/SubAPI.java | 150 ++++++ .../ME1312/SubServers/Proxy/SubCommand.java | 141 +++++ .../ME1312/SubServers/Proxy/SubPlugin.java | 282 ++++++++++ SubServers.Client/src/plugin.yml | 54 ++ 45 files changed, 4029 insertions(+) create mode 100644 .gitignore create mode 100644 Artifacts/SubServers.Bungee.jar create mode 100644 SubServers.Bungee/META-INF/MANIFEST.MF create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Event/SubCreateEvent.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Event/SubSendCommandEvent.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Event/SubStartEvent.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Event/SubStopEvent.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Event/SubStoppedEvent.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Executable.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Host.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalHost.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubCreator.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubLogger.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubServer.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Server.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/SubCreator.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/SubServer.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Launch.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLConfig.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLSection.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLValue.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Container.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/IllegalPacketException.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidDriverException.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidHostException.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidServerException.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/build.sh create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/config.yml create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/lang.yml create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/UniversalFile.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Version/Version.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Version/VersionTokenizer.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Client.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/ClientHandler.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/NetworkManager.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketAuthorization.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketLinkServer.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketRequestServerInfo.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketRequestServers.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/PacketIn.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/PacketOut.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubAPI.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubCommand.java create mode 100644 SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubPlugin.java create mode 100644 SubServers.Client/src/plugin.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..fb1b0391 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# SubServers 2 Ignored Files + +*.iml + +## Directory-based project format: +.idea/ + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + +# Hide Unfinished Project Files +# (none) + +# Hide Others +.DS_STORE \ No newline at end of file diff --git a/Artifacts/SubServers.Bungee.jar b/Artifacts/SubServers.Bungee.jar new file mode 100644 index 0000000000000000000000000000000000000000..9f16b654f8ae94c81956d97773f0ffcc422632fd GIT binary patch literal 122175 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�)$mh`^Aj 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

lq@74^@>^gl!ziOy^h1vgYLfUO3Ox|7P)qmZ>q- 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 getSubServers(); + + /** + * Gets a SubServer + * + * @param name SubServer Name + * @return a SubServer + */ + public abstract SubServer getSubServer(String name); + + /** + * Adds a SubServer + * + * @param player Player who Added + * @param name Name of Server + * @param enabled Enabled Status + * @param port Port Number + * @param motd Motd of the Server + * @param log Logging Status + * @param directory Directory + * @param executable Executable + * @param stopcmd Command to Stop the Server + * @param restart Auto Restart Status + * @param temporary Temporary Status + * @return The SubServer + * @throws InvalidServerException + */ + public abstract SubServer addSubServer(UUID player, String name, boolean enabled, int port, String motd, boolean log, String directory, Executable executable, String stopcmd, boolean start, boolean restart, boolean temporary) throws InvalidServerException; + + /** + * Adds a SubServer + * + * @param name Name of Server + * @param enabled Enabled Status + * @param port Port Number + * @param motd Motd of the Server + * @param log Logging Status + * @param directory Directory + * @param executable Executable + * @param stopcmd Command to Stop the Server + * @param restart Auto Restart Status + * @param temporary Temporary Status + * @return The SubServer + * @throws InvalidServerException + */ + public SubServer addSubServer(String name, boolean enabled, int port, String motd, boolean log, String directory, Executable executable, String stopcmd, boolean start, boolean restart, boolean temporary) throws InvalidServerException { + return addSubServer(null, name, enabled, port, motd, log, directory, executable, stopcmd, start, restart, temporary); + } + + /** + * Removes a SubServer + * + * @param name SubServer Name + * @throws InterruptedException + */ + public abstract void removeSubServer(String name) throws InterruptedException; + + /** + * Forces the Removal of a SubServer + * + * @param name SubServer Name + */ + public abstract void forceRemoveSubServer(String name); + +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalHost.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalHost.java new file mode 100644 index 00000000..d8d0d73f --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalHost.java @@ -0,0 +1,125 @@ +package net.ME1312.SubServers.Proxy.Host.Internal; + +import net.ME1312.SubServers.Proxy.Host.Executable; +import net.ME1312.SubServers.Proxy.Libraries.Exception.InvalidServerException; +import net.ME1312.SubServers.Proxy.Host.Host; +import net.ME1312.SubServers.Proxy.Host.SubCreator; +import net.ME1312.SubServers.Proxy.Host.SubServer; +import net.ME1312.SubServers.Proxy.Libraries.UniversalFile; +import net.ME1312.SubServers.Proxy.SubPlugin; + +import java.io.File; +import java.net.InetAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.UUID; + +public class InternalHost extends Host { + private HashMap servers = new HashMap(); + + private String name; + private boolean enabled; + private InetAddress address; + private InternalSubCreator creator; + UniversalFile directory; + SubPlugin plugin; + + public InternalHost(SubPlugin plugin, String name, Boolean enabled, InetAddress address, UniversalFile directory) { + super(plugin, name, enabled, address, directory); + this.plugin = plugin; + this.name = name; + this.enabled = enabled; + this.address = address; + this.creator = new InternalSubCreator(this); + this.directory = directory; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void setEnabled(boolean value) { + this.enabled = value; + } + + @Override + public InetAddress getAddress() { + return address; + } + + @Override + public String getName() { + return name; + } + + @Override + public void start(UUID player, String... servers) { + for (String server : servers) { + this.servers.get(server.toLowerCase()).start(player); + } + } + + @Override + public void stop(UUID player, String... servers) { + for (String server : servers) { + this.servers.get(server.toLowerCase()).stop(player); + } + } + + @Override + public void terminate(UUID player, String... servers) { + for (String server : servers) { + this.servers.get(server.toLowerCase()).terminate(player); + } + } + + @Override + public void command(UUID player, String command, String... servers) { + for (String server : servers) { + this.servers.get(server.toLowerCase()).command(player, command); + } + } + + @Override + public SubCreator getCreator() { + return creator; + } + + @Override + public Map getSubServers() { + return new TreeMap(servers); + } + + @Override + public SubServer getSubServer(String name) { + return servers.get(name.toLowerCase()); + } + + @Override + public SubServer addSubServer(UUID player, String name, boolean enabled, int port, String motd, boolean log, String directory, Executable executable, String stopcmd, boolean start, boolean restart, boolean temporary) throws InvalidServerException { + if (plugin.getServers().keySet().contains(name.toLowerCase())) throw new InvalidServerException("A Server already exists with this name!"); + SubServer server = new InternalSubServer(this, name, enabled, port, motd, log, directory, executable, stopcmd, start, restart, temporary); + servers.put(name.toLowerCase(), server); + return server; + } + + @Override + public void removeSubServer(String name) throws InterruptedException { + if (getSubServer(name).isRunning()) { + getSubServer(name).stop(); + getSubServer(name).waitFor(); + } + servers.remove(name.toLowerCase()); + } + + @Override + public void forceRemoveSubServer(String name) { + if (getSubServer(name).isRunning()) { + getSubServer(name).terminate(); + } + servers.remove(name.toLowerCase()); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubCreator.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubCreator.java new file mode 100644 index 00000000..5b28ccb7 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubCreator.java @@ -0,0 +1,36 @@ +package net.ME1312.SubServers.Proxy.Host.Internal; + +import net.ME1312.SubServers.Proxy.Host.Host; +import net.ME1312.SubServers.Proxy.Host.SubCreator; +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; + +import java.io.File; +import java.util.UUID; + +public class InternalSubCreator extends SubCreator { + private Host host; + + public InternalSubCreator(Host host) { + this.host = host; + } + + @Override + public void create(UUID player, String name, int port, File directory, ServerType type, Version version, int memory) { + + } + + @Override + public void waitFor() throws InterruptedException { + + } + + @Override + public Host getHost() { + return host; + } + + @Override + public boolean isBusy() { + return false; + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubLogger.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubLogger.java new file mode 100644 index 00000000..1d75eb3e --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubLogger.java @@ -0,0 +1,66 @@ +package net.ME1312.SubServers.Proxy.Host.Internal; + +import net.ME1312.SubServers.Proxy.Libraries.Container; + +import java.io.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class InternalSubLogger extends Thread { + InputStream is; + String name; + Container log; + PrintWriter writer = null; + + InternalSubLogger(InputStream is, String name, Container log, File file) { + this.is = is; + this.name = name; + this.log = log; + if (file != null) + try { + this.writer = new PrintWriter(file, "UTF-8"); + this.writer.println("---------- LOG START: " + name + " ----------"); + this.writer.flush(); + } catch (UnsupportedEncodingException | FileNotFoundException e) { + e.printStackTrace(); + } + } + + @Override + public void run() { + try { + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + String line = null; + while ((line = br.readLine()) != null) { + if (log.get() && !line.startsWith(">")) { + String msg = line; + /* REGEX Formatting + String type = "INFO"; + Matcher matcher = Pattern.compile("^((?:\\s*\\[?[0-9]{2}:[0-9]{2}:[0-9]{2}]?)?\\s*(?:\\[|\\[.*\\/)?(INFO|WARN|WARNING|ERROR|ERR|SEVERE)\\]:?\\s*)").matcher(msg); + while (matcher.find()) { + type = matcher.group(2); + } + */ + msg = msg.replaceAll("^((?:\\s*\\[?[0-9]{2}:[0-9]{2}:[0-9]{2}]?)?\\s*(?:\\[|\\[.*\\/)?(INFO|WARN|WARNING|ERROR|ERR|SEVERE)\\]:?\\s*)", ""); + + System.out.println(name + " > " + msg); + + if (writer != null) { + writer.println(line); + writer.flush(); + } + } + if (writer != null) { + writer.println(line); + writer.flush(); + } + } + } catch (IOException ioe) {} finally { + if (writer != null) { + writer.println("---------- END LOG ----------"); + writer.close(); + } + } + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubServer.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubServer.java new file mode 100644 index 00000000..0c848dea --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Internal/InternalSubServer.java @@ -0,0 +1,229 @@ +package net.ME1312.SubServers.Proxy.Host.Internal; + +import net.ME1312.SubServers.Proxy.Event.SubSendCommandEvent; +import net.ME1312.SubServers.Proxy.Event.SubStartEvent; +import net.ME1312.SubServers.Proxy.Event.SubStopEvent; +import net.ME1312.SubServers.Proxy.Event.SubStoppedEvent; +import net.ME1312.SubServers.Proxy.Host.Executable; +import net.ME1312.SubServers.Proxy.Libraries.Container; +import net.ME1312.SubServers.Proxy.Libraries.Exception.InvalidServerException; +import net.ME1312.SubServers.Proxy.Host.Host; +import net.ME1312.SubServers.Proxy.Host.SubServer; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class InternalSubServer extends SubServer { + private InternalHost host; + private boolean enabled; + private Container log; + private File directory; + private Executable executable; + private String stopcmd; + private Process process; + private List queue; + private boolean restart; + private boolean temporary; + private InternalSubServer instance; + + public InternalSubServer(Host host, String name, boolean enabled, int port, String motd, boolean log, String directory, Executable executable, String stopcmd, boolean start, boolean restart, boolean temporary) throws InvalidServerException { + super(host, name, port, motd); + this.host = (InternalHost) host; + this.enabled = enabled; + this.log = new Container(log); + this.directory = new File(((InternalHost) host).directory, directory); + this.executable = executable; + this.stopcmd = stopcmd; + this.process = null; + this.queue = new ArrayList(); + this.restart = restart; + this.temporary = temporary; + this.instance = this; + + if (start || temporary) start(); + } + + private void run() { + new Thread() { + public void run() { + final Container allowRestart = new Container(true); + try { + process = Runtime.getRuntime().exec(executable.toString(), null, directory); + System.out.println("SubServers > Now starting " + instance.getName()); + final InternalSubLogger read = new InternalSubLogger(process.getInputStream(), instance.getName(), log, null); + read.start(); + final BufferedWriter cmd = new BufferedWriter(new OutputStreamWriter(process.getOutputStream())); + new Thread() { + @Override + public void run() { + try { + do { + if (!queue.isEmpty()) { + while (queue.size() > 0) { + try { + if (queue.get(0).equalsIgnoreCase(stopcmd)) allowRestart.set(false); + cmd.write(queue.get(0)); + cmd.newLine(); + cmd.flush(); + queue.remove(0); + Thread.sleep(100); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + } + } + } + Thread.sleep(500); + + } while (read.isAlive()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + try { + process.waitFor(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } catch (IOException e) { + e.printStackTrace(); + allowRestart.set(false); + } + + SubStoppedEvent event = new SubStoppedEvent(instance); + host.plugin.getPluginManager().callEvent(event); + System.out.println("SubServers > " + instance.getName() + " has stopped"); + process = null; + queue.clear(); + + if (temporary) { + try { + host.removeSubServer(instance.getName()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else { + if (restart && allowRestart.get()) { + try { + Thread.sleep(2500); + start(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + }.start(); + } + + @Override + public void start(UUID player) { + if (enabled && !isRunning()) { + SubStartEvent event = new SubStartEvent(this, player); + host.plugin.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + run(); + } + } + } + + @Override + public void stop(UUID player) { + if (isRunning()) { + SubStopEvent event = new SubStopEvent(this, player, false); + host.plugin.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + queue.add(stopcmd); + } + } + } + + @Override + public void terminate(UUID player) { + if (isRunning()) { + SubStopEvent event = new SubStopEvent(this, player, true); + host.plugin.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + process.destroyForcibly(); + } + } + } + + @Override + public void command(UUID player, String command) { + if (isRunning()) { + SubSendCommandEvent event = new SubSendCommandEvent(this, player, command); + host.plugin.getPluginManager().callEvent(event); + if (!event.isCancelled()) { + queue.add(command); + } + } + } + + @Override + public void waitFor() throws InterruptedException { + if (isRunning()) { + process.waitFor(); + } + } + + @Override + public boolean isRunning() { + return process != null && process.isAlive(); + } + + @Override + public Host getHost() { + return host; + } + + @Override + public boolean isEnabled() { + return enabled; + } + + @Override + public void setEnabled(boolean value) { + enabled = value; + } + + @Override + public boolean isLogging() { + return log.get(); + } + + @Override + public void setLogging(boolean value) { + log.set(value); + } + + @Override + public String getStopCommand() { + return stopcmd; + } + + @Override + public void setStopCommand(String value) { + stopcmd = value; + } + + @Override + public boolean willAutoRestart() { + return restart; + } + + @Override + public void setAutoRestart(boolean value) { + restart = value; + } + + @Override + public boolean isTemporary() { + return temporary; + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Server.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Server.java new file mode 100644 index 00000000..fe36c478 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/Server.java @@ -0,0 +1,38 @@ +package net.ME1312.SubServers.Proxy.Host; + +import net.ME1312.SubServers.Proxy.Libraries.Exception.InvalidServerException; +import net.ME1312.SubServers.Proxy.Network.Client; +import net.ME1312.SubServers.Proxy.Network.ClientHandler; +import net.md_5.bungee.BungeeServerInfo; +import net.md_5.bungee.api.ChatColor; + +import java.net.InetSocketAddress; + +/** + * Server Class + * + * @author ME1312 + */ +public class Server extends BungeeServerInfo implements ClientHandler { + private Client client = null; + + public Server(String name, InetSocketAddress address, String motd, boolean restricted) throws InvalidServerException { + super(name, address, ChatColor.translateAlternateColorCodes('&', motd), restricted); + if (name.contains(" ")) throw new InvalidServerException("Server names cannot have spaces: " + name); + } + + @Override + public Client getSubDataClient() { + return client; + } + + @Override + public void linkSubDataClient(Client client) { + if (this.client == null) { + client.setHandler(this); + this.client = client; + } else if (client == null) { + this.client = null; + } else throw new IllegalStateException("A SubData Client is already linked to Server: " + getName()); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/SubCreator.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/SubCreator.java new file mode 100644 index 00000000..c2607910 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/SubCreator.java @@ -0,0 +1,31 @@ +package net.ME1312.SubServers.Proxy.Host; + +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; + +import java.io.File; +import java.util.UUID; + +/** + * SubCreator Layout Class + * + * @author ME1312 + */ +public abstract class SubCreator { + public enum ServerType { + SPIGOT, + BUKKIT, + VANILLA, + SPONGE, + } + + public abstract void create(UUID player, String name, int port, File directory, ServerType type, Version version, int memory); + public void create(String name, int port, File directory, ServerType type, Version version, int memory) { + create(null, name, port, directory, type, version, memory); + } + + public abstract void waitFor() throws InterruptedException; + + public abstract Host getHost(); + + public abstract boolean isBusy(); +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/SubServer.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/SubServer.java new file mode 100644 index 00000000..006a3972 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Host/SubServer.java @@ -0,0 +1,172 @@ +package net.ME1312.SubServers.Proxy.Host; + +import net.ME1312.SubServers.Proxy.Libraries.Exception.InvalidServerException; + +import java.net.InetSocketAddress; +import java.util.UUID; + +/** + * SubServer Layout Class + * + * @author ME1312 + */ +public abstract class SubServer extends Server { + + /** + * Creates a SubServer + * + * @param host Host + * @param name Server Name + * @param port Port Number + * @param motd Server MOTD + * @throws InvalidServerException + */ + public SubServer(Host host, String name, int port, String motd) throws InvalidServerException { + super(name, InetSocketAddress.createUnresolved(host.getAddress().getHostAddress(), port), motd, false); + } + + /** + * Starts the Server + * + * @param player Player who Started + */ + public abstract void start(UUID player); + + /** + * Starts the Server + */ + public void start() { + start(null); + } + + /** + * Stops the Server + * + * @param player Player who Stopped + */ + public abstract void stop(UUID player); + + /** + * Stops the Server + */ + public void stop() { + stop(null); + } + + /** + * Terminates the Server + * + * @param player Player who Terminated + */ + public abstract void terminate(UUID player); + + /** + * Terminates the Server + */ + public void terminate() { + terminate(null); + } + + /** + * Commands the Server + * + * @param player Player who Commanded + * @param command Command to Send + */ + public abstract void command(UUID player, String command); + + /** + * Commands the Server + * + * @param command Command to Send + */ + public void command(String command) { + command(null, command); + } + + /** + * Waits for the Server to Stop + * + * @throws InterruptedException + */ + public abstract void waitFor() throws InterruptedException; + + /** + * If the Server is Running + * + * @return Running Status + */ + public abstract boolean isRunning(); + + /** + * Grabs the Host of the Server + * + * @return The Host + */ + public abstract Host getHost(); + + /** + * If the Server is Enabled + * + * @return Enabled Status + */ + public abstract boolean isEnabled(); + + /** + * Set if the Server is Enabled + * + * @param value Value + */ + public abstract void setEnabled(boolean value); + + /** + * If the Server is Logging + * + * @return Logging Status + */ + public abstract boolean isLogging(); + + /** + * Set if the Server is Logging + * + * @param value Value + */ + public abstract void setLogging(boolean value); + + /** + * Grab the Command to Stop the Server + * + * @return Stop Command + */ + public abstract String getStopCommand(); + + /** + * Set the Command that Stops the Server + * + * @param value Value + */ + public abstract void setStopCommand(String value); + + /** + * If the Server will Auto Restart on unexpected shutdowns + * + * @return Auto Restart Status + */ + public abstract boolean willAutoRestart(); + + /** + * Set if the Server will Auto Restart on unexpected shutdowns + * + * @param value Value + */ + public abstract void setAutoRestart(boolean value); + + /** + * If the Server is Temporary + * + * @return Temporary Status + */ + public abstract boolean isTemporary(); + + +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Launch.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Launch.java new file mode 100644 index 00000000..57175e25 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Launch.java @@ -0,0 +1,90 @@ +package net.ME1312.SubServers.Proxy; + +import java.security.Security; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import joptsimple.OptionParser; +import joptsimple.OptionSet; +import net.md_5.bungee.Bootstrap; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.command.ConsoleCommandSender; + +/** + * SubServers/BungeeCord Class + * + * @author ME1312 + */ +public final class Launch { + + /** + * Launch SubServers/BungeeCord + * + * @param args + * @throws Exception + */ + public static void main(String[] args) throws Exception { + System.out.println(""); + System.out.println("*******************************************"); + System.out.println("*** Warning, this build is Unofficial ***"); + System.out.println("*** ***"); + System.out.println("*** Please report all issues to ME1312, ***"); + System.out.println("*** NOT the Spigot Staff! Thank You! ***"); + System.out.println("*******************************************"); + System.out.println(""); + + Security.setProperty("networkaddress.cache.ttl", "30"); + Security.setProperty("networkaddress.cache.negative.ttl", "10"); + OptionParser parser = new OptionParser(); + parser.allowsUnrecognizedOptions(); + parser.accepts("v"); + parser.accepts("version"); + parser.accepts("noconsole"); + OptionSet options = parser.parse(args); + if(options.has("version") || options.has("v")) { + System.out.println(Bootstrap.class.getPackage().getImplementationVersion()); + } else { + try { + if (BungeeCord.class.getPackage().getSpecificationVersion() != null) { + Date bungee = (new SimpleDateFormat("yyyyMMdd")).parse(BungeeCord.class.getPackage().getSpecificationVersion()); + Calendar line = Calendar.getInstance(); + line.add(3, -4); + if (bungee.before(line.getTime())) { + System.out.println("*******************************************"); + System.out.println("*** Warning, this build is outdated ***"); + System.out.println("*** Please download a new build from: ***"); + System.out.println("*** http://ci.md-5.net/job/BungeeCord ***"); + System.out.println("*** Errors may arise on older versions! ***"); + System.out.println("*******************************************"); + System.out.println(""); + } + } else throw new NullPointerException(); + } catch (Exception e) { + System.out.println("*******************************************"); + System.out.println("*** Problem checking BungeeCord Version ***"); + System.out.println("*** This build could be outdated. ***"); + System.out.println("*** ***"); + System.out.println("*** Errors may arise on older versions! ***"); + System.out.println("*******************************************"); + System.out.println(""); + } + + SubPlugin bungee = new SubPlugin(); + ProxyServer.setInstance(bungee); + bungee.getLogger().info("Enabled BungeeCord version " + bungee.getVersion()); + bungee.start(); + + String line; + if(!options.has("noconsole")) { + while(bungee.isRunning && (line = bungee.getConsoleReader().readLine(">")) != null) { + if(!bungee.getPluginManager().dispatchCommand(ConsoleCommandSender.getInstance(), line)) { + bungee.getConsole().sendMessage(ChatColor.RED + "Command not found"); + } + } + } + + } + } +} \ No newline at end of file diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLConfig.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLConfig.java new file mode 100644 index 00000000..40ff7ad1 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLConfig.java @@ -0,0 +1,65 @@ +package net.ME1312.SubServers.Proxy.Libraries.Config; + +import org.json.JSONObject; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.util.Map; + +@SuppressWarnings("unused") +public class YAMLConfig { + private File file; + private Yaml yaml; + private YAMLSection config; + + @SuppressWarnings("unchecked") + public YAMLConfig(File file) throws IOException, YAMLException { + if (file.exists()) { + this.config = new YAMLSection((Map) (this.yaml = new Yaml(getDumperOptions())).load(new FileInputStream(this.file = file)), null, null, yaml); + } else { + this.config = new YAMLSection(null, null, null, yaml); + } + } + + public YAMLSection get() { + return config; + } + + public void set(YAMLSection yaml) { + config = yaml; + } + + @SuppressWarnings("unchecked") + public void reload() throws IOException { + config = new YAMLSection((Map) yaml.load(new FileInputStream(file)), null, null, yaml); + } + + public void save() throws IOException { + FileWriter writer = new FileWriter(file); + yaml.dump(config.map, writer); + writer.close(); + } + + @Override + public String toString() { + return yaml.dump(config.map); + } + + public JSONObject toJSON() { + return new JSONObject(config.map); + } + + protected static DumperOptions getDumperOptions() { + DumperOptions options = new DumperOptions(); + options.setAllowUnicode(true); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setIndent(4); + + return options; + } +} \ No newline at end of file diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLSection.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLSection.java new file mode 100644 index 00000000..f22edd25 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLSection.java @@ -0,0 +1,509 @@ +package net.ME1312.SubServers.Proxy.Libraries.Config; + +import net.md_5.bungee.api.ChatColor; +import org.json.JSONObject; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; + +import java.io.InputStream; +import java.io.Reader; +import java.util.*; + +@SuppressWarnings({"unchecked", "unused"}) +public class YAMLSection { + protected Map map; + protected String label = null; + protected YAMLSection up = null; + private Yaml yaml; + + public YAMLSection() { + this.map = new HashMap<>(); + this.yaml = new Yaml(YAMLConfig.getDumperOptions()); + } + + public YAMLSection(InputStream io) throws YAMLException { + this.map = (Map) (this.yaml = new Yaml(YAMLConfig.getDumperOptions())).load(io); + } + + public YAMLSection(Reader reader) throws YAMLException { + this.map = (Map) (this.yaml = new Yaml(YAMLConfig.getDumperOptions())).load(reader); + } + + public YAMLSection(String yaml) throws YAMLException { + this.map = (Map) (this.yaml = new Yaml(YAMLConfig.getDumperOptions())).load(yaml); + } + + protected YAMLSection(Map map, YAMLSection up, String label, Yaml yaml) { + this.map = new HashMap(); + this.yaml = yaml; + this.label = label; + this.up = up; + + if (map != null) { + for (String key : map.keySet()) { + this.map.put(key, map.get(key)); + } + } + } + + public Set getKeys() { + return map.keySet(); + } + + + public Collection getValues() { + List values = new ArrayList(); + for (String value : map.keySet()) { + values.add(new YAMLValue(map.get(value), this, value, yaml)); + } + return values; + } + + public boolean contains(String label) { + return map.keySet().contains(label); + } + + public void remove(String label) { + map.remove(label); + } + + public void clear() { + map.clear(); + } + + public void set(String label, Object value) { + if (value instanceof YAMLConfig) { // YAML Handler Values + ((YAMLConfig) value).get().up = this; + ((YAMLConfig) value).get().label = label; + map.put(label, ((YAMLConfig) value).get().map); + } else if (value instanceof YAMLSection) { + ((YAMLSection) value).up = this; + ((YAMLSection) value).label = label; + map.put(label, ((YAMLSection) value).map); + } else if (value instanceof YAMLValue) { + map.put(label, ((YAMLValue) value).asObject()); + } else { + map.put(label, value); + } + + if (this.label != null && this.up != null) { + this.up.set(this.label, this); + } + } + + public void setAll(Map values) { + for (String value : values.keySet()) { + set(value, values.get(value)); + } + } + + public void setAll(YAMLSection values) { + for (String value : values.map.keySet()) { + set(value, values.map.get(value)); + } + } + + public YAMLSection superSection() { + return up; + } + + @Override + public String toString() { + return yaml.dump(map); + } + + public JSONObject toJSON() { + return new JSONObject(map); + } + + public YAMLValue get(String label) { + return (map.get(label) != null)?(new YAMLValue(map.get(label), this, label, yaml)):null; + } + + public YAMLValue get(String label, Object def) { + return new YAMLValue((map.get(label) != null)?map.get(label):def, this, label, yaml); + } + + public YAMLValue get(String label, YAMLValue def) { + return (map.get(label) != null) ? (new YAMLValue(map.get(label), this, label, yaml)) : def; + } + + public List getList(String label) { + if (map.get(label) != null) { + List values = new ArrayList(); + for (Object value : (List) map.get(label)) { + values.add(new YAMLValue(value, null, null, yaml)); + } + return values; + } else { + return null; + } + } + + public List getList(String label, Collection def) { + if (map.get(label) != null) { + return getList(label); + } else { + List values = new ArrayList(); + for (Object value : def) { + values.add(new YAMLValue(value, null, null, yaml)); + } + return values; + } + } + + public List getList(String label, List def) { + if (map.get(label) != null) { + return getList(label); + } else { + List values = new ArrayList(); + for (YAMLValue value : def) { + values.add(value); + } + return values; + } + } + + public Object getObject(String label) { + return map.get(label); + } + + public Object getObject(String label, Object def) { + return (map.get(label) != null)?map.get(label):def; + } + + public List getObjectList(String label) { + return (List) map.get(label); + } + + public List getObjectList(String label, List def) { + return (List) ((map.get(label) != null)?map.get(label):def); + } + + public boolean getBoolean(String label) { + return (boolean) map.get(label); + } + + public boolean getBoolean(String label, boolean def) { + return (boolean) ((map.get(label) != null)?map.get(label):def); + } + + public List getBooleanList(String label) { + return (List) map.get(label); + } + + public List getBooleanList(String label, List def) { + return (List) ((map.get(label) != null)?map.get(label):def); + } + + public YAMLSection getSection(String label) { + return (map.get(label) != null)?(new YAMLSection((Map) map.get(label), this, label, yaml)):null; + } + + public YAMLSection getSection(String label, Map def) { + return new YAMLSection((Map) ((map.get(label) != null)?map.get(label):def), this, label, yaml); + } + + public YAMLSection getSection(String label, YAMLSection def) { + return (map.get(label) != null)?(new YAMLSection((Map) map.get(label), this, label, yaml)):def; + } + + public List getSectionList(String label) { + if (map.get(label) != null) { + List values = new ArrayList(); + for (Map value : (List>) map.get(label)) { + values.add(new YAMLSection(value, null, null, yaml)); + } + return values; + } else { + return null; + } + } + + public List getSectionList(String label, Collection> def) { + if (map.get(label) != null) { + return getSectionList(label); + } else { + List values = new ArrayList(); + for (Map value : def) { + values.add(new YAMLSection(value, null, null, yaml)); + } + return values; + } + } + + public List getSectionList(String label, List def) { + if (map.get(label) != null) { + return getSectionList(label); + } else { + List values = new ArrayList(); + for (YAMLSection value : def) { + values.add(value); + } + return values; + } + } + + public double getDouble(String label) { + return (double) map.get(label); + } + + public double getDouble(String label, double def) { + return (double) ((map.get(label) != null)?map.get(label):def); + } + + public List getDoubleList(String label) { + return (List) map.get(label); + } + + public List getDoubleList(String label, List def) { + return (List) ((map.get(label) != null)?map.get(label):def); + } + + public float getFloat(String label) { + return (float) map.get(label); + } + + public float getFloat(String label, float def) { + return (float) ((map.get(label) != null)?map.get(label):def); + } + + public List getFloatList(String label) { + return (List) map.get(label); + } + + public List getFloatList(String label, float def) { + return (List) ((map.get(label) != null)?map.get(label):def); + } + + public int getInt(String label) { + return (int) map.get(label); + } + + public int getInt(String label, int def) { + return (int) ((map.get(label) != null)?map.get(label):def); + } + + public List getIntList(String label) { + return (List) map.get(label); + } + + public List getIntList(String label, List def) { + return (List) ((map.get(label) != null)?map.get(label):def); + } + + public long getLong(String label) { + return (long) map.get(label); + } + + public long getLong(String label, long def) { + return (long) ((map.get(label) != null)?map.get(label):def); + } + + public List getLongList(String label) { + return (List) map.get(label); + } + + public List getLongList(String label, List def) { + return (List) ((map.get(label) != null)?map.get(label):def); + } + + public short getShort(String label) { + return (short) map.get(label); + } + + public short getShort(String label, short def) { + return (short) ((map.get(label) != null)?map.get(label):def); + } + + public List getShortList(String label) { + return (List) map.get(label); + } + + public List getShortList(String label, List def) { + return (List) ((map.get(label) != null)?map.get(label):def); + } + + public String getRawString(String label) { + return (String) map.get(label); + } + + public String getRawString(String label, String def) { + return (String) ((map.get(label) != null)?map.get(label):def); + } + + public List getRawStringList(String label) { + return (List) map.get(label); + } + + public List getRawStringList(String label, List def) { + return (List) ((map.get(label) != null)?map.get(label):def); + } + + public String getString(String label) { + return (map.get(label) != null)?unescapeJavaString((String) map.get(label)):null; + } + + public String getString(String label, String def) { + return unescapeJavaString((String) ((map.get(label) != null) ? map.get(label) : def)); + } + + public List getStringList(String label) { + if (map.get(label) != null) { + List values = new ArrayList(); + for (String value : (List) map.get(label)) { + values.add(unescapeJavaString(value)); + } + return values; + } else { + return null; + } + } + + public List getStringList(String label, List def) { + if (map.get(label) != null) { + return getStringList(label); + } else { + List values = new ArrayList(); + for (String value : def) { + values.add(unescapeJavaString(value)); + } + return values; + } + } + + public String getColoredString(String label, char color) { + return (map.get(label) != null)? ChatColor.translateAlternateColorCodes(color, unescapeJavaString((String) map.get(label))):null; + } + + public String getColoredString(String label, String def, char color) { + return ChatColor.translateAlternateColorCodes(color, unescapeJavaString((String) ((map.get(label) != null) ? map.get(label) : def))); + } + + public List getColoredStringList(String label, char color) { + if (map.get(label) != null) { + List values = new ArrayList(); + for (String value : (List) map.get(label)) { + values.add(ChatColor.translateAlternateColorCodes(color, unescapeJavaString(value))); + } + return values; + } else { + return null; + } + } + + public List getColoredStringList(String label, List def, char color) { + if (map.get(label) != null) { + return getColoredStringList(label, color); + } else { + List values = new ArrayList(); + for (String value : def) { + values.add(ChatColor.translateAlternateColorCodes(color, unescapeJavaString(value))); + } + return values; + } + } + + public boolean isBoolean(String label) { + return (map.get(label) instanceof Boolean); + } + + public boolean isSection(String label) { + return (map.get(label) instanceof Map); + } + + public boolean isDouble(String label) { + return (map.get(label) instanceof Double); + } + + public boolean isFloat(String label) { + return (map.get(label) instanceof Float); + } + + public boolean isInt(String label) { + return (map.get(label) instanceof Integer); + } + + public boolean isList(String label) { + return (map.get(label) instanceof List); + } + + public boolean isLong(String label) { + return (map.get(label) instanceof Long); + } + + public boolean isString(String label) { + return (map.get(label) instanceof String); + } + + static String unescapeJavaString(String str) { + + StringBuilder sb = new StringBuilder(str.length()); + + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + if (ch == '\\') { + char nextChar = (i == str.length() - 1) ? '\\' : str + .charAt(i + 1); + // Octal escape? + if (nextChar >= '0' && nextChar <= '7') { + String code = "" + nextChar; + i++; + if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' + && str.charAt(i + 1) <= '7') { + code += str.charAt(i + 1); + i++; + if ((i < str.length() - 1) && str.charAt(i + 1) >= '0' + && str.charAt(i + 1) <= '7') { + code += str.charAt(i + 1); + i++; + } + } + sb.append((char) Integer.parseInt(code, 8)); + continue; + } + switch (nextChar) { + case '\\': + ch = '\\'; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + case '\"': + ch = '\"'; + break; + case '\'': + ch = '\''; + break; + // Hex Unicode: u???? + case 'u': + if (i >= str.length() - 5) { + ch = 'u'; + break; + } + int code = Integer.parseInt( + "" + str.charAt(i + 2) + str.charAt(i + 3) + + str.charAt(i + 4) + str.charAt(i + 5), 16); + sb.append(Character.toChars(code)); + i += 5; + continue; + } + i++; + } + sb.append(ch); + } + return sb.toString(); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLValue.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLValue.java new file mode 100644 index 00000000..c5567135 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Config/YAMLValue.java @@ -0,0 +1,156 @@ +package net.ME1312.SubServers.Proxy.Libraries.Config; + +import net.md_5.bungee.api.ChatColor; +import org.yaml.snakeyaml.Yaml; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@SuppressWarnings({"unchecked", "unused"}) +public class YAMLValue { + protected Object obj; + protected String label; + protected YAMLSection up; + private Yaml yaml; + + public YAMLValue(Object obj, YAMLSection up, String label, Yaml yaml) { + this.obj = obj; + this.label = label; + this.yaml = yaml; + this.up = up; + } + + public YAMLSection getDefiningSection() { + return up; + } + + public Object asObject() { + return obj; + } + + public List asObjectList() { + return (List) obj; + } + + public boolean asBoolean() { + return (boolean) obj; + } + + public List asBooleanList() { + return (List) obj; + } + + public YAMLSection asSection() { + return new YAMLSection((Map) obj, up, label, yaml); + } + + public List asSectionList() { + List values = new ArrayList(); + for (Map value : (List>) obj) { + values.add(new YAMLSection(value, null, null, yaml)); + } + return values; + } + + public double asDouble() { + return (double) obj; + } + + public List asDoubleList() { + return (List) obj; + } + + public float asFloat() { + return (float) obj; + } + + public List asFloatList() { + return (List) obj; + } + + public int asInt() { + return (int) obj; + } + + public List asIntList() { + return (List) obj; + } + + public long asLong() { + return (long) obj; + } + + public List asLongList() { + return (List) obj; + } + + public String asRawString() { + return (String) obj; + } + + public List asRawStringList() { + return (List) obj; + } + + public String asString() { + return YAMLSection.unescapeJavaString((String) obj); + } + + public List asStringList() { + List values = new ArrayList(); + for (String value : (List) obj) { + values.add(YAMLSection.unescapeJavaString(value)); + } + return values; + } + + public String asColoredString(char color) { + return ChatColor.translateAlternateColorCodes(color, YAMLSection.unescapeJavaString((String) obj)); + } + + public List asColoredStringList(char color) { + List values = new ArrayList(); + for (String value : (List) obj) { + values.add(ChatColor.translateAlternateColorCodes(color, YAMLSection.unescapeJavaString(value))); + } + return values; + } + + public boolean isBoolean() { + return (obj instanceof Boolean); + } + + public boolean isSection() { + return (obj instanceof Map); + } + + public boolean isDouble() { + return (obj instanceof Double); + } + + public boolean isFloat(String path) { + return (obj instanceof Float); + } + + public boolean isInt() { + return (obj instanceof Integer); + } + + public boolean isList() { + return (obj instanceof List); + } + + public boolean isLong() { + return (obj instanceof Long); + } + + public boolean isString() { + return (obj instanceof String); + } + + @Override + public String toString() { + return obj.toString(); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Container.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Container.java new file mode 100644 index 00000000..4b6b08a0 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Container.java @@ -0,0 +1,37 @@ +package net.ME1312.SubServers.Proxy.Libraries; + +/** + * Container Class + * + * @author ME1312 + */ +public class Container { + private T obj; + + /** + * Creates a Container + * + * @param item Object to Store + */ + public Container(T item) { + obj = item; + } + + /** + * Grabs the Object + * + * @return The Object + */ + public T get() { + return obj; + } + + /** + * Overwrite the Object + * + * @param value Object to Store + */ + public void set(T value) { + obj = value; + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/IllegalPacketException.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/IllegalPacketException.java new file mode 100644 index 00000000..71591d40 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/IllegalPacketException.java @@ -0,0 +1,8 @@ +package net.ME1312.SubServers.Proxy.Libraries.Exception; + +public class IllegalPacketException extends IllegalStateException { + public IllegalPacketException() {} + public IllegalPacketException(String s) { + super(s); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidDriverException.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidDriverException.java new file mode 100644 index 00000000..190b46ee --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidDriverException.java @@ -0,0 +1,8 @@ +package net.ME1312.SubServers.Proxy.Libraries.Exception; + +public class InvalidDriverException extends IllegalStateException { + public InvalidDriverException() {} + public InvalidDriverException(String s) { + super(s); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidHostException.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidHostException.java new file mode 100644 index 00000000..68bf6320 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidHostException.java @@ -0,0 +1,8 @@ +package net.ME1312.SubServers.Proxy.Libraries.Exception; + +public class InvalidHostException extends IllegalStateException { + public InvalidHostException() {} + public InvalidHostException(String s) { + super(s); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidServerException.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidServerException.java new file mode 100644 index 00000000..fb6021b8 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Exception/InvalidServerException.java @@ -0,0 +1,8 @@ +package net.ME1312.SubServers.Proxy.Libraries.Exception; + +public class InvalidServerException extends IllegalStateException { + public InvalidServerException() {} + public InvalidServerException(String s) { + super(s); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/build.sh b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/build.sh new file mode 100644 index 00000000..4fc78dda --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/build.sh @@ -0,0 +1,156 @@ +# Version: 2.11.0a+ +# +# SubCreator Build Script +# Usage: "bash build.sh [jre]" +# +#!/usr/bin/env bash +if [ -z "$1" ] + then + echo ERROR: No Build Version Supplied + rm -Rf build-subserver.sh + exit 1 +fi +if [ -z "$2" ] + then + echo ERROR: No Server Software Supplied + rm -Rf build-subserver.sh + exit 1 +fi +echo ---------- SERVER BUILD START ---------- +if [ $2 == bukkit ] || [ $2 == spigot ] + then + echo Downloading Buildtools... + curl -o BuildTools.jar https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar; retvalb=$? + if [ $retvalb -eq 0 ]; then + echo Downloaded Buildtools! + else + echo ERROR: Failed Downloading Buildtools. Is SpigotMC.org down? + rm -Rf build-subserver.sh + exit 1 + fi + if [ -d "Buildtools" ] + then + rm -Rf Buildtools + fi + mkdir Buildtools + cd "Buildtools" + echo Building CraftBukkit/Spigot Jarfiles... + export MAVEN_OPTS="-Xmx2G" + if [ -z "$3" ] + then + java -Xmx2G -jar ../BuildTools.jar --rev $1; retvalc=$? + else + HOME=$3 java -Xmx2G -jar ../BuildTools.jar --rev $1; retvalc=$? + fi + cd ../ + if [ $retvalc -eq 0 ]; then + echo CraftBukkit/Spigot Jarfiles Built! + if [ $2 == "spigot" ]; then + cp Buildtools/spigot-*.jar Spigot.jar + else + cp Buildtools/craftbukkit-*.jar Craftbukkit.jar + fi + echo Added Jarfiles! + echo Cleaning Up... + rm -Rf BuildTools.jar + rm -Rf Buildtools + echo ---------- END SERVER BUILD ---------- + rm -Rf build-subserver.sh + exit 0 + else + echo ERROR: Buildtools exited with an error. Please try again + rm -Rf BuildTools.jar + rm -Rf Buildtools + rm -Rf build-subserver.sh + exit 1 + fi +else + if [ $2 == "vanilla" ]; then + if [ -d "Buildtools" ] + then + rm -Rf Buildtools + fi + mkdir Buildtools + mkdir Buildtools/Vanilla + echo Downloading Vanilla Server Jarfile + curl -o Buildtools/Vanilla/minecraft_server.$1.jar https://s3.amazonaws.com/Minecraft.Download/versions/$1/minecraft_server.$1.jar; retvald=$? + if [ $retvald -eq 0 ]; then + echo Downloading Vanilla Patches... + curl -o Buildtools/Vanilla/bungee-patch.jar http://minecraft.me1312.net/lib/subservers/vanilla-bungee-patch.1.2.jar; retvale=$? + if [ $retvale -eq 0 ]; then + echo Patching Vanilla for BungeeCord Support + cd Buildtools/Vanilla + java -jar bungee-patch.jar $1; retvalf=$?; + if [ $retvalf -eq 0 ]; then + echo Patched Vanilla Jar! + cd ../../ + cp Buildtools/Vanilla/out/$1-bungee.jar Buildtools/vanilla-$1.jar + cp Buildtools/Vanilla/out/$1-bungee.jar Vanilla.jar + echo Added Jarfiles! + echo Cleaning Up... + rm -Rf Buildtools + echo ---------- END SERVER BUILD ---------- + rm -Rf build-subserver.sh + exit 0 + else + echo ERROR: Failed Applying Patch. + rm -Rf Buildtools + rm -Rf build-subserver.sh + exit 1 + fi + else + echo ERROR: Failed Downloading Patch. Is Dropbox.com down? + rm -Rf Buildtools + rm -Rf build-subserver.sh + exit 1 + fi + else + echo ERROR: Failed Downloading Jarfile. Is Minecraft.net down? + rm -Rf Buildtools + rm -Rf build-subserver.sh + exit 1 + fi + else + if [ $2 == "sponge" ]; then + IFS='::' read -r -a version <<< "$1" + sversion=$(echo ${version[@]:1} | tr -d ' ') + echo Downloading Minecraft Forge + curl -o forge-${version[0]}-installer.jar http://files.minecraftforge.net/maven/net/minecraftforge/forge/${version[0]}/forge-${version[0]}-installer.jar; retvalg=$? + if [ $retvalg -eq 0 ]; then + echo Installing Minecraft Forge Server + java -jar ./forge-${version[0]}-installer.jar --installServer; retvalh=$? + if [ $retvalh -eq 0 ]; then + mkdir ./mods + echo Downloading SpongeForge + curl -o mods/Sponge.jar http://files.minecraftforge.net/maven/org/spongepowered/spongeforge/$sversion/spongeforge-$sversion.jar; retvali=$? + if [ $retvali -eq 0 ]; then + echo Cleaning Up... + rm -Rf forge-${version[0]}-installer.jar + rm -Rf forge-${version[0]}-installer.jar.log + mv -f forge-${version[0]}-universal.jar Forge.jar + echo ---------- END SERVER BUILD ---------- + rm -Rf build-subserver.sh + exit 0 + else + echo ERROR: Failed Downloading Jarfile. Is MinecraftForge.net down? + rm -Rf forge-${version[0]}-installer.jar + rm -Rf forge-${version[0]}-installer.jar.log + rm -Rf forge-${version[0]}-universal.jar + rm -Rf build-subserver.sh + exit 1 + fi + else + echo ERROR: Failed Installing Forge. + rm -Rf forge-${version[0]}-installer.jar + rm -Rf forge-${version[0]}-installer.jar.log + rm -Rf build-subserver.sh + exit 1 + fi + else + echo ERROR: Failed Downloading Jarfile. Is MinecraftForge.net down? + rm -Rf build-subserver.sh + exit 1 + fi + fi + fi +fi \ No newline at end of file diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/config.yml b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/config.yml new file mode 100644 index 00000000..6b058469 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/config.yml @@ -0,0 +1,30 @@ +Settings: + Version: '2.11.0a+' + Git-Bash: 'C:\Program Files\Git' + Log-Creator: true + Enable-Graphic-Interface: true + Update-External-Files: true + SubData: + Address: '127.0.0.1:4391' + Password: 'password123' + Allowed-Connections: [] + +Hosts: + '~': + Enabled: true + Driver: 'BUILT-IN' + Address: '127.0.0.1' + Directory: './' + +Servers: + 'Server_1': + Enabled: false + Host: '~' + Port: 25566 + Motd: '&aThis is a SubServer' + Log: true + Directory: '~/Server_1' + Executable: 'java -Djline.terminal=jline.UnsupportedTerminal -jar Spigot.jar' + Stop-Command: 'stop' + Run-On-Launch: false + Auto-Restart: false \ No newline at end of file diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/lang.yml b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/lang.yml new file mode 100644 index 00000000..d9022879 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Files/lang.yml @@ -0,0 +1,3 @@ +Version: '2.11.0a+' +Lang: + 'Console-Only-Command': '&4Console Access Only.' \ No newline at end of file diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/UniversalFile.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/UniversalFile.java new file mode 100644 index 00000000..29e36c80 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/UniversalFile.java @@ -0,0 +1,72 @@ +package net.ME1312.SubServers.Proxy.Libraries; + +import java.io.File; + +/** + * Universal File Class + * + * @author ME1312 + */ +public class UniversalFile extends File { + + /** + * Creates a File Link. Path names are separated by ':' + * + * @param pathname Path name + */ + public UniversalFile(String pathname) { + super(pathname.replace(".:", System.getProperty("user.dir") + ":").replace(':', File.separatorChar)); + } + + /** + * Creates a File Link. Path names are separated by the divider + * + * @param pathname Path name + * @param divider Divider to use + */ + public UniversalFile(String pathname, char divider) { + super(pathname.replace("." + divider, System.getProperty("user.dir") + divider).replace(divider, File.separatorChar)); + } + + /** + * Creates a File Link. + * + * @see File + * @param file File + */ + public UniversalFile(File file) { + super(file.getPath()); + } + + /** + * Creates a File. Path names are separated by the ':' + * + * @see File + * @param parent Parent File + * @param child Path name + */ + public UniversalFile(File parent, String child) { + super(parent, child.replace(':', File.separatorChar)); + } + + /** + * Creates a File. Path names are separated by the divider + * + * @see File + * @param parent Parent File + * @param child Path name + * @param divider Divider to use + */ + public UniversalFile(File parent, String child, char divider) { + super(parent, child.replace(divider, File.separatorChar)); + } + + /** + * Gets the Universal File Path (separated by ':') + * + * @return + */ + public String getUniversalPath() { + return getPath().replace(File.separatorChar, ':'); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Version/Version.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Version/Version.java new file mode 100644 index 00000000..03cae495 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Version/Version.java @@ -0,0 +1,224 @@ +package net.ME1312.SubServers.Proxy.Libraries.Version; + +import java.io.Serializable; + +/** + * Version Class + * + * @author ME1312 + */ +@SuppressWarnings("serial") +public class Version implements Serializable, Comparable { + private String string; + + /** + * Creates a Version + * + * @param string Version String + */ + public Version(String string) { + this.string = string; + } + + + /** + * Creates a Version + * + * @param ints Version Numbers (Will be separated with dots) + */ + public Version(Integer... ints) { + String string = Integer.toString(ints[0]); + int i = 0; + if (ints.length != 1) { + do { + i++; + string = string + "." + ints[i]; + } while ((i + 1) != ints.length); + } + this.string = string; + } + + @Override + public String toString() { + return string; + } + + /** + * See if Versions are Equal + * + * @param version Version to Compare + * @return + */ + public boolean equals(Version version) { + return compareTo(version) == 0; + } + + /* + * Returns 1 if Greater than + * Returns 0 if Equal + * Returns -1 if Less than + *//** + * + * Compare Versions + * + * @param version The version to compare to + */ + public int compareTo(Version version) { + String version1 = this.string; + String version2 = version.toString(); + + VersionTokenizer tokenizer1 = new VersionTokenizer(version1); + VersionTokenizer tokenizer2 = new VersionTokenizer(version2); + + int number1 = 0, number2 = 0; + String suffix1 = "", suffix2 = ""; + + while (tokenizer1.MoveNext()) { + if (!tokenizer2.MoveNext()) { + do { + number1 = tokenizer1.getNumber(); + suffix1 = tokenizer1.getSuffix(); + if (number1 != 0 || suffix1.length() != 0) { + // Version one is longer than number two, and non-zero + return 1; + } + } + while (tokenizer1.MoveNext()); + + // Version one is longer than version two, but zero + return 0; + } + + number1 = tokenizer1.getNumber(); + suffix1 = tokenizer1.getSuffix(); + number2 = tokenizer2.getNumber(); + suffix2 = tokenizer2.getSuffix(); + + if (number1 < number2) { + // Number one is less than number two + return -1; + } + if (number1 > number2) { + // Number one is greater than number two + return 1; + } + + boolean empty1 = suffix1.length() == 0; + boolean empty2 = suffix2.length() == 0; + + if (empty1 && empty2) continue; // No suffixes + if (empty1) return 1; // First suffix is empty (1.2 > 1.2b) + if (empty2) return -1; // Second suffix is empty (1.2a < 1.2) + + // Lexical comparison of suffixes + int result = suffix1.compareTo(suffix2); + if (result != 0) return result; + + } + if (tokenizer2.MoveNext()) { + do { + number2 = tokenizer2.getNumber(); + suffix2 = tokenizer2.getSuffix(); + if (number2 != 0 || suffix2.length() != 0) { + // Version one is longer than version two, and non-zero + return -1; + } + } + while (tokenizer2.MoveNext()); + + // Version two is longer than version one, but zero + return 0; + } + return 0; + } + + /** + * See if Versions are Equal + * + * @param ver1 Version to Compare + * @param ver2 Version to Compare + * @return + */ + public static boolean isEqual(Version ver1, Version ver2) { + return compare(ver1, ver2) == 0; + } + + /* + * Returns 1 if Greater than + * Returns 0 if Equal + * Returns -1 if Less than + *//** + * Compare Versions + * + * @param ver1 Version to Compare + * @param ver2 Version to Compare + */ + public static int compare(Version ver1, Version ver2) { + String version1 = ver1.toString(); + String version2 = ver2.toString(); + + VersionTokenizer tokenizer1 = new VersionTokenizer(version1); + VersionTokenizer tokenizer2 = new VersionTokenizer(version2); + + int number1 = 0, number2 = 0; + String suffix1 = "", suffix2 = ""; + + while (tokenizer1.MoveNext()) { + if (!tokenizer2.MoveNext()) { + do { + number1 = tokenizer1.getNumber(); + suffix1 = tokenizer1.getSuffix(); + if (number1 != 0 || suffix1.length() != 0) { + // Version one is longer than number two, and non-zero + return 1; + } + } + while (tokenizer1.MoveNext()); + + // Version one is longer than version two, but zero + return 0; + } + + number1 = tokenizer1.getNumber(); + suffix1 = tokenizer1.getSuffix(); + number2 = tokenizer2.getNumber(); + suffix2 = tokenizer2.getSuffix(); + + if (number1 < number2) { + // Number one is less than number two + return -1; + } + if (number1 > number2) { + // Number one is greater than number two + return 1; + } + + boolean empty1 = suffix1.length() == 0; + boolean empty2 = suffix2.length() == 0; + + if (empty1 && empty2) continue; // No suffixes + if (empty1) return 1; // First suffix is empty (1.2 > 1.2b) + if (empty2) return -1; // Second suffix is empty (1.2a < 1.2) + + // Lexical comparison of suffixes + int result = suffix1.compareTo(suffix2); + if (result != 0) return result; + + } + if (tokenizer2.MoveNext()) { + do { + number2 = tokenizer2.getNumber(); + suffix2 = tokenizer2.getSuffix(); + if (number2 != 0 || suffix2.length() != 0) { + // Version one is longer than version two, and non-zero + return -1; + } + } + while (tokenizer2.MoveNext()); + + // Version two is longer than version one, but zero + return 0; + } + return 0; + } +} \ No newline at end of file diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Version/VersionTokenizer.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Version/VersionTokenizer.java new file mode 100644 index 00000000..34ecf2e6 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Libraries/Version/VersionTokenizer.java @@ -0,0 +1,64 @@ +package net.ME1312.SubServers.Proxy.Libraries.Version; + +public final class VersionTokenizer { + private final String _versionString; + private final int _length; + + private int _position; + private int _number; + private String _suffix; + private boolean _hasValue; + + protected int getNumber() { + return _number; + } + + protected String getSuffix() { + return _suffix; + } + + protected boolean hasValue() { + return _hasValue; + } + + protected VersionTokenizer(String versionString) { + if (versionString == null) + throw new IllegalArgumentException("versionString is null"); + + _versionString = versionString; + _length = versionString.length(); + } + + protected boolean MoveNext() { + _number = 0; + _suffix = ""; + _hasValue = false; + + // No more characters + if (_position >= _length) + return false; + + _hasValue = true; + + while (_position < _length) { + char c = _versionString.charAt(_position); + if (c < '0' || c > '9') break; + _number = _number * 10 + (c - '0'); + _position++; + } + + int suffixStart = _position; + + while (_position < _length) { + char c = _versionString.charAt(_position); + if (c == '.') break; + _position++; + } + + _suffix = _versionString.substring(suffixStart, _position); + + if (_position < _length) _position++; + + return true; + } +} \ No newline at end of file diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Client.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Client.java new file mode 100644 index 00000000..66547214 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Client.java @@ -0,0 +1,211 @@ +package net.ME1312.SubServers.Proxy.Network; + +import net.ME1312.SubServers.Proxy.Libraries.Exception.IllegalPacketException; +import net.ME1312.SubServers.Proxy.Network.Packet.PacketAuthorization; +import net.ME1312.SubServers.Proxy.SubPlugin; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.net.Socket; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +/** + * Network Client Class + * + * @author ME1312 + */ +public class Client { + private Socket socket; + private SocketAddress address; + private ClientHandler handler; + private List queue = new ArrayList(); + private Timer authorized; + private SubPlugin plugin; + private Client instance; + + /** + * Network Client + * + * @param plugin SubPlugin + * @param client Socket to Bind + */ + public Client(SubPlugin plugin, Socket client) { + this.plugin = plugin; + socket = client; + address = client.getRemoteSocketAddress(); + instance = this; + authorized = new Timer("auth" + client.getRemoteSocketAddress().toString()); + authorized.schedule(new TimerTask() { + @Override + public void run() { + if (!socket.isClosed()) try { + plugin.subdata.removeClient(instance); + } catch (IOException e) { + e.printStackTrace(); + } + } + }, 15000); + loop(); + } + + /** + * Network Loop + */ + protected void loop() { + new Thread() { + public void run() { + try { + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + String input; + while ((input = in.readLine()) != null) { + try { + JSONObject json = new JSONObject(input); + PacketIn packet = plugin.subdata.decodePacket(json); + if (authorized == null || packet instanceof PacketAuthorization) { + try { + packet.execute(instance, (json.keySet().contains("c")) ? json.getJSONObject("c") : null); + } catch (Exception e) { + new InvocationTargetException(e, "Exception while executing PacketIn").printStackTrace(); + } + } + } catch (IllegalPacketException e) { + e.printStackTrace(); + } catch (JSONException e) { + new IllegalPacketException("Unknown Packet Format: " + input).printStackTrace(); + } + } + try { + plugin.subdata.removeClient(instance); + } catch (IOException e1) { + e1.printStackTrace(); + } + } catch (Exception e) { + if (e.getMessage() == null || !e.getMessage().equals("Socket closed")) e.printStackTrace(); + try { + plugin.subdata.removeClient(instance); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + }.start(); + new Thread() { + public void run() { + try { + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + while (!socket.isClosed()) { + while (queue.size() > 0) { + try { + out.println(plugin.subdata.encodePacket(queue.get(0))); + queue.remove(0); + } catch (IllegalPacketException e) { + e.printStackTrace(); + } + } + sleep(100); + } + try { + plugin.subdata.removeClient(instance); + } catch (IOException e1) { + e1.printStackTrace(); + } + } catch (Exception e) { + if (e.getMessage() == null || !e.getMessage().equals("Socket closed")) e.printStackTrace(); + try { + plugin.subdata.removeClient(instance); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + }.start(); + } + + /** + * Authorize Connection + */ + public void authorize() { + if (authorized != null) { + authorized.cancel(); + System.out.println("SubData > " + socket.getRemoteSocketAddress().toString() + " logged in"); + } + authorized = null; + } + + /** + * Send Packet to Client + * + * @param packet Packet to send + */ + public void sendPacket(PacketOut packet) { + queue.add(packet); + } + + /** + * Get Raw Connection + * + * @return Socket + */ + public Socket getConnection() { + return socket; + } + + /** + * Get Remote Address + * + * @return Address + */ + public SocketAddress getAddress() { + return address; + } + + /** + * If the connection is authorized + * + * @return Authorization Status + */ + public boolean isAuthorized() { + return authorized == null; + } + + /** + * Gets the Linked Handler + * + * @return Handler + */ + public ClientHandler getHandler() { + return handler; + } + + /** + * Sets the Handler + * Warning: This method should only be called by ClientHandler methods + * + * @see ClientHandler + * @param obj Handler + */ + public void setHandler(ClientHandler obj) { + handler = obj; + } + + /** + * Disconnects the Client + * + * @throws IOException + */ + protected void disconnect() throws IOException { + if (!socket.isClosed()) getConnection().close(); + if (handler != null) handler.linkSubDataClient(null); + handler = null; + + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/ClientHandler.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/ClientHandler.java new file mode 100644 index 00000000..51879f82 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/ClientHandler.java @@ -0,0 +1,22 @@ +package net.ME1312.SubServers.Proxy.Network; + +/** + * Client Handler Layout Class + * + * @author ME1312 + */ +public interface ClientHandler { + /** + * Gets the SubData Client + * + * @return SubData Client (or null if not linked) + */ + Client getSubDataClient(); + + /** + * Link a SubData Client to this Object + * + * @param client Client to Link + */ + void linkSubDataClient(Client client); +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/NetworkManager.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/NetworkManager.java new file mode 100644 index 00000000..c1bf665a --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/NetworkManager.java @@ -0,0 +1,250 @@ +package net.ME1312.SubServers.Proxy.Network; + +import net.ME1312.SubServers.Proxy.Libraries.Exception.IllegalPacketException; +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import net.ME1312.SubServers.Proxy.Network.Packet.PacketAuthorization; +import net.ME1312.SubServers.Proxy.Network.Packet.PacketLinkServer; +import net.ME1312.SubServers.Proxy.Network.Packet.PacketRequestServerInfo; +import net.ME1312.SubServers.Proxy.Network.Packet.PacketRequestServers; +import net.ME1312.SubServers.Proxy.SubPlugin; +import org.json.JSONObject; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * NetworkManager Class + * + * @author ME1312 + */ +public final class NetworkManager { + private HashMap, String> pOut = new HashMap, String>(); + private HashMap pIn = new HashMap(); + private HashMap clients = new HashMap(); + private List allowedAddresses = new ArrayList(); + private ServerSocket server; + private SubPlugin plugin; + + /** + * SubServers Network Manager + * + * @param plugin SubPlugin + * @param port Port + * @param backlog Connection Queue + * @param address Bind Address + * @throws IOException + */ + public NetworkManager(SubPlugin plugin, int port, int backlog, InetAddress address) throws IOException { + server = new ServerSocket(port, backlog, address); + this.plugin = plugin; + + allowConnection(address); + loadDefaults(); + } + + private void loadDefaults() { + for (String s : plugin.config.get().getSection("Settings").getSection("SubData").getStringList("Allowed-Connections")) { + try { + allowedAddresses.add(InetAddress.getByName(s)); + } catch (Exception e) { + e.printStackTrace(); + } + } + + registerPacket(new PacketAuthorization(plugin), "Authorization"); + registerPacket(new PacketLinkServer(plugin), "LinkServer"); + registerPacket(new PacketRequestServerInfo(plugin), "RequestServerInfo"); + registerPacket(new PacketRequestServers(plugin), "RequestServers"); + + registerPacket(PacketAuthorization.class, "Authorization"); + registerPacket(PacketLinkServer.class, "LinkServer"); + registerPacket(PacketRequestServerInfo.class, "RequestServerInfo"); + registerPacket(PacketRequestServers.class, "RequestServers"); + } + + /** + * Gets the Server Socket + * + * @return Server Socket + */ + public ServerSocket getServer() { + return server; + } + + /** + * Add a Client to the Network + * + * @param socket Client to add + * @throws IOException + */ + public Client addClient(Socket socket) throws IOException { + if (allowedAddresses.contains(socket.getInetAddress())) { + Client client = new Client(plugin, socket); + System.out.println("SubData > " + client.getAddress().toString() + " has connected"); + clients.put(client.getAddress(), client); + return client; + } else { + System.out.println("SubData > " + socket.getRemoteSocketAddress().toString() + " attempted to connect, but isn't whitelisted"); + socket.close(); + return null; + } + } + + /** + * Grabs a Client from the Network + * + * @param socket Socket to search + * @return Client + */ + public Client getClient(Socket socket) { + return clients.get(socket.getRemoteSocketAddress()); + } + + /** + * Grabs a Client from the Network + * + * @param address Address to search + * @return Client + */ + public Client getClient(SocketAddress address) { + return clients.get(address); + } + + /** + * Remove a Client from the Network + * + * @param client Client to Kick + * @throws IOException + */ + public void removeClient(Client client) throws IOException { + SocketAddress address = client.getAddress(); + if (clients.keySet().contains(address)) { + clients.remove(address); + client.disconnect(); + System.out.println("SubData > " + client.getAddress().toString() + " has disconnected"); + } + } + + /** + * Remove a Client from the Network + * + * @param address Address to Kick + * @throws IOException + */ + public void removeClient(SocketAddress address) throws IOException { + Client client = clients.get(address); + if (clients.keySet().contains(address)) { + clients.remove(address); + client.disconnect(); + System.out.println("SubData > " + client.getAddress().toString() + " has disconnected"); + } + } + + /** + * Register Packet to the Network + * + * @param packet PacketIn to register + * @param handle Handle to Bind + */ + public void registerPacket(PacketIn packet, String handle) { + pIn.put(handle, packet); + } + + /** + * Register Packet to the Network + * + * @param packet PacketOut to register + * @param handle Handle to bind + */ + public void registerPacket(Class packet, String handle) { + pOut.put(packet, handle); + } + + /** + * Broadcast a Packet to everything on the Network + * Warning: There are usually different types of applications on the network at once, they may not recognise the same packet handles + * + * @param packet Packet to send + */ + public void broadcastPacket(PacketOut packet) { + for (Client client : clients.values()) { + client.sendPacket(packet); + } + } + + /** + * Allow Connections from an Address + * + * @param address Address to allow + */ + public void allowConnection(InetAddress address) { + if (!allowedAddresses.contains(address)) allowedAddresses.add(address); + } + + /** + * Deny Connections from an Address + * + * @param address Address to deny + */ + public void denyConnection(InetAddress address) { + allowedAddresses.remove(address); + } + + /** + * JSON Encode PacketOut + * + * @param packet PacketOut + * @return JSON Formatted Packet + * @throws IllegalPacketException + */ + protected JSONObject encodePacket(PacketOut packet) throws IllegalPacketException { + JSONObject json = new JSONObject(); + + if (!pOut.keySet().contains(packet.getClass())) throw new IllegalPacketException("Unknown PacketOut Channel: " + packet.getClass().getCanonicalName()); + if (packet.getVersion().toString() == null) throw new NullPointerException("PacketOut Version cannot be null: " + packet.getClass().getCanonicalName()); + + JSONObject contents = packet.generate(); + json.put("h", pOut.get(packet.getClass())); + json.put("v", packet.getVersion().toString()); + if (contents != null) json.put("c", contents); + return json; + } + + /** + * JSON Decode PacketIn + * + * @param json JSON to Decode + * @return PacketIn + * @throws IllegalPacketException + * @throws InvocationTargetException + */ + protected PacketIn decodePacket(JSONObject json) throws IllegalPacketException, InvocationTargetException { + if (!json.keySet().contains("h") || !json.keySet().contains("v")) throw new IllegalPacketException("Unknown Packet Format: " + json.toString()); + if (!pIn.keySet().contains(json.getString("h"))) throw new IllegalPacketException("Unknown PacketIn Channel: " + json.getString("h")); + + PacketIn packet = pIn.get(json.getString("h")); + if (!new Version(json.getString("v")).equals(packet.getVersion())) throw new IllegalPacketException("Packet Version Mismatch in " + json.getString("h") + ": " + json.getString("v") + "->" + packet.getVersion().toString()); + return packet; + } + + /** + * Drops All Connections and Stops the SubData Listener + * + * @throws IOException + */ + public void destroy() throws IOException { + while(clients.size() > 0) { + removeClient((Client) clients.values().toArray()[0]); + } + server.close(); + System.out.println("SubServers > The SubData Listener has been closed"); + plugin.subdata = null; + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketAuthorization.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketAuthorization.java new file mode 100644 index 00000000..4f1af814 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketAuthorization.java @@ -0,0 +1,47 @@ +package net.ME1312.SubServers.Proxy.Network.Packet; + +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import net.ME1312.SubServers.Proxy.Network.Client; +import net.ME1312.SubServers.Proxy.Network.PacketIn; +import net.ME1312.SubServers.Proxy.Network.PacketOut; +import net.ME1312.SubServers.Proxy.SubPlugin; +import org.json.JSONObject; + +public class PacketAuthorization implements PacketIn, PacketOut { + private SubPlugin plugin; + private boolean response; + private String message; + + public PacketAuthorization(SubPlugin plugin) { + this.plugin = plugin; + } + + public PacketAuthorization(boolean response, String message) { + this.response = response; + this.message = message; + } + + @Override + public JSONObject generate() { + return new JSONObject("{\"r\": " + response + ", \"" + message.replace("\"", "\\\"") + "\"}"); + } + + @Override + public void execute(Client client, JSONObject data) { + try { + if (data.getString("password").equals(plugin.config.get().getSection("Settings").getSection("SubData").getString("Password"))) { + client.authorize(); + client.sendPacket(new PacketAuthorization(true, "Successfully Logged in")); + } else { + client.sendPacket(new PacketAuthorization(false, "Invalid Password")); + } + } catch (Exception e) { + client.sendPacket(new PacketAuthorization(false, e.getClass().getCanonicalName() + ": " + e.getMessage())); + } + } + + @Override + public Version getVersion() { + return new Version("2.11.0a"); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketLinkServer.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketLinkServer.java new file mode 100644 index 00000000..9b05b3ee --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketLinkServer.java @@ -0,0 +1,55 @@ +package net.ME1312.SubServers.Proxy.Network.Packet; + +import net.ME1312.SubServers.Proxy.Host.Server; +import net.ME1312.SubServers.Proxy.Host.SubServer; +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import net.ME1312.SubServers.Proxy.Network.Client; +import net.ME1312.SubServers.Proxy.Network.PacketIn; +import net.ME1312.SubServers.Proxy.Network.PacketOut; +import net.ME1312.SubServers.Proxy.SubPlugin; +import org.json.JSONObject; + +import java.util.Map; + +public class PacketLinkServer implements PacketIn, PacketOut { + private SubPlugin plugin; + private boolean response; + private String message; + + public PacketLinkServer(SubPlugin plugin) { + this.plugin = plugin; + } + + public PacketLinkServer(boolean response, String message) { + this.response = response; + this.message = message; + } + + + @Override + public JSONObject generate() { + return new JSONObject("{\"r\": " + response + ", \"" + message.replace("\"", "\\\"") + "\"}"); + } + + @Override + public void execute(Client client, JSONObject data) { + try { + Map servers = plugin.api.getServers(); + if (servers.keySet().contains(data.getString("name").toLowerCase())) { + Server server = servers.get(data.getString("name").toLowerCase()); + server.linkSubDataClient(client); + System.out.println("SubData > " + client.getAddress().toString() + " has been defined as " + ((server instanceof SubServer)?"SubServer":"Server") + ": " + server.getName()); + client.sendPacket(new PacketLinkServer(true, "Definition Successful")); + } else { + client.sendPacket(new PacketLinkServer(false, "There is no server with that name")); + } + } catch (Exception e) { + client.sendPacket(new PacketLinkServer(false, e.getClass().getCanonicalName() + ": " + e.getMessage())); + } + } + + @Override + public Version getVersion() { + return new Version("2.11.0a"); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketRequestServerInfo.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketRequestServerInfo.java new file mode 100644 index 00000000..6e9f3654 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketRequestServerInfo.java @@ -0,0 +1,62 @@ +package net.ME1312.SubServers.Proxy.Network.Packet; + +import net.ME1312.SubServers.Proxy.Host.Server; +import net.ME1312.SubServers.Proxy.Host.SubServer; +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import net.ME1312.SubServers.Proxy.Network.Client; +import net.ME1312.SubServers.Proxy.Network.PacketIn; +import net.ME1312.SubServers.Proxy.Network.PacketOut; +import net.ME1312.SubServers.Proxy.SubPlugin; +import org.json.JSONObject; + +public class PacketRequestServerInfo implements PacketIn, PacketOut { + private SubPlugin plugin; + private Server server; + + public PacketRequestServerInfo(SubPlugin plugin, Server server) { + this.server = server; + } + + public PacketRequestServerInfo(SubPlugin plugin) { + this.plugin = plugin; + } + + @Override + public JSONObject generate() { + JSONObject json = new JSONObject(); + JSONObject info = new JSONObject(); + json.put("type", (server == null)?"invalid":((server instanceof SubServer)?"subserver":"server")); + + if (server != null && server instanceof SubServer) { + info.put("host", ((SubServer) server).getHost().getName()); + info.put("enabled", ((SubServer) server).isEnabled()); + info.put("log", ((SubServer) server).isLogging()); + info.put("dir", plugin.config.get().getSection("Servers").getSection(server.getName()).getString("Directory")); + info.put("exec", plugin.config.get().getSection("Servers").getSection(server.getName()).getString("Executable")); + info.put("running", ((SubServer) server).isRunning()); + info.put("stop-cmd", ((SubServer) server).getStopCommand()); + info.put("auto-run", plugin.config.get().getSection("Servers").getSection(server.getName()).getBoolean("Run-On-Launch")); + info.put("auto-restart", ((SubServer) server).willAutoRestart()); + info.put("temp", ((SubServer) server).isTemporary()); + } if (server != null) { + info.put("name", server.getName()); + info.put("address", server.getAddress().toString()); + info.put("restricted", server.isRestricted()); + info.put("motd", server.getMotd()); + info.put("subdata", server.getSubDataClient() == null); + } + + json.put("server", info); + return json; + } + + @Override + public void execute(Client client, JSONObject data) { + client.sendPacket(new PacketRequestServerInfo(plugin, plugin.api.getServer(data.getString("server")))); + } + + @Override + public Version getVersion() { + return new Version("2.11.0a"); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketRequestServers.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketRequestServers.java new file mode 100644 index 00000000..c47b0b80 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/Packet/PacketRequestServers.java @@ -0,0 +1,53 @@ +package net.ME1312.SubServers.Proxy.Network.Packet; + +import net.ME1312.SubServers.Proxy.Host.Server; +import net.ME1312.SubServers.Proxy.Host.SubServer; +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import net.ME1312.SubServers.Proxy.Network.Client; +import net.ME1312.SubServers.Proxy.Network.PacketIn; +import net.ME1312.SubServers.Proxy.Network.PacketOut; +import net.ME1312.SubServers.Proxy.SubPlugin; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; + +public class PacketRequestServers implements PacketIn, PacketOut { + private SubPlugin plugin; + + public PacketRequestServers(SubPlugin plugin) { + this.plugin = plugin; + } + + @Override + public JSONObject generate() { + JSONObject json = new JSONObject(); + + List exServers = new ArrayList(); + for (Server server : plugin.exServers.values()) { + exServers.add(server.getName()); + } + json.put("servers", exServers); + + TreeMap> hosts = new TreeMap>(); + for (SubServer server : plugin.api.getSubServers().values()) { + List servers = (hosts.keySet().contains(server.getHost().getName()))?hosts.get(server.getHost().getName()):new ArrayList(); + servers.add(server.getName()); + hosts.put(server.getHost().getName(), servers); + } + json.put("hosts", hosts); + + return json; + } + + @Override + public void execute(Client client, JSONObject data) { + client.sendPacket(this); + } + + @Override + public Version getVersion() { + return new Version("2.11.0a"); + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/PacketIn.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/PacketIn.java new file mode 100644 index 00000000..21f5a8d7 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/PacketIn.java @@ -0,0 +1,27 @@ +package net.ME1312.SubServers.Proxy.Network; + +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import net.ME1312.SubServers.Proxy.Network.Client; +import org.json.JSONObject; + +/** + * PacketIn Layout Class + * + * @author ME1312 + */ +public interface PacketIn { + /** + * Execute Incoming Packet + * + * @param client Client Accepting + * @param data Incoming Data + */ + void execute(Client client, JSONObject data); + + /** + * Get Packet Version + * + * @return Packet Version + */ + Version getVersion(); +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/PacketOut.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/PacketOut.java new file mode 100644 index 00000000..ef9cdd10 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/Network/PacketOut.java @@ -0,0 +1,25 @@ +package net.ME1312.SubServers.Proxy.Network; + +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import org.json.JSONObject; + +/** + * PacketOut Layout Class + * + * @author ME1312 + */ +public interface PacketOut { + /** + * Generate JSON Packet Contents + * + * @return Packet Contents + */ + JSONObject generate(); + + /** + * Get Packet Version + * + * @return Packet Version + */ + Version getVersion(); +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubAPI.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubAPI.java new file mode 100644 index 00000000..7ffca944 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubAPI.java @@ -0,0 +1,150 @@ +package net.ME1312.SubServers.Proxy; + +import net.ME1312.SubServers.Proxy.Host.Server; +import net.ME1312.SubServers.Proxy.Host.Host; +import net.ME1312.SubServers.Proxy.Host.SubServer; +import net.ME1312.SubServers.Proxy.Libraries.UniversalFile; +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import net.ME1312.SubServers.Proxy.Network.NetworkManager; + +import java.util.Map; +import java.util.TreeMap; + +/** + * SubAPI Class + * + * @author ME1312 + */ +public final class SubAPI { + private SubPlugin plugin; + private static SubAPI api; + + protected SubAPI(SubPlugin plugin) { + this.plugin = plugin; + api = this; + } + + /** + * Gets the SubAPI Methods + * + * @return SubAPI + */ + public static SubAPI getInstance() { + return api; + } + + /** + * Gets the SubServers Internals + * + * @deprecated Use SubAPI Methods when available + * @return SubPlugin Internals + */ + @Deprecated + public SubPlugin getInternals() { + return plugin; + } + + /** + * Gets the SubData Network Manager + * + * @return SubData Network Manager + */ + public NetworkManager getSubDataNetwork() { + return plugin.subdata; + } + + /** + * Adds a Driver for Hosts + * + * @param driver Driver to add + * @param handle Handle to Bind + */ + public void addHostDriver(Class driver, String handle) { + if (plugin.hostDrivers.keySet().contains(handle.toLowerCase())) throw new IllegalStateException("Driver already exists: " + handle); + plugin.hostDrivers.put(handle, driver); + } + + /** + * Gets the Hosts + * + * @return Host Map + */ + public Map getHosts() { + return new TreeMap<>(plugin.hosts); + } + + /** + * Gets a Host + * + * @param name Host name + * @return a Host + */ + public Host getHost(String name) { + return getHosts().get(name.toLowerCase()); + } + + /** + * Gets the Servers (including SubServers) + * + * @return Server Map + */ + public Map getServers() { + TreeMap servers = new TreeMap(); + servers.putAll(plugin.exServers); + for (Host host : plugin.hosts.values()) { + servers.putAll(host.getSubServers()); + } + return servers; + } + + /** + * Gets a Server + * + * @param name Server name + * @return a Server + */ + public Server getServer(String name) { + return getServers().get(name.toLowerCase()); + } + + /** + * Gets the SubServers + * + * @return SubServer Map + */ + public Map getSubServers() { + TreeMap servers = new TreeMap(); + for (Host host : plugin.hosts.values()) { + servers.putAll(host.getSubServers()); + } + return servers; + } + + /** + * Gets a SubServer + * + * @param name SubServer name + * @return a SubServer + */ + public SubServer getSubServer(String name) { + return getSubServers().get(name.toLowerCase()); + } + + /** + * Gets the Runtime Directory + * + * @return Directory + */ + public UniversalFile getRuntimeDirectory() { + return plugin.dir; + } + + /** + * Gets the SubServers Version + * + * @return SubServers Version + */ + public Version getWrapperVersion() { + return plugin.version; + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubCommand.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubCommand.java new file mode 100644 index 00000000..9ad2c386 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubCommand.java @@ -0,0 +1,141 @@ +package net.ME1312.SubServers.Proxy; + +import net.ME1312.SubServers.Proxy.Host.SubServer; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.plugin.Command; +import net.md_5.bungee.command.ConsoleCommandSender; + +import java.util.Map; + +/** + * Plugin Command Class + * + * @author ME1312 + */ +public final class SubCommand extends Command { + private SubPlugin plugin; + + public SubCommand(SubPlugin plugin) { + super("subserver", "subservers.console_only", "sub", "subservers"); + this.plugin = plugin; + } + + /** + * Load /Sub in console + * + * @param sender + * @param args + */ + @SuppressWarnings("deprecation") + @Override + public void execute(CommandSender sender, String[] args) { + if (sender instanceof ConsoleCommandSender) { + if (args.length > 0) { + if (args[0].equalsIgnoreCase("help") || args[0].equalsIgnoreCase("?")) { + sender.sendMessages(printHelp()); + } else if (args[0].equalsIgnoreCase("version") || args[0].equalsIgnoreCase("ver")) { + sender.sendMessage("SubServers > SubServers.Proxy is running version " + plugin.version.toString()); + } else if (args[0].equalsIgnoreCase("list")) { + sender.sendMessages( + "SubServers > Host List:", plugin.hosts.keySet().toString(), + "SubServers > Server List:", plugin.getServers().keySet().toString()); + } else if (args[0].equalsIgnoreCase("start")) { + if (args.length > 1) { + Map servers = plugin.getServers(); + if (!servers.keySet().contains(args[1].toLowerCase())) { + sender.sendMessage("SubServers > There is no server with that name"); + } else if (!(servers.get(args[1].toLowerCase()) instanceof SubServer)) { + sender.sendMessage("SubServers > That Server is not a SubServer"); + } else if (!((SubServer) servers.get(args[1].toLowerCase())).isEnabled()) { + sender.sendMessage("SubServers > That SubServer is not enabled"); + } else if (((SubServer) servers.get(args[1].toLowerCase())).isRunning()) { + sender.sendMessage("SubServers > That SubServer is already running"); + } else { + ((SubServer) servers.get(args[1].toLowerCase())).start(); + } + } else { + sender.sendMessage("SubServers > Usage: /sub start "); + } + } else if (args[0].equalsIgnoreCase("stop")) { + if (args.length > 1) { + Map servers = plugin.getServers(); + if (!servers.keySet().contains(args[1].toLowerCase())) { + sender.sendMessage("SubServers > There is no server with that name"); + } else if (!(servers.get(args[1].toLowerCase()) instanceof SubServer)) { + sender.sendMessage("SubServers > That Server is not a SubServer"); + } else if (!((SubServer) servers.get(args[1].toLowerCase())).isRunning()) { + sender.sendMessage("SubServers > That SubServer is not running"); + } else { + ((SubServer) servers.get(args[1].toLowerCase())).stop(); + } + } else { + sender.sendMessage("SubServers > Usage: /sub stop "); + } + } else if (args[0].equalsIgnoreCase("kill") || args[0].equalsIgnoreCase("terminate")) { + if (args.length > 1) { + Map servers = plugin.getServers(); + if (!servers.keySet().contains(args[1].toLowerCase())) { + sender.sendMessage("SubServers > There is no server with that name"); + } else if (!(servers.get(args[1].toLowerCase()) instanceof SubServer)) { + sender.sendMessage("SubServers > That Server is not a SubServer"); + } else if (!((SubServer) servers.get(args[1].toLowerCase())).isRunning()) { + sender.sendMessage("SubServers > That SubServer is not running"); + } else { + ((SubServer) servers.get(args[1].toLowerCase())).terminate(); + } + } else { + sender.sendMessage("SubServers > Usage: /sub kill "); + } + } else if (args[0].equalsIgnoreCase("cmd") || args[0].equalsIgnoreCase("command")) { + if (args.length > 2) { + Map servers = plugin.getServers(); + if (!servers.keySet().contains(args[1].toLowerCase())) { + sender.sendMessage("SubServers > There is no server with that name"); + } else if (!(servers.get(args[1].toLowerCase()) instanceof SubServer)) { + sender.sendMessage("SubServers > That Server is not a SubServer"); + } else if (!((SubServer) servers.get(args[1].toLowerCase())).isRunning()) { + sender.sendMessage("SubServers > That SubServer is not running"); + } else { + int i = 2; + String str = args[2]; + if (args.length > 3) { + do { + i++; + str = str + " " + args[i]; + } while ((i + 1) != args.length); + } + ((SubServer) servers.get(args[1].toLowerCase())).command(str); + } + } else { + sender.sendMessage("SubServers > Usage: /sub cmd [Args...]"); + } + } else if (args[0].equalsIgnoreCase("create")) { + + } + } else { + sender.sendMessages(printHelp()); + } + } else { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', plugin.lang.get().getSection("Lang").getString("Console-Only-Command"))); + } + } + + public String[] printHelp() { + return new String[]{ + "SubServers > Console Command Help:", + " Help: /sub help", + " List: /sub list", + " Version: /sub version", + " Start Server: /sub start ", + " Stop Server: /sub stop ", + " Terminate Server: /sub kill ", + " Command Server: /sub cmd [Args...]", + " Create Server: /sub create [RAM]", + "", + " To see BungeeCord Supplied Commands, please visit:", + " https://www.spigotmc.org/wiki/bungeecord-commands/" + }; + } +} diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubPlugin.java b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubPlugin.java new file mode 100644 index 00000000..7e62be86 --- /dev/null +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Proxy/SubPlugin.java @@ -0,0 +1,282 @@ +package net.ME1312.SubServers.Proxy; + +import net.ME1312.SubServers.Proxy.Host.Executable; +import net.ME1312.SubServers.Proxy.Host.Server; +import net.ME1312.SubServers.Proxy.Libraries.Config.YAMLConfig; +import net.ME1312.SubServers.Proxy.Libraries.Exception.InvalidHostException; +import net.ME1312.SubServers.Proxy.Libraries.Exception.InvalidServerException; +import net.ME1312.SubServers.Proxy.Host.Host; +import net.ME1312.SubServers.Proxy.Host.SubServer; +import net.ME1312.SubServers.Proxy.Libraries.UniversalFile; +import net.ME1312.SubServers.Proxy.Libraries.Version.Version; +import net.ME1312.SubServers.Proxy.Network.NetworkManager; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.config.ServerInfo; + +import java.io.*; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.util.*; + +/** + * Main Plugin Class + * + * @author ME1312 + */ +public final class SubPlugin extends BungeeCord { + protected final HashMap> hostDrivers = new HashMap>(); + public final HashMap exServers = new HashMap(); + public final HashMap hosts = new HashMap(); + + public final UniversalFile dir = new UniversalFile(new File("./")); + public YAMLConfig config; + public YAMLConfig lang; + public NetworkManager subdata; + public final Version version = new Version("2.11.0a"); + + protected boolean running = false; + public final SubAPI api = new SubAPI(this); + + protected SubPlugin() throws IOException { + enable(); + } + + /** + * Enable Plugin + * + * @throws IOException + */ + protected void enable() throws IOException { + if (running) throw new IllegalStateException("SubServers has already been loaded"); + System.out.println("SubServers > Loading SubServers v" + version.toString() + " Libraries... "); + running = true; + UniversalFile dir = new UniversalFile(this.dir, "SubServers"); + dir.mkdir(); + if (!(new UniversalFile(dir, "config.yml").exists())) { + copyFromJar("net/ME1312/SubServers/Proxy/Libraries/Files/config.yml", new UniversalFile(dir, "config.yml").getPath()); + System.out.println("SubServers > Created ~/SubServers/config.yml"); + } else if ((new Version((new YAMLConfig(new UniversalFile(dir, "config.yml"))).get().getSection("Settings").getString("Version", "0")).compareTo(new Version("2.11.0a+"))) != 0) { + Files.move(new UniversalFile(dir, "config.yml").toPath(), new UniversalFile(dir, "config.old" + Math.round(Math.random() * 100000) + ".yml").toPath()); + + copyFromJar("net/ME1312/SubServers/Proxy/Libraries/Files/config.yml", new UniversalFile(dir, "config.yml").getPath()); + System.out.println("SubServers > Updated ~/SubServers/config.yml"); + } + + if (!(new UniversalFile(dir, "lang.yml").exists())) { + copyFromJar("net/ME1312/SubServers/Proxy/Libraries/Files/lang.yml", new UniversalFile(dir, "lang.yml").getPath()); + System.out.println("SubServers > Created ~/SubServers/lang.yml"); + } else if ((new Version((new YAMLConfig(new UniversalFile(dir, "lang.yml"))).get().getString("Version", "0")).compareTo(new Version("2.11.0a+"))) != 0) { + Files.move(new UniversalFile(dir, "lang.yml").toPath(), new UniversalFile(dir, "lang.old" + Math.round(Math.random() * 100000) + ".yml").toPath()); + copyFromJar("net/ME1312/SubServers/Proxy/Libraries/Files/lang.yml", new UniversalFile(dir, "lang.yml").getPath()); + System.out.println("SubServers > Updated ~/SubServers/lang.yml"); + } + + if (!(new UniversalFile(dir, "build.sh").exists())) { + copyFromJar("net/ME1312/SubServers/Proxy/Libraries/Files/build.sh", new UniversalFile(dir, "build.sh").getPath()); + System.out.println("SubServers > Created ~/SubServers/build.sh"); + } else { + String Version = "null"; + BufferedReader brText = new BufferedReader(new FileReader(new UniversalFile(dir, "build.sh"))); + try { + Version = brText.readLine().split("Version: ")[1]; + } catch (NullPointerException e) {} + brText.close(); + + if (!Version.equalsIgnoreCase("2.11.0a+")) { + Files.move(new UniversalFile(dir, "build.sh").toPath(), new UniversalFile(dir, "build.old" + Math.round(Math.random() * 100000) + ".sh").toPath()); + copyFromJar("net/ME1312/SubServers/Proxy/Libraries/Files/build.sh", new UniversalFile(dir, "build.sh").getPath()); + System.out.println("SubServers > Updated ~/SubServers/build.sh"); + } + } + + hostDrivers.put("built-in", net.ME1312.SubServers.Proxy.Host.Internal.InternalHost.class); + + System.out.println("SubServers > Loading BungeeCord Libraries..."); + } + + /** + * Load Hosts, Servers, SubServers, and SubData. + */ + @Override + public void startListeners() { + try { + config = new YAMLConfig(new UniversalFile(dir, "SubServers:config.yml")); + lang = new YAMLConfig(new UniversalFile(dir, "SubServers:lang.yml")); + subdata = new NetworkManager(this, Integer.parseInt(config.get().getSection("Settings").getSection("SubData").getString("Address", "127.0.0.1:4391").split(":")[1]), 10, + InetAddress.getByName(config.get().getSection("Settings").getSection("SubData").getString("Address", "127.0.0.1:4391").split(":")[0])); + System.out.println("SubServers > SubData Listening on " + subdata.getServer().getLocalSocketAddress().toString()); + loop(); + + long begin = Calendar.getInstance().getTime().getTime(); + int hosts = 0; + System.out.println("SubServers > Loading Hosts..."); + for (String name : config.get().getSection("Hosts").getKeys()) { + try { + if (name.contains(" ")) throw new InvalidHostException("Host names cannot have spaces: " + name); + if (!hostDrivers.keySet().contains(config.get().getSection("Hosts").getSection(name).getString("Driver").toLowerCase())) throw new InvalidHostException("Invalid Driver for host: " + name); + Host host = hostDrivers.get(config.get().getSection("Hosts").getSection(name).getString("Driver").toLowerCase()).getConstructor(SubPlugin.class, String.class, Boolean.class, InetAddress.class, UniversalFile.class).newInstance( + this, name, (Boolean) config.get().getSection("Hosts").getSection(name).getBoolean("Enabled"), InetAddress.getByName(config.get().getSection("Hosts").getSection(name).getString("Address")), new UniversalFile(new File(config.get().getSection("Hosts").getSection(name).getString("Directory")))); + this.hosts.put(name.toLowerCase(), host); + subdata.allowConnection(host.getAddress()); + hosts++; + } catch (Exception e) { + e.printStackTrace(); + } + } + + int servers = 0; + System.out.println("SubServers > Loading Servers..."); + YAMLConfig bungee = new YAMLConfig(new UniversalFile(dir, "config.yml")); + for (String name : bungee.get().getSection("servers").getKeys()) { + try { + Server server = new Server(name, new InetSocketAddress(bungee.get().getSection("servers").getSection(name).getString("address").split(":")[0], + Integer.parseInt(bungee.get().getSection("servers").getSection(name).getString("address").split(":")[1])), bungee.get().getSection("servers").getSection(name).getString("motd"), + bungee.get().getSection("servers").getSection(name).getBoolean("restricted")); + exServers.put(name.toLowerCase(), server); + subdata.allowConnection(server.getAddress().getAddress()); + servers++; + } catch (Exception e) { + e.printStackTrace(); + } + } + + int subservers = 0; + System.out.println("SubServers > Loading SubServers..."); + for (String name : config.get().getSection("Servers").getKeys()) { + try { + if (!this.hosts.keySet().contains(config.get().getSection("Servers").getSection(name).getString("Host").toLowerCase())) throw new InvalidServerException("There is no host with this name:" + name); + SubServer server = this.hosts.get(config.get().getSection("Servers").getSection(name).getString("Host").toLowerCase()).addSubServer(name, config.get().getSection("Servers").getSection(name).getBoolean("Enabled"), + config.get().getSection("Servers").getSection(name).getInt("Port"), config.get().getSection("Servers").getSection(name).getString("Motd"), config.get().getSection("Servers").getSection(name).getBoolean("Log"), + config.get().getSection("Servers").getSection(name).getString("Directory"), new Executable(config.get().getSection("Servers").getSection(name).getString("Executable")), config.get().getSection("Servers").getSection(name).getString("Stop-Command"), + config.get().getSection("Servers").getSection(name).getBoolean("Run-On-Launch"), config.get().getSection("Servers").getSection(name).getBoolean("Auto-Restart"), false); + + subservers++; + } catch (Exception e) { + e.printStackTrace(); + } + } + + System.out.println("SubServers > " + hosts + " Host(s), " + servers + " Server(s), and " + subservers + " SubServer(s) loaded in " + (Calendar.getInstance().getTime().getTime() - begin) + "ms"); + + getPluginManager().registerCommand(null, new SubCommand(this)); + + super.startListeners(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * SubData Listener Loop + */ + public void loop() { + new Thread() { + public void run() { + while(running && subdata != null) { + try { + subdata.addClient(subdata.getServer().accept()); + } catch (IOException e) { + if (e.getMessage() == null || !e.getMessage().equals("Socket closed")) e.printStackTrace(); + } + } + } + }.start(); + } + + /** + * Override BungeeCord Servers + * + * @see SubAPI#getServers() + * @return Server Map + */ + @Override + public Map getServers() { + TreeMap servers = new TreeMap(); + servers.putAll(this.exServers); + for (Host host : this.hosts.values()) { + servers.putAll(host.getSubServers()); + } + return servers; + } + + /** + * Reset all changes made by startListeners + * + * @see SubPlugin#startListeners() + */ + @Override + public void stopListeners() { + try { + System.out.println("SubServers > Resetting Hosts and Server Data"); + List hosts = new ArrayList(); + hosts.addAll(this.hosts.keySet()); + + for (String host : hosts) { + List subservers = new ArrayList(); + subservers.addAll(this.hosts.get(host).getSubServers().keySet()); + + for (String server : subservers) { + this.hosts.get(host).removeSubServer(server); + } + subservers.clear(); + this.hosts.remove(host); + } + hosts.clear(); + exServers.clear(); + + subdata.destroy(); + } catch (Exception e) { + e.printStackTrace(); + } + + super.stopListeners(); + } + + /** + * Disable Plugin + */ + protected void disable() { + if (running) { + running = false; + } + } + + /** + * Override BungeeCord Stop Functions + */ + @Override + public void stop() { + disable(); + super.stop(); + } + + /** + * Override BungeeCord Stop Functions + * + * @param reason Reason + */ + @Override + public void stop(String reason) { + disable(); + super.stop(reason); + } + + private void copyFromJar(String resource, String destination) { + InputStream resStreamIn = SubPlugin.class.getClassLoader().getResourceAsStream(resource); + File resDestFile = new File(destination); + try { + OutputStream resStreamOut = new FileOutputStream(resDestFile); + int readBytes; + byte[] buffer = new byte[4096]; + while ((readBytes = resStreamIn.read(buffer)) > 0) { + resStreamOut.write(buffer, 0, readBytes); + } + resStreamOut.close(); + resStreamIn.close(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + +} diff --git a/SubServers.Client/src/plugin.yml b/SubServers.Client/src/plugin.yml new file mode 100644 index 00000000..2176d8c9 --- /dev/null +++ b/SubServers.Client/src/plugin.yml @@ -0,0 +1,54 @@ +name: 'SubServers' +main: 'net.ME1312.SubServers.Client.SubPlugin' +version: '2.11.0a' +authors: [ME1312] +website: 'http://www.example.com/' +commands: + subservers: + description: All SubServer Commands + usage: /SubServers [Server] + subserver: + description: All SubServer Commands + usage: /SubServer [Server] + sub: + description: All SubServer Commands + usage: /Sub [Server] +permissions: + subserver.*: + description: All Subserver Commands + default: op + children: + subserver.command: + description: Subservers GUI + default: op + subserver.command.*: + description: 'Subserver Commands/Actions' + default: op + children: + subserver.command.create: + description: Creates a SubServer + default: op + subserver.command.start.*: + description: Starts a Subserver + default: op + subserver.command.kill.*: + description: Terminates a Subserver + default: op + subserver.command.stop.*: + description: Stops a Subserver + default: op + subserver.command.send.*: + description: Sends Commands to a Subserver + default: op + subserver.command.edit.*: + description: Edits a SubServer + default: op + subserver.command.teleport.*: + description: Teleport to SubServers + default: op + subserver.command.teleport.others.*: + description: Teleport Others to SubServers + default: op + subserver.command.reload: + description: Reload Subservers Configs + default: op \ No newline at end of file