diff --git a/README.md b/README.md index e6f02e38..706a31d4 100644 --- a/README.md +++ b/README.md @@ -28,5 +28,6 @@ These are some quick links for common resources of SubServers 2. > [https://bstats.org/plugin/bungeecord/SubServers_Bungee](https://bstats.org/plugin/bungeecord/SubServers%202)
> [https://bstats.org/plugin/other/SubServers_Host](https://bstats.org/plugin/other/SubServers%20Host)
> [https://bstats.org/plugin/bungeecord/SubServers_Sync](https://bstats.org/plugin/bungeecord/SubServers%20Sync)
+> [https://bstats.org/plugin/bungeecord/SubServers_Console](https://bstats.org/plugin/bungeecord/SubServers%20Console)
> [https://bstats.org/plugin/bukkit/SubServers_Client](https://bstats.org/plugin/bukkit/SubServers%20Client)
> [https://bstats.org/plugin/sponge/SubServers_Client](https://bstats.org/plugin/sponge/SubServers%20Client) diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/Files/lang.yml b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/Files/lang.yml index 6b15ba0f..ce0bb061 100644 --- a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/Files/lang.yml +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Library/Files/lang.yml @@ -1,4 +1,4 @@ -Version: '2.13.1a+' +Version: '2.13.2c+' Lang: 'Bungee.Feature.Return': '&6Returning to $str$: &r$msg$' 'Bungee.Server.Current': '&6You are currently connected to $str$' @@ -27,9 +27,9 @@ Lang: 'Command.Help.List': ' &7List:&f $str$' 'Command.Help.Version': ' &7Version:&f $str$' 'Command.Help.Info': ' &7Info:&f $str$' - 'Command.Help.Terminate': ' &7Teleport to Server:&f $str$' 'Command.Help.Host.Create': ' &7Create Server:&f $str$' 'Command.Help.SubServer.Start': ' &7Start Server:&f $str$' + 'Command.Help.SubServer.Restart': ' &7Restart Server:&f $str$' 'Command.Help.SubServer.Stop': ' &7Stop Server:&f $str$' 'Command.Help.SubServer.Terminate': ' &7Terminate Server:&f $str$' 'Command.Help.SubServer.Command': ' &7Command Server:&f $str$' @@ -60,6 +60,15 @@ Lang: 'Command.Start.Server-Disabled': '&cSubServers &4&l\u00BB&c That SubServer is not enabled' 'Command.Start.Server-Incompatible': '&cSubServers &4&l\u00BB&c That SubServer cannot start while these server(s) are running: &4$str$' 'Command.Start.Running': '&cSubServers &4&l\u00BB&c That SubServer is already running' + 'Command.Restart': '&aSubServers &2&l\u00BB&a Stopping SubServer' + 'Command.Restart.Finish': '&aSubServers &2&l\u00BB&a Starting SubServer' + 'Command.Restart.Unknown': '&cSubServers &4&l\u00BB&c There is no Server with that name' + 'Command.Restart.Invalid': '&cSubServers &4&l\u00BB&c That Server is not a SubServer' + 'Command.Restart.Disappeared': '&cSubServers &4&l\u00BB&c Could not restart server: That SubServer has disappeared' + 'Command.Restart.Host-Unavailable': '&cSubServers &4&l\u00BB&c Could not restart server: That SubServer\u0027s Host is no longer available' + 'Command.Restart.Host-Disabled': '&cSubServers &4&l\u00BB&c Could not restart server: That SubServer\u0027s Host is no longer enabled' + 'Command.Restart.Server-Disabled': '&cSubServers &4&l\u00BB&c Could not restart server: That SubServer is no longer enabled' + 'Command.Restart.Server-Incompatible': '&cSubServers &4&l\u00BB&c Could not restart server: That SubServer cannot start while these server(s) are running: &4$str$' 'Command.Stop': '&aSubServers &2&l\u00BB&a Stopping SubServer' 'Command.Stop.Unknown': '&cSubServers &4&l\u00BB&c There is no Server with that name' 'Command.Stop.Invalid': '&cSubServers &4&l\u00BB&c That Server is not a SubServer' diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Network/Packet/PacketStopServer.java b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Network/Packet/PacketStopServer.java index 1407e477..81547681 100644 --- a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Network/Packet/PacketStopServer.java +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/Network/Packet/PacketStopServer.java @@ -59,23 +59,54 @@ public class PacketStopServer implements PacketIn, PacketOut { public void execute(Client client, YAMLSection data) { try { Map servers = plugin.api.getServers(); - if (!servers.keySet().contains(data.getRawString("server").toLowerCase())) { + if (!data.getRawString("server").equals("*") && !servers.keySet().contains(data.getRawString("server").toLowerCase())) { client.sendPacket(new PacketStopServer(3, "There is no server with that name", (data.contains("id"))?data.getRawString("id"):null)); - } else if (!(servers.get(data.getRawString("server").toLowerCase()) instanceof SubServer)) { + } else if (!data.getRawString("server").equals("*") && !(servers.get(data.getRawString("server").toLowerCase()) instanceof SubServer)) { client.sendPacket(new PacketStopServer(4, "That Server is not a SubServer", (data.contains("id"))?data.getRawString("id"):null)); - } else if (!((SubServer) servers.get(data.getRawString("server").toLowerCase())).isRunning()) { + } else if (!data.getRawString("server").equals("*") && !((SubServer) servers.get(data.getRawString("server").toLowerCase())).isRunning()) { client.sendPacket(new PacketStopServer(5, "That SubServer is not running", (data.contains("id"))?data.getRawString("id"):null)); - } else if (data.contains("force") && data.getBoolean("force")) { - if (((SubServer) servers.get(data.getRawString("server").toLowerCase())).terminate((data.contains("player"))?UUID.fromString(data.getRawString("player")):null)) { - client.sendPacket(new PacketStopServer(0, "Terminating SubServer", (data.contains("id"))?data.getRawString("id"):null)); + } else if (data.getRawString("server").equals("*")) { + boolean sent = false; + if (data.contains("force") && data.getBoolean("force")) { + for (Server server : servers.values()) { + if (server instanceof SubServer && ((SubServer) server).isRunning()) { + if (((SubServer) server).terminate((data.contains("player"))?UUID.fromString(data.getRawString("player")):null)) { + sent = true; + } + } + } + if (sent) { + client.sendPacket(new PacketStopServer(0, "Terminating SubServers", (data.contains("id"))?data.getRawString("id"):null)); + } else { + client.sendPacket(new PacketStopServer(1, "Couldn't terminate SubServers", (data.contains("id"))?data.getRawString("id"):null)); + } } else { - client.sendPacket(new PacketStopServer(1, "Couldn't terminate SubServer", (data.contains("id"))?data.getRawString("id"):null)); + for (Server server : servers.values()) { + if (server instanceof SubServer && ((SubServer) server).isRunning()) { + if (((SubServer) server).stop((data.contains("player"))?UUID.fromString(data.getRawString("player")):null)) { + sent = true; + } + } + } + if (sent) { + client.sendPacket(new PacketStopServer(0, "Stopping SubServers", (data.contains("id"))?data.getRawString("id"):null)); + } else { + client.sendPacket(new PacketStopServer(1, "Couldn't stop SubServers", (data.contains("id"))?data.getRawString("id"):null)); + } } } else { - if (((SubServer) servers.get(data.getRawString("server").toLowerCase())).stop((data.contains("player"))?UUID.fromString(data.getRawString("player")):null)) { - client.sendPacket(new PacketStopServer(0, "Stopping SubServer", (data.contains("id"))?data.getRawString("id"):null)); + if (data.contains("force") && data.getBoolean("force")) { + if (((SubServer) servers.get(data.getRawString("server").toLowerCase())).terminate((data.contains("player"))?UUID.fromString(data.getRawString("player")):null)) { + client.sendPacket(new PacketStopServer(0, "Terminating SubServer", (data.contains("id"))?data.getRawString("id"):null)); + } else { + client.sendPacket(new PacketStopServer(1, "Couldn't terminate SubServer", (data.contains("id"))?data.getRawString("id"):null)); + } } else { - client.sendPacket(new PacketStopServer(1, "Couldn't stop SubServer", (data.contains("id"))?data.getRawString("id"):null)); + if (((SubServer) servers.get(data.getRawString("server").toLowerCase())).stop((data.contains("player"))?UUID.fromString(data.getRawString("player")):null)) { + client.sendPacket(new PacketStopServer(0, "Stopping SubServer", (data.contains("id"))?data.getRawString("id"):null)); + } else { + client.sendPacket(new PacketStopServer(1, "Couldn't stop SubServer", (data.contains("id"))?data.getRawString("id"):null)); + } } } } catch (Throwable e) { diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubCommand.java b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubCommand.java index 8d96156a..c2b08cb0 100644 --- a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubCommand.java +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubCommand.java @@ -413,15 +413,69 @@ public final class SubCommand extends CommandX { } else { sender.sendMessage("SubServers > Usage: " + label + " " + args[0].toLowerCase() + " "); } - } else if (args[0].equalsIgnoreCase("stop")) { + } else if (args[0].equalsIgnoreCase("restart")) { if (args.length > 1) { + Runnable starter = () -> { + Map servers = plugin.api.getServers(); + if (!servers.keySet().contains(args[1].toLowerCase()) || !(servers.get(args[1].toLowerCase()) instanceof SubServer)) { + sender.sendMessage("SubServers > Could not restart server: That SubServer has disappeared"); + } else if (!((SubServer) servers.get(args[1].toLowerCase())).getHost().isAvailable()) { + sender.sendMessage("SubServers > Could not restart server: That SubServer's Host is no longer available"); + } else if (!((SubServer) servers.get(args[1].toLowerCase())).getHost().isEnabled()) { + sender.sendMessage("SubServers > Could not restart server: That SubServer's Host is no longer enabled"); + } else if (!((SubServer) servers.get(args[1].toLowerCase())).isEnabled()) { + sender.sendMessage("SubServers > Could not restart server: That SubServer is no longer enabled"); + } else if (!((SubServer) servers.get(args[1].toLowerCase())).isRunning()) { + if (((SubServer) servers.get(args[1].toLowerCase())).getCurrentIncompatibilities().size() != 0) { + String list = ""; + for (SubServer server : ((SubServer) servers.get(args[1].toLowerCase())).getCurrentIncompatibilities()) { + if (list.length() != 0) list += ", "; + list += server.getName(); + } + sender.sendMessages("Could not restart server: That SubServer cannot start while these server(s) are running:", list); + } else { + ((SubServer) servers.get(args[1].toLowerCase())).start(); + } + } + }; + Map servers = plugin.api.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()) { + } else if (((SubServer) servers.get(args[1].toLowerCase())).isRunning()) { + new Thread(() -> { + try { + ((SubServer) servers.get(args[1].toLowerCase())).stop(); + ((SubServer) servers.get(args[1].toLowerCase())).waitFor(); + Thread.sleep(100); + starter.run(); + } catch (Exception e) { + e.printStackTrace(); + } + }, "SubServers.Bungee::Server_Restart_Command_Handler(" + servers.get(args[1].toLowerCase()).getName() + ')').start(); + } else { + starter.run(); + } + } else { + sender.sendMessage("SubServers > Usage: " + label + " " + args[0].toLowerCase() + " "); + } + } else if (args[0].equalsIgnoreCase("stop")) { + if (args.length > 1) { + Map servers = plugin.api.getServers(); + if (!args[1].equals("*") && !servers.keySet().contains(args[1].toLowerCase())) { + sender.sendMessage("SubServers > There is no server with that name"); + } else if (!args[1].equals("*") && !(servers.get(args[1].toLowerCase()) instanceof SubServer)) { + sender.sendMessage("SubServers > That Server is not a SubServer"); + } else if (!args[1].equals("*") && !((SubServer) servers.get(args[1].toLowerCase())).isRunning()) { sender.sendMessage("SubServers > That SubServer is not running"); + } else if (args[1].equals("*")) { + for (Server server : servers.values()) { + if (server instanceof SubServer && ((SubServer) server).isRunning()) { + ((SubServer) server).stop(); + } + } } else { ((SubServer) servers.get(args[1].toLowerCase())).stop(); } @@ -431,12 +485,18 @@ public final class SubCommand extends CommandX { } else if (args[0].equalsIgnoreCase("kill") || args[0].equalsIgnoreCase("terminate")) { if (args.length > 1) { Map servers = plugin.api.getServers(); - if (!servers.keySet().contains(args[1].toLowerCase())) { + if (!args[1].equals("*") && !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)) { + } else if (!args[1].equals("*") && !(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()) { + } else if (!args[1].equals("*") && !((SubServer) servers.get(args[1].toLowerCase())).isRunning()) { sender.sendMessage("SubServers > That SubServer is not running"); + } else if (args[1].equals("*")) { + for (Server server : servers.values()) { + if (server instanceof SubServer && ((SubServer) server).isRunning()) { + ((SubServer) server).terminate(); + } + } } else { ((SubServer) servers.get(args[1].toLowerCase())).terminate(); } @@ -555,6 +615,7 @@ public final class SubCommand extends CommandX { " Reload: /sub reload [all|config|templates]", " Info: /sub info [proxy|host|group|server] ", " Start Server: /sub start ", + " Restart Server: /sub restart ", " Stop Server: /sub stop ", " Terminate Server: /sub kill ", " Command Server: /sub cmd [Args...]", @@ -685,16 +746,31 @@ public final class SubCommand extends CommandX { return new NamedContainer<>(null, Collections.emptyList()); } } else if (args[0].equals("start") || - args[0].equals("stop") || - args[0].equals("kill") || args[0].equals("terminate")) { + args[0].equals("restart")) { List list = new ArrayList(); if (args.length == 2) { if (last.length() == 0) { for (SubServer server : plugin.api.getSubServers().values()) list.add(server.getName()); } else { for (SubServer server : plugin.api.getSubServers().values()) { - if (server.getName().toLowerCase().startsWith(last)) - list.add(last + server.getName().substring(last.length())); + if (server.getName().toLowerCase().startsWith(last)) list.add(last + server.getName().substring(last.length())); + } + } + return new NamedContainer<>((list.size() <= 0)?plugin.api.getLang("SubServers", "Command.Generic.Unknown-SubServer").replace("$str$", args[0]):null, list); + } else { + return new NamedContainer<>(null, Collections.emptyList()); + } + } else if (args[0].equals("stop") || + args[0].equals("kill") || args[0].equals("terminate")) { + List list = new ArrayList(); + if (args.length == 2) { + if (last.length() == 0) { + list.add("*"); + for (SubServer server : plugin.api.getSubServers().values()) list.add(server.getName()); + } else { + if ("*".startsWith(last)) list.add("*"); + for (SubServer server : plugin.api.getSubServers().values()) { + if (server.getName().toLowerCase().startsWith(last)) list.add(last + server.getName().substring(last.length())); } } return new NamedContainer<>((list.size() <= 0)?plugin.api.getLang("SubServers", "Command.Generic.Unknown-SubServer").replace("$str$", args[0]):null, list); @@ -705,8 +781,10 @@ public final class SubCommand extends CommandX { if (args.length == 2) { List list = new ArrayList(); if (last.length() == 0) { + list.add("*"); for (SubServer server : plugin.api.getSubServers().values()) list.add(server.getName()); } else { + if ("*".startsWith(last)) list.add("*"); for (SubServer server : plugin.api.getSubServers().values()) { if (server.getName().toLowerCase().startsWith(last)) list.add(last + server.getName().substring(last.length())); } diff --git a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubPlugin.java b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubPlugin.java index ba7aa260..7ad91dbb 100644 --- a/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubPlugin.java +++ b/SubServers.Bungee/src/net/ME1312/SubServers/Bungee/SubPlugin.java @@ -99,7 +99,7 @@ public final class SubPlugin extends BungeeCord implements Listener { if (!(new UniversalFile(dir, "lang.yml").exists())) { Util.copyFromJar(SubPlugin.class.getClassLoader(), "net/ME1312/SubServers/Bungee/Library/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.13.1a+"))) != 0) { + } else if ((new Version((new YAMLConfig(new UniversalFile(dir, "lang.yml"))).get().getString("Version", "0")).compareTo(new Version("2.13.2c+"))) != 0) { Files.move(new UniversalFile(dir, "lang.yml").toPath(), new UniversalFile(dir, "lang.old" + Math.round(Math.random() * 100000) + ".yml").toPath()); Util.copyFromJar(SubPlugin.class.getClassLoader(), "net/ME1312/SubServers/Bungee/Library/Files/lang.yml", new UniversalFile(dir, "lang.yml").getPath()); System.out.println("SubServers > Updated ~/SubServers/lang.yml"); diff --git a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubCommand.java b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubCommand.java index bb1d2f6b..f79645b5 100644 --- a/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubCommand.java +++ b/SubServers.Client/Bukkit/src/net/ME1312/SubServers/Client/Bukkit/SubCommand.java @@ -1,7 +1,9 @@ package net.ME1312.SubServers.Client.Bukkit; import net.ME1312.SubServers.Client.Bukkit.Graphic.UIRenderer; +import net.ME1312.SubServers.Client.Bukkit.Library.Callback; import net.ME1312.SubServers.Client.Bukkit.Library.Config.YAMLSection; +import net.ME1312.SubServers.Client.Bukkit.Library.Container; import net.ME1312.SubServers.Client.Bukkit.Library.Util; import net.ME1312.SubServers.Client.Bukkit.Library.Version.Version; import net.ME1312.SubServers.Client.Bukkit.Network.API.*; @@ -353,16 +355,87 @@ public final class SubCommand implements CommandExecutor { case 9: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Start.Server-Incompatible").replace("$str$", data.getString("m").split(":\\s")[1])); break; + default: + Bukkit.getLogger().warning("SubData > PacketStartServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + args[1] + ") responded with: " + data.getString("m")); case 0: case 1: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Start")); break; + } + })); + } else { + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Generic.Invalid-Permission").replace("$str$", "subservers.subserver.start." + args[1].toLowerCase())); + } + } else { + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Generic.Usage").replace("$str$", label.toLowerCase() + " " + args[0].toLowerCase() + " ")); + } + } else if (args[0].equalsIgnoreCase("restart")) { + if (args.length > 1) { + if ((sender.hasPermission("subservers.subserver.stop.*") || sender.hasPermission("subservers.subserver.stop." + args[1].toLowerCase())) && (sender.hasPermission("subservers.subserver.start.*") || sender.hasPermission("subservers.subserver.start." + args[1].toLowerCase()))) { + Runnable starter = () -> plugin.subdata.sendPacket(new PacketStartServer(null, args[1], data -> { + switch (data.getInt("r")) { + case 3: + case 4: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart.Disappeared")); + break; + case 5: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart.Host-Unavailable")); + break; + case 6: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart.Host-Disabled")); + break; + case 7: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart.Server-Disabled")); + break; + case 9: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart.Server-Incompatible").replace("$str$", data.getString("m").split(":\\s")[1])); + break; default: Bukkit.getLogger().warning("SubData > PacketStartServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + args[1] + ") responded with: " + data.getString("m")); - sender.sendMessage(plugin.api.getLang("SubServers", "Command.Start")); + case 8: + case 0: + case 1: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart.Finish")); break; } })); + + final Container listening = new Container(true); + PacketInRunEvent.callback("SubStoppedEvent", new Callback() { + @Override + public void run(YAMLSection json) { + try { + if (listening.get()) if (!json.getString("server").equalsIgnoreCase(args[1])) { + PacketInRunEvent.callback("SubStoppedEvent", this); + } else { + Bukkit.getScheduler().runTaskLater(plugin, starter, 5); + } + } catch (Exception e) {} + } + }); + + plugin.subdata.sendPacket(new PacketStopServer((sender instanceof Player)?((Player) sender).getUniqueId():null, args[1], false, data -> { + if (data.getInt("r") != 0) listening.set(false); + switch (data.getInt("r")) { + case 3: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart.Unknown")); + break; + case 4: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart.Invalid")); + break; + case 5: + starter.run(); + break; + default: + Bukkit.getLogger().warning("SubData > PacketStopServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + args[1] + ", false) responded with: " + data.getString("m")); + case 0: + case 1: + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Restart")); + break; + } + })); + } else if (!(sender.hasPermission("subservers.subserver.stop.*") || sender.hasPermission("subservers.subserver.stop." + args[1].toLowerCase()))) { + sender.sendMessage(plugin.api.getLang("SubServers", "Command.Generic.Invalid-Permission").replace("$str$", "subservers.subserver.stop." + args[1].toLowerCase())); } else { sender.sendMessage(plugin.api.getLang("SubServers", "Command.Generic.Invalid-Permission").replace("$str$", "subservers.subserver.start." + args[1].toLowerCase())); } @@ -383,12 +456,10 @@ public final class SubCommand implements CommandExecutor { case 5: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Stop.Not-Running")); break; - case 0: - case 1: - sender.sendMessage(plugin.api.getLang("SubServers", "Command.Stop")); - break; default: Bukkit.getLogger().warning("SubData > PacketStopServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + args[1] + ", false) responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Stop")); break; } @@ -413,12 +484,10 @@ public final class SubCommand implements CommandExecutor { case 5: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Terminate.Not-Running")); break; - case 0: - case 1: - sender.sendMessage(plugin.api.getLang("SubServers", "Command.Terminate")); - break; default: Bukkit.getLogger().warning("SubData > PacketStopServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + args[1] + ", true) responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Terminate")); break; } @@ -452,12 +521,10 @@ public final class SubCommand implements CommandExecutor { case 5: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Command.Not-Running")); break; - case 0: - case 1: - sender.sendMessage(plugin.api.getLang("SubServers", "Command.Command")); - break; default: Bukkit.getLogger().warning("SubData > PacketCommandServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + args[1] + ", /" + cmd + ") responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Command")); break; } @@ -501,12 +568,10 @@ public final class SubCommand implements CommandExecutor { case 11: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Creator.Invalid-Port")); break; - case 0: - case 1: - sender.sendMessage(plugin.api.getLang("SubServers", "Command.Creator")); - break; default: Bukkit.getLogger().warning("SubData > PacketCreateServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + args[1] + ", " + args[2] + ", " + args[3] + ", " + args[4] + ", " + ((args.length > 5)?args[5]:"null") + ") responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(plugin.api.getLang("SubServers", "Command.Creator")); break; } @@ -594,6 +659,7 @@ public final class SubCommand implements CommandExecutor { plugin.api.getLang("SubServers", "Command.Help.Version").replace("$str$", label.toLowerCase() + " version"), plugin.api.getLang("SubServers", "Command.Help.Info").replace("$str$", label.toLowerCase() + " info [proxy|host|group|server] "), plugin.api.getLang("SubServers", "Command.Help.SubServer.Start").replace("$str$", label.toLowerCase() + " start "), + plugin.api.getLang("SubServers", "Command.Help.SubServer.Restart").replace("$str$", label.toLowerCase() + " restart "), plugin.api.getLang("SubServers", "Command.Help.SubServer.Stop").replace("$str$", label.toLowerCase() + " stop "), plugin.api.getLang("SubServers", "Command.Help.SubServer.Terminate").replace("$str$", label.toLowerCase() + " kill "), plugin.api.getLang("SubServers", "Command.Help.SubServer.Command").replace("$str$", label.toLowerCase() + " cmd [Args...]"), diff --git a/SubServers.Client/Sponge/src/net/ME1312/SubServers/Client/Sponge/SubCommand.java b/SubServers.Client/Sponge/src/net/ME1312/SubServers/Client/Sponge/SubCommand.java index a06dc50c..8140e126 100644 --- a/SubServers.Client/Sponge/src/net/ME1312/SubServers/Client/Sponge/SubCommand.java +++ b/SubServers.Client/Sponge/src/net/ME1312/SubServers/Client/Sponge/SubCommand.java @@ -2,8 +2,10 @@ package net.ME1312.SubServers.Client.Sponge; import com.google.gson.Gson; import net.ME1312.SubServers.Client.Sponge.Graphic.UIRenderer; +import net.ME1312.SubServers.Client.Sponge.Library.Callback; import net.ME1312.SubServers.Client.Sponge.Library.ChatColor; import net.ME1312.SubServers.Client.Sponge.Library.Config.YAMLSection; +import net.ME1312.SubServers.Client.Sponge.Library.Container; import net.ME1312.SubServers.Client.Sponge.Library.Util; import net.ME1312.SubServers.Client.Sponge.Library.Version.Version; import net.ME1312.SubServers.Client.Sponge.Network.API.*; @@ -29,6 +31,7 @@ import java.net.URL; import java.nio.charset.Charset; import java.text.DecimalFormat; import java.util.*; +import java.util.concurrent.TimeUnit; public final class SubCommand implements CommandExecutor { private SubPlugin plugin; @@ -73,6 +76,11 @@ public final class SubCommand implements CommandExecutor { .executor(new START()) .arguments(GenericArguments.optional(GenericArguments.string(Text.of("SubServer"))), GenericArguments.optional(GenericArguments.remainingJoinedStrings(Text.of("extra")))) .build(), "start") + .child(CommandSpec.builder() + .description(Text.of("The SubServers Command - Restart")) + .executor(new RESTART()) + .arguments(GenericArguments.optional(GenericArguments.string(Text.of("SubServer"))), GenericArguments.optional(GenericArguments.remainingJoinedStrings(Text.of("extra")))) + .build(), "restart") .child(CommandSpec.builder() .description(Text.of("The SubServers Command - Stop")) .executor(new STOP()) @@ -657,12 +665,10 @@ public final class SubCommand implements CommandExecutor { case 9: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Start.Server-Incompatible").replace("$str$", data.getString("m").split(":\\s")[1]))); break; - case 0: - case 1: - sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Start"))); - break; default: plugin.logger.warn("PacketStartServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + subserver.get() + ") responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Start"))); break; } @@ -683,6 +689,93 @@ public final class SubCommand implements CommandExecutor { } } + public final class RESTART implements CommandExecutor { + public CommandResult execute(CommandSource sender, CommandContext args) throws CommandException { + if (canRun(sender)) { + Optional subserver = args.getOne(Text.of("SubServer")); + if (subserver.isPresent()) { + if (sender.hasPermission("subservers.subserver.stop." + subserver.get().toLowerCase()) && sender.hasPermission("subservers.subserver.start." + subserver.get().toLowerCase())) { + Runnable starter = () -> plugin.subdata.sendPacket(new PacketStartServer(null, subserver.get(), data -> { + switch (data.getInt("r")) { + case 3: + case 4: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Restart.Disappeared"))); + break; + case 5: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Restart.Host-Unavailable"))); + break; + case 6: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Restart.Host-Disabled"))); + break; + case 7: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Restart.Server-Disabled"))); + break; + case 9: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Start.Server-Incompatible").replace("$str$", data.getString("m").split(":\\s")[1]))); + break; + default: + plugin.logger.warn("PacketStartServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + subserver.get() + ") responded with: " + data.getString("m")); + case 8: + case 0: + case 1: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Restart.Finish"))); + break; + } + })); + + final Container listening = new Container(true); + PacketInRunEvent.callback("SubStoppedEvent", new Callback() { + @Override + public void run(YAMLSection json) { + try { + if (listening.get()) if (!json.getString("server").equalsIgnoreCase(subserver.get())) { + PacketInRunEvent.callback("SubStoppedEvent", this); + } else { + plugin.game.getScheduler().createTaskBuilder().execute(starter).delay(100, TimeUnit.MILLISECONDS).submit(plugin); + } + } catch (Exception e) {} + } + }); + + plugin.subdata.sendPacket(new PacketStopServer((sender instanceof Player) ? ((Player) sender).getUniqueId():null, subserver.get(), false, data -> { + if (data.getInt("r") != 0) listening.set(false); + switch (data.getInt("r")) { + case 3: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Restart.Unknown"))); + break; + case 4: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Restart.Invalid"))); + break; + case 5: + starter.run(); + break; + default: + plugin.logger.warn("PacketStopServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + subserver.get() + ", false) responded with: " + data.getString("m")); + case 0: + case 1: + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Restart"))); + break; + } + })); + return CommandResult.builder().successCount(1).build(); + } else if (!sender.hasPermission("subservers.subserver.stop." + subserver.get().toLowerCase())) { + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Generic.Invalid-Permission").replace("$str$", "subservers.subserver.stop." + subserver.get().toLowerCase()))); + return CommandResult.builder().successCount(0).build(); + } else { + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Generic.Invalid-Permission").replace("$str$", "subservers.subserver.start." + subserver.get().toLowerCase()))); + return CommandResult.builder().successCount(0).build(); + } + } else { + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Generic.Usage").replace("$str$", "/sub stop "))); + return CommandResult.builder().successCount(0).build(); + } + } else { + sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Generic.Invalid-Permission").replace("$str$", "subservers.command"))); + return CommandResult.builder().successCount(0).build(); + } + } + } + public final class STOP implements CommandExecutor { public CommandResult execute(CommandSource sender, CommandContext args) throws CommandException { if (canRun(sender)) { @@ -700,12 +793,10 @@ public final class SubCommand implements CommandExecutor { case 5: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Stop.Not-Running"))); break; - case 0: - case 1: - sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Stop"))); - break; default: plugin.logger.warn("PacketStopServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + subserver.get() + ", false) responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Stop"))); break; } @@ -743,12 +834,10 @@ public final class SubCommand implements CommandExecutor { case 5: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Terminate.Not-Running"))); break; - case 0: - case 1: - sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Terminate"))); - break; default: plugin.logger.warn("PacketStopServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + subserver.get() + ", true) responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Terminate"))); break; } @@ -787,12 +876,10 @@ public final class SubCommand implements CommandExecutor { case 5: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Command.Not-Running"))); break; - case 0: - case 1: - sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Command"))); - break; default: plugin.logger.warn("PacketCommandServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + subserver.get() + ", /" + command.get() + ") responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Command"))); break; } @@ -854,12 +941,10 @@ public final class SubCommand implements CommandExecutor { case 11: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Creator.Invalid-Port"))); break; - case 0: - case 1: - sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Creator"))); - break; default: plugin.logger.warn("PacketCreateServer(" + ((sender instanceof Player)?((Player) sender).getUniqueId().toString():"null") + ", " + name.get() + ", " + host.get() + ", " + template.get() + ", " + version.get() + ", " + (port.orElse("null")) + ") responded with: " + data.getString("m")); + case 0: + case 1: sender.sendMessage(ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Creator"))); break; } @@ -952,6 +1037,7 @@ public final class SubCommand implements CommandExecutor { ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Help.Version").replace("$str$", "/sub version")), ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Help.Info").replace("$str$", "/sub info [proxy|host|group|server] ")), ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Help.SubServer.Start").replace("$str$", "/sub start ")), + ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Help.SubServer.Restart").replace("$str$", "/sub restart ")), ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Help.SubServer.Stop").replace("$str$", "/sub stop ")), ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Help.SubServer.Terminate").replace("$str$", "/sub kill ")), ChatColor.convertColor(plugin.api.getLang("SubServers","Command.Help.SubServer.Command").replace("$str$", "/sub cmd [Args...]")), diff --git a/SubServers.Console/pom.xml b/SubServers.Console/pom.xml index fb57342c..a8e18bb5 100644 --- a/SubServers.Console/pom.xml +++ b/SubServers.Console/pom.xml @@ -61,7 +61,7 @@ maven-compiler-plugin 3.7.0 - 1.7 + 1.7 1.8 diff --git a/SubServers.Console/src/net/ME1312/SubServers/Console/ConsoleCommand.java b/SubServers.Console/src/net/ME1312/SubServers/Console/ConsoleCommand.java index 62142307..7baa6087 100644 --- a/SubServers.Console/src/net/ME1312/SubServers/Console/ConsoleCommand.java +++ b/SubServers.Console/src/net/ME1312/SubServers/Console/ConsoleCommand.java @@ -51,9 +51,9 @@ public final class ConsoleCommand { } } else { if (type == null) { - sender.sendMessage("SubServers > There is no object with that name"); + sender.sendMessage("SubConsole > There is no object with that name"); } else { - sender.sendMessage("SubServers > There is no subserver with that name"); + sender.sendMessage("SubConsole > There is no subserver with that name"); } } } diff --git a/SubServers.Console/src/net/ME1312/SubServers/Console/ConsolePlugin.java b/SubServers.Console/src/net/ME1312/SubServers/Console/ConsolePlugin.java index 299f4ffb..7113c4b2 100644 --- a/SubServers.Console/src/net/ME1312/SubServers/Console/ConsolePlugin.java +++ b/SubServers.Console/src/net/ME1312/SubServers/Console/ConsolePlugin.java @@ -9,6 +9,7 @@ import net.ME1312.SubServers.Bungee.Host.SubServer; import net.ME1312.SubServers.Bungee.Library.Config.YAMLConfig; import net.ME1312.SubServers.Bungee.SubAPI; import net.ME1312.SubServers.Bungee.SubPlugin; +import net.ME1312.SubServers.Console.Library.Metrics; import net.md_5.bungee.api.plugin.Listener; import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.event.EventHandler; @@ -24,6 +25,7 @@ public final class ConsolePlugin extends Plugin implements Listener { public HashMap cCurrent = new HashMap(); public HashMap sCurrent = new HashMap(); public YAMLConfig config; + public boolean init = false; @Override public void onEnable() { @@ -54,16 +56,21 @@ public final class ConsolePlugin extends Plugin implements Listener { } if (save) config.save(); - getProxy().getPluginManager().registerListener(this, this); - getProxy().getPluginManager().registerCommand(this, new ConsoleCommand.POPOUT(this, "popout")); - getProxy().getPluginManager().registerCommand(this, new ConsoleCommand.AUTO_POPOUT(this, "apopout")); - getProxy().getPluginManager().registerCommand(this, new ConsoleCommand.AUTO_POPOUT(this, "autopopout")); + if (!init) { + init = true; - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - new JFrame("SubServers 2"); - } catch (ClassNotFoundException | InstantiationException | UnsupportedLookAndFeelException | IllegalAccessException e) { - e.printStackTrace(); + getProxy().getPluginManager().registerListener(this, this); + getProxy().getPluginManager().registerCommand(this, new ConsoleCommand.POPOUT(this, "popout")); + getProxy().getPluginManager().registerCommand(this, new ConsoleCommand.AUTO_POPOUT(this, "autopopout")); + + new Metrics(this); + + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + new JFrame("SubServers 2"); + } catch (ClassNotFoundException | InstantiationException | UnsupportedLookAndFeelException | IllegalAccessException e) { + e.printStackTrace(); + } } } catch (IOException e) { e.printStackTrace(); diff --git a/SubServers.Console/src/net/ME1312/SubServers/Console/ConsoleWindow.java b/SubServers.Console/src/net/ME1312/SubServers/Console/ConsoleWindow.java index c653acda..c3372448 100644 --- a/SubServers.Console/src/net/ME1312/SubServers/Console/ConsoleWindow.java +++ b/SubServers.Console/src/net/ME1312/SubServers/Console/ConsoleWindow.java @@ -5,6 +5,7 @@ import net.ME1312.SubServers.Bungee.Host.SubLogFilter; import net.ME1312.SubServers.Bungee.Host.SubLogger; import net.ME1312.SubServers.Bungee.Host.SubServer; import net.ME1312.SubServers.Bungee.Library.Util; +import net.ME1312.SubServers.Console.Library.HTMLogger; import javax.imageio.ImageIO; import javax.swing.*; @@ -24,7 +25,7 @@ import java.util.List; import java.util.logging.Level; public final class ConsoleWindow implements SubLogFilter { - private static final int MAX_SCROLLBACK = (Integer.getInteger("subservers.console.max_scrollback", 0) >= 128)?Integer.getInteger("subservers.console.max_scrollback"):15000; + private static final int MAX_SCROLLBACK = (Integer.getInteger("subservers.console.max_scrollback", 0) >= 128)?Integer.getInteger("subservers.console.max_scrollback"):7500; private static final String RESET_VALUE = "\n\u00A0\n\u00A0"; private ConsolePlugin plugin; private JFrame window; @@ -67,7 +68,7 @@ public final class ConsoleWindow implements SubLogFilter { while (log.getSelectionStart() == log.getSelectionEnd() && (lines = countLines(content = log.getDocument().getText(0, log.getDocument().getLength()))) > MAX_SCROLLBACK) { int lineBreak = 1; for (lines -= MAX_SCROLLBACK; lines > 0; lines--) lineBreak = content.indexOf('\n', lineBreak + 1); - if (lineBreak >= 2) { + if (lineBreak >= 2 && log.getSelectionStart() == log.getSelectionEnd()) { log.getDocument().remove(2, lineBreak); } else break; } diff --git a/SubServers.Console/src/net/ME1312/SubServers/Console/HTMLogger.java b/SubServers.Console/src/net/ME1312/SubServers/Console/Library/HTMLogger.java similarity index 99% rename from SubServers.Console/src/net/ME1312/SubServers/Console/HTMLogger.java rename to SubServers.Console/src/net/ME1312/SubServers/Console/Library/HTMLogger.java index 41bbee61..93314e7d 100644 --- a/SubServers.Console/src/net/ME1312/SubServers/Console/HTMLogger.java +++ b/SubServers.Console/src/net/ME1312/SubServers/Console/Library/HTMLogger.java @@ -1,11 +1,10 @@ -package net.ME1312.SubServers.Console; +package net.ME1312.SubServers.Console.Library; import net.ME1312.SubServers.Bungee.Library.Container; import org.fusesource.jansi.AnsiOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.net.URLDecoder; import java.util.LinkedList; /** diff --git a/SubServers.Console/src/net/ME1312/SubServers/Console/Library/Metrics.java b/SubServers.Console/src/net/ME1312/SubServers/Console/Library/Metrics.java new file mode 100644 index 00000000..1a8299a7 --- /dev/null +++ b/SubServers.Console/src/net/ME1312/SubServers/Console/Library/Metrics.java @@ -0,0 +1,490 @@ +package net.ME1312.SubServers.Console.Library; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import net.ME1312.SubServers.Console.ConsolePlugin; +import net.ME1312.SubServers.Console.ConsoleWindow; +import net.md_5.bungee.config.Configuration; +import net.md_5.bungee.config.ConfigurationProvider; +import net.md_5.bungee.config.YamlConfiguration; + +import javax.net.ssl.HttpsURLConnection; +import java.io.*; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.zip.GZIPOutputStream; + +/** + * bStats collects some data for plugin authors. + * + * Check out https://bStats.org/ to learn more about bStats! + */ +public class Metrics { + + static { + // You can use the property to disable the check in your test environment + if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) { + // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D + final String defaultPackage = new String( + new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'n', 'g', 'e', 'e', 'c', 'o', 'r', 'd'}); + final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); + // We want to make sure nobody just copy & pastes the example and use the wrong package names + if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) { + throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); + } + } + } + + // The version of this bStats class + public static final int B_STATS_VERSION = 1; + + // The url to which the data is sent + private static final String URL = "https://bStats.org/submitData/bungeecord"; + + // The plugin + private final ConsolePlugin plugin; + + // Is bStats enabled on this server? + private boolean enabled; + + // The uuid of the server + private String serverUUID; + + // Should failed requests be logged? + private boolean logFailedRequests = false; + + // A list with all known metrics class objects including this one + private static final List knownMetricsInstances = new ArrayList<>(); + + public Metrics(ConsolePlugin plugin) { + this.plugin = plugin; + + try { + loadConfig(); + } catch (IOException e) { + // Failed to load configuration + plugin.getLogger().log(Level.WARNING, "Failed to load bStats config!", e); + return; + } + + // We are not allowed to send data about this server :( + if (!enabled) { + return; + } + + Class usedMetricsClass = getFirstBStatsClass(); + if (usedMetricsClass == null) { + // Failed to get first metrics class + return; + } + if (usedMetricsClass == getClass()) { + // We are the first! :) + linkMetrics(this); + startSubmitting(); + } else { + // We aren't the first so we link to the first metrics class + try { + usedMetricsClass.getMethod("linkMetrics", Object.class).invoke(null,this); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Failed to link to first metrics class " + usedMetricsClass.getName() + "!", e); + } + } + } + } + + /** + * Links an other metrics class with this class. + * This method is called using Reflection. + * + * @param metrics An object of the metrics class to link. + */ + public static void linkMetrics(Object metrics) { + knownMetricsInstances.add(metrics); + } + + /** + * Gets the plugin specific data. + * This method is called using Reflection. + * + * @return The plugin specific data. + */ + public JsonObject getPluginData() { + JsonObject data = new JsonObject(); + + String pluginName = "SubServers Console"; + String pluginVersion = plugin.getDescription().getVersion(); + + data.addProperty("pluginName", pluginName); + data.addProperty("pluginVersion", pluginVersion); + + JsonArray customCharts = new JsonArray(); + customCharts.add(new SingleLineChart("managed_hosts", new Callable() { + @Override + public Integer call() throws Exception { + return plugin.getProxy().api.getHosts().size(); + } + }).getRequestJsonObject(plugin.getLogger(), logFailedRequests)); + customCharts.add(new SingleLineChart("open_windows", new Callable() { + @Override + public Integer call() throws Exception { + int i = 0; + for (ConsoleWindow window : plugin.cCurrent.values()) if (window.isOpen()) i++; + for (ConsoleWindow window : plugin.sCurrent.values()) if (window.isOpen()) i++; + return i; + } + }).getRequestJsonObject(plugin.getLogger(), logFailedRequests)); + customCharts.add(new SimplePie("subservers_version", new Callable() { + @Override + public String call() throws Exception { + return plugin.getProxy().version.toString(); + } + }).getRequestJsonObject(plugin.getLogger(), logFailedRequests)); + data.add("customCharts", customCharts); + + return data; + } + + private void startSubmitting() { + new Timer("SubServers.Console::Metrics_Uploader").schedule( new TimerTask() { + @Override + public void run() { + // The data collection is async, as well as sending the data + // Bungeecord does not have a main thread, everything is async + submitData(); + } + }, TimeUnit.MINUTES.toMillis(2), TimeUnit.MINUTES.toMillis(30)); + // Submit the data every 30 minutes, first time after 2 minutes to give other plugins enough time to start + // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted! + // WARNING: Just don't do it! + } + + /** + * Gets the server specific data. + * + * @return The server specific data. + */ + private JsonObject getServerData() { + // Minecraft specific data + int playerAmount = plugin.getProxy().getOnlineCount(); + playerAmount = playerAmount > 500 ? 500 : playerAmount; + int onlineMode = plugin.getProxy().getConfig().isOnlineMode() ? 1 : 0; + String bungeecordVersion = (plugin.getProxy().getName().equals("SubServers Platform"))?"SubServers-Bungee-Patched":plugin.getProxy().getVersion(); + int managedServers = plugin.getProxy().getServers().size(); + + // OS/Java specific data + String javaVersion = System.getProperty("java.version"); + String osName = System.getProperty("os.name"); + String osArch = System.getProperty("os.arch"); + String osVersion = System.getProperty("os.version"); + int coreCount = Runtime.getRuntime().availableProcessors(); + + JsonObject data = new JsonObject(); + + data.addProperty("serverUUID", serverUUID); + + data.addProperty("playerAmount", playerAmount); + data.addProperty("managedServers", managedServers); + data.addProperty("onlineMode", onlineMode); + data.addProperty("bungeecordVersion", bungeecordVersion); + + data.addProperty("javaVersion", javaVersion); + data.addProperty("osName", osName); + data.addProperty("osArch", osArch); + data.addProperty("osVersion", osVersion); + data.addProperty("coreCount", coreCount); + + return data; + } + + /** + * Collects the data and sends it afterwards. + */ + private void submitData() { + final JsonObject data = getServerData(); + + final JsonArray pluginData = new JsonArray(); + // Search for all other bStats Metrics classes to get their plugin data + for (Object metrics : knownMetricsInstances) { + try { + Object plugin = metrics.getClass().getMethod("getPluginData").invoke(metrics); + if (plugin instanceof JsonObject) { + pluginData.add((JsonObject) plugin); + } + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { } + } + + data.add("plugins", pluginData); + + try { + // Send the data + sendData(data); + } catch (Exception e) { + // Something went wrong! :( + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats!", e); + } + } + } + + /** + * Loads the bStats configuration. + * + * @throws IOException If something did not work :( + */ + private void loadConfig() throws IOException { + Path configPath = new File(plugin.getProxy().dir, "plugins").toPath().resolve("bStats"); + configPath.toFile().mkdirs(); + File configFile = new File(configPath.toFile(), "config.yml"); + if (!configFile.exists()) { + writeFile(configFile, + "#bStats collects some data for plugin authors like how many servers are using their plugins.", + "#To honor their work, you should not disable it.", + "#This has nearly no effect on the server performance!", + "#Check out https://bStats.org/ to learn more :)", + "enabled: true", + "serverUuid: \"" + UUID.randomUUID().toString() + "\"", + "logFailedRequests: false"); + } + + Configuration configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile); + + // Load configuration + enabled = configuration.getBoolean("enabled", true); + serverUUID = configuration.getString("serverUuid"); + logFailedRequests = configuration.getBoolean("logFailedRequests", false); + } + + /** + * Gets the first bStat Metrics class. + * + * @return The first bStats metrics class. + */ + private Class getFirstBStatsClass() { + Path configPath = new File(plugin.getProxy().dir, "plugins").toPath().resolve("bStats"); + configPath.toFile().mkdirs(); + File tempFile = new File(configPath.toFile(), "temp.txt"); + + try { + String className = readFile(tempFile); + if (className != null) { + try { + // Let's check if a class with the given name exists. + return Class.forName(className); + } catch (ClassNotFoundException ignored) { } + } + writeFile(tempFile, getClass().getName()); + return getClass(); + } catch (IOException e) { + if (logFailedRequests) { + plugin.getLogger().log(Level.WARNING, "Failed to get first bStats class!", e); + } + return null; + } + } + + /** + * Reads the first line of the file. + * + * @param file The file to read. Cannot be null. + * @return The first line of the file or null if the file does not exist or is empty. + * @throws IOException If something did not work :( + */ + private String readFile(File file) throws IOException { + if (!file.exists()) { + return null; + } + try ( + FileReader fileReader = new FileReader(file); + BufferedReader bufferedReader = new BufferedReader(fileReader); + ) { + return bufferedReader.readLine(); + } + } + + /** + * Writes a String to a file. It also adds a note for the user, + * + * @param file The file to write to. Cannot be null. + * @param lines The lines to write. + * @throws IOException If something did not work :( + */ + private void writeFile(File file, String... lines) throws IOException { + if (!file.exists()) { + file.createNewFile(); + } + try ( + FileWriter fileWriter = new FileWriter(file); + BufferedWriter bufferedWriter = new BufferedWriter(fileWriter) + ) { + for (String line : lines) { + bufferedWriter.write(line); + bufferedWriter.newLine(); + } + } + } + + /** + * Sends the data to the bStats server. + * + * @param data The data to send. + * @throws Exception If the request failed. + */ + private static void sendData(JsonObject data) throws Exception { + if (data == null) { + throw new IllegalArgumentException("Data cannot be null"); + } + + HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection(); + + // Compress the data to save bandwidth + byte[] compressedData = compress(data.toString()); + + // Add headers + connection.setRequestMethod("POST"); + connection.addRequestProperty("Accept", "application/json"); + connection.addRequestProperty("Connection", "close"); + connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request + connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); + connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format + connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION); + + // Send data + connection.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream()); + outputStream.write(compressedData); + outputStream.flush(); + outputStream.close(); + + connection.getInputStream().close(); // We don't care about the response - Just send our data :) + } + + /** + * Gzips the given String. + * + * @param str The string to gzip. + * @return The gzipped String. + * @throws IOException If the compression failed. + */ + private static byte[] compress(final String str) throws IOException { + if (str == null) { + return null; + } + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + GZIPOutputStream gzip = new GZIPOutputStream(outputStream); + gzip.write(str.getBytes("UTF-8")); + gzip.close(); + return outputStream.toByteArray(); + } + /** + * Represents a custom chart. + */ + private static abstract class CustomChart { + + // The id of the chart + private final String chartId; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + */ + CustomChart(String chartId) { + if (chartId == null || chartId.isEmpty()) { + throw new IllegalArgumentException("ChartId cannot be null or empty!"); + } + this.chartId = chartId; + } + + JsonObject getRequestJsonObject(Logger logger, boolean logFailedRequests) { + JsonObject chart = new JsonObject(); + chart.addProperty("chartId", chartId); + try { + JsonObject data = getChartData(); + if (data == null) { + // If the data is null we don't send the chart. + return null; + } + chart.add("data", data); + } catch (Throwable t) { + if (logFailedRequests) { + logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t); + } + return null; + } + return chart; + } + + protected abstract JsonObject getChartData() throws Exception; + + } + + /** + * Represents a custom single line chart. + */ + private static class SingleLineChart extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SingleLineChart(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + int value = callable.call(); + if (value == 0) { + // Null = skip the chart + return null; + } + data.addProperty("value", value); + return data; + } + + } + + /** + * Represents a custom simple pie. + */ + private static class SimplePie extends CustomChart { + + private final Callable callable; + + /** + * Class constructor. + * + * @param chartId The id of the chart. + * @param callable The callable which is used to request the chart data. + */ + public SimplePie(String chartId, Callable callable) { + super(chartId); + this.callable = callable; + } + + @Override + protected JsonObject getChartData() throws Exception { + JsonObject data = new JsonObject(); + String value = callable.call(); + if (value == null || value.isEmpty()) { + // Null = skip the chart + return null; + } + data.addProperty("value", value); + return data; + } + } +} \ No newline at end of file diff --git a/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java b/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java index 6efd9454..c054d7de 100644 --- a/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java +++ b/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java @@ -1,6 +1,9 @@ package net.ME1312.SubServers.Host; import net.ME1312.Galaxi.Engine.GalaxiEngine; +import net.ME1312.Galaxi.Library.Callback; +import net.ME1312.Galaxi.Library.Config.YAMLSection; +import net.ME1312.Galaxi.Library.Container; import net.ME1312.Galaxi.Library.Util; import net.ME1312.Galaxi.Library.Version.Version; import net.ME1312.Galaxi.Plugin.Command.Command; @@ -456,12 +459,10 @@ public class SubCommand { case 9: sender.sendMessage("That SubServer cannot start while these server(s) are running:", data.getRawString("m").split(":\\s")[1]); break; - case 0: - case 1: - sender.sendMessage("Server was started successfully"); - break; default: host.log.warn.println("PacketStartServer(null, " + args[0] + ") responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("Server was started successfully"); break; } @@ -497,6 +498,92 @@ public class SubCommand { "Example:", " /start ExampleServer" ).register("start"); + new Command(host.info) { + @Override + public void command(CommandSender sender, String handle, String[] args) { + if (args.length > 0) { + TimerTask starter = new TimerTask() { + @Override + public void run() { + host.subdata.sendPacket(new PacketStartServer(null, args[0], data -> { + switch (data.getInt("r")) { + case 3: + case 4: + sender.sendMessage("Could not restart server: That SubServer has disappeared"); + break; + case 5: + sender.sendMessage("Could not restart server: That SubServer's Host is no longer available"); + break; + case 6: + sender.sendMessage("Could not restart server: That SubServer's Host is no longer enabled"); + break; + case 7: + sender.sendMessage("Could not restart server: That SubServer is no longer enabled"); + break; + case 9: + sender.sendMessage("Could not restart server: That SubServer cannot start while these server(s) are running:", data.getRawString("m").split(":\\s")[1]); + break; + default: + host.log.warn.println("PacketStartServer(null, " + args[0] + ") responded with: " + data.getRawString("m")); + case 8: + case 0: + case 1: + sender.sendMessage("Server was started successfully"); + break; + } + })); + } + }; + + final Container listening = new Container(true); + PacketInRunEvent.callback("SubStoppedEvent", new Callback() { + @Override + public void run(YAMLSection json) { + try { + if (listening.get()) if (!json.getString("server").equalsIgnoreCase(args[0])) { + PacketInRunEvent.callback("SubStoppedEvent", this); + } else { + new Timer(SubAPI.getInstance().getAppInfo().getName() + "::Server_Restart_Command_Handler(" + args[0] + ')').schedule(starter, 100); + } + } catch (Exception e) {} + } + }); + + host.subdata.sendPacket(new PacketStopServer(null, args[0], false, data -> { + if (data.getInt("r") != 0) listening.set(false); + switch (data.getInt("r")) { + case 3: + sender.sendMessage("There is no server with that name"); + break; + case 4: + sender.sendMessage("That Server is not a SubServer"); + break; + case 5: + starter.run(); + break; + default: + host.log.warn.println("PacketStopServer(null, " + args[0] + ", false) responded with: " + data.getRawString("m")); + case 0: + case 1: + sender.sendMessage("Server was stopped successfully"); + break; + } + })); + } else { + sender.sendMessage("Usage: /" + handle + " "); + } + } + }.autocomplete(defaultCompletor).usage("").description("Restarts a SubServer").help( + "This command is used to request a SubServer to restart via the network.", + "Restarting a SubServer in this way will run the stop command", + "specified in the server's configuration before re-launching the start command.", + "", + "The argument is required, and should be the name of", + "the SubServer you want to restart.", + "", + "Example:", + " /restart ExampleServer" + ).register("restart"); new Command(host.info) { @Override public void command(CommandSender sender, String handle, String[] args) { @@ -552,12 +639,10 @@ public class SubCommand { case 5: sender.sendMessage("That SubServer is not running"); break; - case 0: - case 1: - sender.sendMessage("Server was terminated successfully"); - break; default: host.log.warn.println("PacketStopServer(null, " + args[0] + ", true) responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("Server was terminated successfully"); break; } @@ -601,12 +686,10 @@ public class SubCommand { case 5: sender.sendMessage("That SubServer is not running"); break; - case 0: - case 1: - sender.sendMessage("Command was sent successfully"); - break; default: host.log.warn.println("PacketCommandServer(null, " + args[0] + ", /" + cmd + ") responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("Command was sent successfully"); break; } @@ -663,12 +746,10 @@ public class SubCommand { case 11: sender.sendMessage("Invalid Port Number"); break; - case 0: - case 1: - sender.sendMessage("Launching SubCreator..."); - break; default: host.log.warn.println("PacketCreateServer(null, " + args[0] + ", " + args[1] + ", " + args[2] + ", " + args[3] + ", " + ((args.length > 4)?args[4]:"null") + ") responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("Launching SubCreator..."); break; } diff --git a/SubServers.Sync/src/net/ME1312/SubServers/Sync/SubCommand.java b/SubServers.Sync/src/net/ME1312/SubServers/Sync/SubCommand.java index 59fcd4a2..049266a6 100644 --- a/SubServers.Sync/src/net/ME1312/SubServers/Sync/SubCommand.java +++ b/SubServers.Sync/src/net/ME1312/SubServers/Sync/SubCommand.java @@ -1,8 +1,10 @@ package net.ME1312.SubServers.Sync; import com.google.gson.Gson; +import net.ME1312.SubServers.Sync.Library.Callback; import net.ME1312.SubServers.Sync.Library.Compatibility.CommandX; import net.ME1312.SubServers.Sync.Library.Config.YAMLSection; +import net.ME1312.SubServers.Sync.Library.Container; import net.ME1312.SubServers.Sync.Library.NamedContainer; import net.ME1312.SubServers.Sync.Library.Util; import net.ME1312.SubServers.Sync.Library.Version.Version; @@ -354,20 +356,90 @@ public final class SubCommand extends CommandX { sender.sendMessage("SubServers > That SubServer is already running"); break; case 9: - sender.sendMessages("That SubServer cannot start while these server(s) are running:", data.getRawString("m").split(":\\s")[1]); - break; - case 0: - case 1: - sender.sendMessage("SubServers > Server was started successfully"); + sender.sendMessage("SubServers > That SubServer cannot start while these server(s) are running: " + data.getRawString("m").split(":\\s")[1]); break; default: System.out.println("PacketStartServer(null, " + args[1] + ") responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("SubServers > Server was started successfully"); break; } })); } else { - sender.sendMessage("Usage: " + label + " "); + sender.sendMessage("Usage: " + label + " " + args[0].toLowerCase() + " "); + } + } else if (args[0].equalsIgnoreCase("restart")) { + if (args.length > 1) { + TimerTask starter = new TimerTask() { + @Override + public void run() { + plugin.subdata.sendPacket(new PacketStartServer(null, args[1], data -> { + switch (data.getInt("r")) { + case 3: + case 4: + sender.sendMessage("SubServers > Could not restart server: That SubServer has disappeared"); + break; + case 5: + sender.sendMessage("SubServers > Could not restart server: That SubServer's Host is no longer available"); + break; + case 6: + sender.sendMessage("SubServers > Could not restart server: That SubServer's Host is no longer enabled"); + break; + case 7: + sender.sendMessage("SubServers > Could not restart server: That SubServer is no longer enabled"); + break; + case 9: + sender.sendMessage("SubServers > Could not restart server: That SubServer cannot start while these server(s) are running: " + data.getRawString("m").split(":\\s")[1]); + break; + default: + System.out.println("PacketStartServer(null, " + args[1] + ") responded with: " + data.getRawString("m")); + case 8: + case 0: + case 1: + sender.sendMessage("SubServers > Server was started successfully"); + break; + } + })); + } + }; + + final Container listening = new Container(true); + PacketInRunEvent.callback("SubStoppedEvent", new Callback() { + @Override + public void run(YAMLSection json) { + try { + if (listening.get()) if (!json.getString("server").equalsIgnoreCase(args[1])) { + PacketInRunEvent.callback("SubStoppedEvent", this); + } else { + new Timer("SubServers.Sync::Server_Restart_Command_Handler(" + args[1] + ')').schedule(starter, 100); + } + } catch (Exception e) {} + } + }); + + plugin.subdata.sendPacket(new PacketStopServer(null, args[1], false, data -> { + if (data.getInt("r") != 0) listening.set(false); + switch (data.getInt("r")) { + case 3: + sender.sendMessage("SubServers > There is no server with that name"); + break; + case 4: + sender.sendMessage("SubServers > That Server is not a SubServer"); + break; + case 5: + starter.run(); + break; + default: + System.out.println("PacketStopServer(null, " + args[1] + ", false) responded with: " + data.getRawString("m")); + case 0: + case 1: + sender.sendMessage("SubServers > Server was stopped successfully"); + break; + } + })); + } else { + sender.sendMessage("Usage: " + label + " " + args[0].toLowerCase() + " "); } } else if (args[0].equalsIgnoreCase("stop")) { if (args.length > 1) { @@ -382,18 +454,16 @@ public final class SubCommand extends CommandX { case 5: sender.sendMessage("SubServers > That SubServer is not running"); break; - case 0: - case 1: - sender.sendMessage("SubServers > Server was stopped successfully"); - break; default: System.out.println("PacketStopServer(null, " + args[1] + ", false) responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("SubServers > Server was stopped successfully"); break; } })); } else { - sender.sendMessage("Usage: " + label + " "); + sender.sendMessage("Usage: " + label + " " + args[0].toLowerCase() + " "); } } else if (args[0].equalsIgnoreCase("kill") || args[0].equalsIgnoreCase("terminate")) { if (args.length > 1) { @@ -408,18 +478,16 @@ public final class SubCommand extends CommandX { case 5: sender.sendMessage("SubServers > That SubServer is not running"); break; - case 0: - case 1: - sender.sendMessage("SubServers > Server was terminated successfully"); - break; default: System.out.println("PacketStopServer(null, " + args[1] + ", true) responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("SubServers > Server was terminated successfully"); break; } })); } else { - sender.sendMessage("Usage: " + label + " "); + sender.sendMessage("Usage: " + label + " " + args[0].toLowerCase() + " "); } } else if (args[0].equalsIgnoreCase("cmd") || args[0].equalsIgnoreCase("command")) { if (args.length > 2) { @@ -443,18 +511,16 @@ public final class SubCommand extends CommandX { case 5: sender.sendMessage("SubServers > That SubServer is not running"); break; - case 0: - case 1: - sender.sendMessage("SubServers > Command was sent successfully"); - break; default: System.out.println("PacketCommandServer(null, " + args[1] + ", /" + cmd + ") responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("SubServers > Command was sent successfully"); break; } })); } else { - sender.sendMessage("Usage: " + label + " [Args...]"); + sender.sendMessage("Usage: " + label + " " + args[0].toLowerCase() + " [Args...]"); } } else if (args[0].equalsIgnoreCase("create")) { if (args.length > 4) { @@ -488,19 +554,17 @@ public final class SubCommand extends CommandX { case 11: sender.sendMessage("SubServers > Invalid Port Number"); break; - case 0: - case 1: - sender.sendMessage("SubServers > Launching SubCreator..."); - break; default: System.out.println("PacketCreateServer(null, " + args[1] + ", " + args[2] + ", " + args[3] + ", " + args[4] + ", " + ((args.length > 5)?args[5]:"null") + ") responded with: " + data.getRawString("m")); + case 0: + case 1: sender.sendMessage("SubServers > Launching SubCreator..."); break; } })); } } else { - sender.sendMessage("Usage: " + label + "