diff --git a/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from PULL_REQUEST_TEMPLATE.md rename to .github/PULL_REQUEST_TEMPLATE.md diff --git a/Artifacts/SubServers.Bungee.jar b/Artifacts/SubServers.Bungee.jar index 9447c233..d7bfc659 100644 Binary files a/Artifacts/SubServers.Bungee.jar and b/Artifacts/SubServers.Bungee.jar differ diff --git a/Artifacts/SubServers.Client.Bukkit.jar b/Artifacts/SubServers.Client.Bukkit.jar index b4b06d68..07b107d6 100644 Binary files a/Artifacts/SubServers.Client.Bukkit.jar and b/Artifacts/SubServers.Client.Bukkit.jar differ diff --git a/Artifacts/SubServers.Host.jar b/Artifacts/SubServers.Host.jar index 93695aa2..1cae732c 100644 Binary files a/Artifacts/SubServers.Host.jar and b/Artifacts/SubServers.Host.jar differ diff --git a/Artifacts/SubServers.Sync.jar b/Artifacts/SubServers.Sync.jar index 05f42be0..f030346e 100644 Binary files a/Artifacts/SubServers.Sync.jar and b/Artifacts/SubServers.Sync.jar differ diff --git a/Javadoc/SubServers.Bungee.jar b/Javadoc/SubServers.Bungee.jar index 1b84ab26..5945043f 100644 Binary files a/Javadoc/SubServers.Bungee.jar and b/Javadoc/SubServers.Bungee.jar differ diff --git a/Javadoc/SubServers.Client.Bukkit.jar b/Javadoc/SubServers.Client.Bukkit.jar index 636e1572..369e19c2 100644 Binary files a/Javadoc/SubServers.Client.Bukkit.jar and b/Javadoc/SubServers.Client.Bukkit.jar differ diff --git a/Javadoc/SubServers.Host.jar b/Javadoc/SubServers.Host.jar index 7e24f206..7dac3706 100644 Binary files a/Javadoc/SubServers.Host.jar and b/Javadoc/SubServers.Host.jar differ diff --git a/Javadoc/SubServers.Sync.jar b/Javadoc/SubServers.Sync.jar index 628e1cd4..1211b791 100644 Binary files a/Javadoc/SubServers.Sync.jar and b/Javadoc/SubServers.Sync.jar differ diff --git a/SubServers.Bungee/src.jar b/SubServers.Bungee/src.jar index 7fc8a56f..3184b8fd 100644 Binary files a/SubServers.Bungee/src.jar and b/SubServers.Bungee/src.jar differ diff --git a/SubServers.Bungee/src/META-INF/MANIFEST.MF b/SubServers.Bungee/src/META-INF/MANIFEST.MF index 70acbd7f..57ca8980 100644 --- a/SubServers.Bungee/src/META-INF/MANIFEST.MF +++ b/SubServers.Bungee/src/META-INF/MANIFEST.MF @@ -2,4 +2,4 @@ Manifest-Version: 1.0 Class-Path: BungeeCord.jar Waterfall.jar Main-Class: net.ME1312.SubServers.Bungee.Launch Implementation-Title: SubServers.Bungee -Specification-Title: 18w26d +Specification-Title: 18w26n diff --git a/SubServers.Client/Bukkit/src.jar b/SubServers.Client/Bukkit/src.jar index bd30ca1c..295ac36c 100644 Binary files a/SubServers.Client/Bukkit/src.jar and b/SubServers.Client/Bukkit/src.jar differ diff --git a/SubServers.Client/Bukkit/src/META-INF/MANIFEST.MF b/SubServers.Client/Bukkit/src/META-INF/MANIFEST.MF index 98797fca..fc5fd146 100644 --- a/SubServers.Client/Bukkit/src/META-INF/MANIFEST.MF +++ b/SubServers.Client/Bukkit/src/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ Manifest-Version: 1.0 Implementation-Title: SubServers.Client.Bukkit -Specification-Title: 18w26d +Specification-Title: 18w26n diff --git a/SubServers.Client/Bukkit/src/plugin.yml b/SubServers.Client/Bukkit/src/plugin.yml index 3f265599..e9424d91 100644 --- a/SubServers.Client/Bukkit/src/plugin.yml +++ b/SubServers.Client/Bukkit/src/plugin.yml @@ -7,13 +7,13 @@ website: 'http://www.ME1312.net/' commands: subservers: description: 'The SubServers Command' - usage: /subservers help + usage: 'An exception may have occurred while running this command' subserver: description: 'The SubServers Command' - usage: /subserver help + usage: 'An exception may have occurred while running this command' sub: description: 'The SubServers Command' - usage: /sub help + usage: 'An exception may have occurred while running this command' permissions: subservers.*: description: 'Grants Access to to Everything in SubServers.Client' diff --git a/SubServers.Host/src.jar b/SubServers.Host/src.jar index 9848bde8..838899e7 100644 Binary files a/SubServers.Host/src.jar and b/SubServers.Host/src.jar differ diff --git a/SubServers.Host/src/META-INF/MANIFEST.MF b/SubServers.Host/src/META-INF/MANIFEST.MF index 587ed18b..bb94d968 100644 --- a/SubServers.Host/src/META-INF/MANIFEST.MF +++ b/SubServers.Host/src/META-INF/MANIFEST.MF @@ -1,4 +1,4 @@ Manifest-Version: 1.0 Main-Class: net.ME1312.SubServers.Host.ExHost Implementation-Title: SubServers.Host -Specification-Title: 18w26d +Specification-Title: 18w26n diff --git a/SubServers.Host/src/net/ME1312/SubServers/Host/ExHost.java b/SubServers.Host/src/net/ME1312/SubServers/Host/ExHost.java index 187cda92..35ee364f 100644 --- a/SubServers.Host/src/net/ME1312/SubServers/Host/ExHost.java +++ b/SubServers.Host/src/net/ME1312/SubServers/Host/ExHost.java @@ -35,6 +35,8 @@ import java.util.*; import java.util.concurrent.TimeUnit; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * SubServers.Host Main Class @@ -253,7 +255,7 @@ public final class ExHost { Object obj = clazz.getConstructor().newInstance(); try { - SubPluginInfo plugin = new SubPluginInfo(this, obj, clazz.getAnnotation(SubPlugin.class).name(), new Version(clazz.getAnnotation(SubPlugin.class).version()), + SubPluginInfo plugin = new SubPluginInfo(this, obj, clazz.getAnnotation(SubPlugin.class).name(), Version.fromString(clazz.getAnnotation(SubPlugin.class).version()), Arrays.asList(clazz.getAnnotation(SubPlugin.class).authors()), (clazz.getAnnotation(SubPlugin.class).description().length() > 0)?clazz.getAnnotation(SubPlugin.class).description():null, (clazz.getAnnotation(SubPlugin.class).website().length() > 0)?new URL(clazz.getAnnotation(SubPlugin.class).website()):null, Arrays.asList(clazz.getAnnotation(SubPlugin.class).loadBefore()), Arrays.asList(clazz.getAnnotation(SubPlugin.class).dependencies()), Arrays.asList(clazz.getAnnotation(SubPlugin.class).softDependencies())); @@ -336,7 +338,18 @@ public final class ExHost { api.plugins.put(plugin.getName().toLowerCase(), plugin); api.plugins.put(plugin.getName().toLowerCase(), plugin); loaded.add(plugin.getName().toLowerCase()); - log.info.println("Loaded " + plugin.getName() + " v" + plugin.getVersion().toString() + " by " + plugin.getAuthors().toString().substring(1, plugin.getAuthors().toString().length() - 1)); + String a = ""; + int ai = 0; + for (String author : plugin.getAuthors()) { + ai++; + if (ai > 1) { + if (plugin.getAuthors().size() > 2) a += ", "; + else if (plugin.getAuthors().size() == 2) a += ' '; + if (ai == plugin.getAuthors().size()) a += "and "; + } + a += author; + } + log.info.println("Loaded " + plugin.getName() + " v" + plugin.getVersion().toString() + " by " + a); i++; } catch (Throwable e) { plugin.setEnabled(false); @@ -446,30 +459,112 @@ public final class ExHost { } private void loop() throws Exception { - String umsg; + String line; ready = true; - while (ready && (umsg = jline.readLine(">")) != null) { - if (!ready || umsg.equals("")) continue; + while (ready && (line = jline.readLine(">")) != null) { + if (!ready || line.replaceAll("\\s", "").length() == 0) continue; final CommandPreProcessEvent event; - api.executeEvent(event = new CommandPreProcessEvent(this, umsg)); + api.executeEvent(event = new CommandPreProcessEvent(this, line)); if (!event.isCancelled()) { - final String cmd = (umsg.startsWith("/"))?((umsg.contains(" ")?umsg.split(" "):new String[]{umsg})[0].substring(1)):((umsg.contains(" ")?umsg.split(" "):new String[]{umsg})[0]); - if (api.commands.keySet().contains(cmd.toLowerCase())) { - ArrayList args = new ArrayList(); - args.addAll(Arrays.asList(umsg.contains(" ") ? umsg.split(" ") : new String[]{umsg})); + LinkedList args = new LinkedList(); + Matcher parser = Pattern.compile("(?:^|\\s+)(\"(?:\\\\\"|[^\"])+\"?|(?:\\\\\\s|[^\\s])+)").matcher(line); + while (parser.find()) { + String arg = parser.group(1); + if (arg.startsWith("\"")) arg = arg.substring(1, arg.length() - ((arg.endsWith("\""))?1:0)); + arg = unescapeCommand(arg); + args.add(arg); + } + String cmd = args.get(0); + args.remove(0); + if (cmd.startsWith("/")) cmd = cmd.substring(1); + if (args.size() >= 1 && + ((cmd.equalsIgnoreCase("sub") && !api.commands.keySet().contains("sub")) || + (cmd.equalsIgnoreCase("subserver") && !api.commands.keySet().contains("subserver")) || + (cmd.equalsIgnoreCase("subservers") && !api.commands.keySet().contains("subservers")))) { + cmd = args.get(0); args.remove(0); + } + + if (api.commands.keySet().contains(cmd.toLowerCase())) { try { api.commands.get(cmd.toLowerCase()).command(cmd, args.toArray(new String[args.size()])); } catch (Exception e) { log.error.println(new InvocationTargetException(e, "Uncaught exception while running command")); } } else { - log.message.println("Unknown Command - " + umsg); + String s = cmd.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "\\\"").replace(" ", "\\ "); + for (String arg : args) { + s += ' ' + arg.replace("\\", "\\\\").replace("\n", "\\n").replace("\"", "\\\"").replace(" ", "\\ "); + } + log.message.println("Unknown Command - " + s); } jline.getOutput().write("\b \b"); } } } + /** + * Parse escapes in a command + * + * @param str String + * @return Unescaped String + */ + private String unescapeCommand(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 'n': + ch = '\n'; + 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(); + } private void loadDefaults() { SubCommand.load(this); diff --git a/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java b/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java index be421440..02a9c1b9 100644 --- a/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java +++ b/SubServers.Host/src/net/ME1312/SubServers/Host/SubCommand.java @@ -28,42 +28,66 @@ public class SubCommand { new Command(null) { @Override public void command(String handle, String[] args) { - if (args.length == 0) { + if (args.length == 0 || host.api.plugins.get(args[0].toLowerCase()) != null) { host.log.message.println( - "These are the platforms and versions that are running SubServers.Host:", + "These are the platforms and versions that are running " + ((args.length == 0)?"SubServers.Host":host.api.plugins.get(args[0].toLowerCase()).getName()) +":", " " + System.getProperty("os.name") + ' ' + System.getProperty("os.version") + ',', " Java " + System.getProperty("java.version") + ',', - " SubServers.Host v" + host.version.toExtendedString(), - ""); - new Thread(() -> { - try { - Document updxml = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(Util.readAll(new BufferedReader(new InputStreamReader(new URL("https://src.me1312.net/maven/net/ME1312/SubServers/SubServers.Host/maven-metadata.xml").openStream(), Charset.forName("UTF-8"))))))); + " SubServers.Host v" + host.version.toExtendedString() + ((args.length == 0)?"":",")); + if (args.length == 0) { + host.log.message.println(""); + new Thread(() -> { + try { + Document updxml = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(Util.readAll(new BufferedReader(new InputStreamReader(new URL("https://src.me1312.net/maven/net/ME1312/SubServers/SubServers.Host/maven-metadata.xml").openStream(), Charset.forName("UTF-8"))))))); - NodeList updnodeList = updxml.getElementsByTagName("version"); - Version updversion = host.version; - int updcount = 0; - for (int i = 0; i < updnodeList.getLength(); i++) { - Node node = updnodeList.item(i); - if (node.getNodeType() == Node.ELEMENT_NODE) { - if (!node.getTextContent().startsWith("-") && !node.getTextContent().equals(host.version.toString()) && Version.fromString(node.getTextContent()).compareTo(updversion) > 0) { - updversion = Version.fromString(node.getTextContent()); - updcount++; + NodeList updnodeList = updxml.getElementsByTagName("version"); + Version updversion = host.version; + int updcount = 0; + for (int i = 0; i < updnodeList.getLength(); i++) { + Node node = updnodeList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + if (!node.getTextContent().startsWith("-") && !node.getTextContent().equals(host.version.toString()) && Version.fromString(node.getTextContent()).compareTo(updversion) > 0) { + updversion = Version.fromString(node.getTextContent()); + updcount++; + } } } + if (updcount == 0) { + host.log.message.println("You are on the latest version."); + } else { + host.log.message.println("SubServers.Host v" + updversion + " is available. You are " + updcount + " version" + ((updcount == 1)?"":"s") + " behind."); + } + } catch (Exception e) {} + }).start(); + } else { + SubPluginInfo plugin = host.api.plugins.get(args[0].toLowerCase()); + String title = " " + plugin.getName() + " v" + plugin.getVersion().toExtendedString(); + String subtitle = " by "; + int i = 0; + for (String author : plugin.getAuthors()) { + i++; + if (i > 1) { + if (plugin.getAuthors().size() > 2) subtitle += ", "; + else if (plugin.getAuthors().size() == 2) subtitle += ' '; + if (i == plugin.getAuthors().size()) subtitle += "and "; } - if (updcount == 0) { - host.log.message.println("You are on the latest version."); - } else { - host.log.message.println("SubServers.Host v" + updversion + " is available. You are " + updcount + " version" + ((updcount == 1)?"":"s") + " behind."); - } - } catch (Exception e) { + subtitle += author; } - }).start(); - } else if (host.api.plugins.get(args[0].toLowerCase()) != null) { - SubPluginInfo plugin = host.api.plugins.get(args[0].toLowerCase()); - host.log.message.println(plugin.getName() + " v" + plugin.getVersion() + " by " + plugin.getAuthors().toString().substring(1, plugin.getAuthors().toString().length() - 1)); - if (plugin.getWebsite() != null) host.log.message.println(plugin.getWebsite().toString()); - if (plugin.getDescription() != null) host.log.message.println("", plugin.getDescription()); + if (plugin.getWebsite() != null) { + if (title.length() > subtitle.length() + 5 + plugin.getWebsite().toString().length()) { + i = subtitle.length(); + while (i < title.length() - plugin.getWebsite().toString().length() - 2) { + i++; + subtitle += ' '; + } + } else { + subtitle += " - "; + } + subtitle += plugin.getWebsite().toString(); + } + host.log.message.println(title, subtitle); + if (plugin.getDescription() != null) host.log.message.println("", plugin.getDescription()); + } } else { host.log.message.println("There is no plugin with that name"); } @@ -202,7 +226,7 @@ public class SubCommand { } })); } else { - host.log.message.println("Usage: " + handle + " "); + host.log.message.println("Usage: /" + handle + " "); } } }.usage("").description("Gets information about a SubServer").help( @@ -251,7 +275,7 @@ public class SubCommand { } })); } else { - host.log.message.println("Usage: " + handle + " "); + host.log.message.println("Usage: /" + handle + " "); } } }.usage("").description("Starts a SubServer").help( @@ -290,7 +314,7 @@ public class SubCommand { } })); } else { - host.log.message.println("Usage: " + handle + " "); + host.log.message.println("Usage: /" + handle + " "); } } }.usage("").description("Stops a SubServer").help( @@ -330,7 +354,7 @@ public class SubCommand { } })); } else { - host.log.message.println("Usage: " + handle + " "); + host.log.message.println("Usage: /" + handle + " "); } } }.usage("").description("Terminates a SubServer").help( @@ -379,7 +403,7 @@ public class SubCommand { } })); } else { - host.log.message.println("Usage: " + handle + " [Args...]"); + host.log.message.println("Usage: /" + handle + " [Args...]"); } } }.usage("", "", "[Args...]").description("Sends a Command to a SubServer").help( @@ -431,7 +455,7 @@ public class SubCommand { })); } } else { - host.log.message.println("Usage: " + handle + "