diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..1f4b85c --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @JKetelaar @Fryslan @iSully @PBLord @SandroCoutinho @Shadowrs @Parabot/client-team \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 2fd0c5d..1e5ecbe 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,20 +1,8 @@ -* Provide a general summary of the issue in the **Title** above. -* Before you open an issue, please check if a similar issue already exists or has been closed before. +### Short description +... -### Important -Mark with [x] to select. Leave as [ ] to unselect. +### Steps to reproduce +... -### When reporting a bug/issue: -- [ ] Screenshot -- [ ] The Parabot version and the operating system you're using -- [ ] The behavior you expect to see, and the actual behavior -- [ ] Steps to reproduce the behavior -- [ ] \(optional) Possible solution/fix/workaround - -### When you open an issue for a change/improvement/feature request: -- [ ] A description of the problem you're trying to solve, including _why_ you think this is a problem -- [ ] If the feature changes current behavior, reasons why your solution is better -- [ ] \(optional) Possible solution/fix/workaround - -### Description +### Extra information (Operating system, device, etc) ... \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 82097bb..3fbb7bd 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,37 +1,2 @@ -Provide a general summary of your changes in the **Title** above. - -### Important -Mark with [x] to select. Leave as [ ] to unselect. -If possible; include a screenshot or gif of the change you've made - -### Motivation and Context -Each item you can check should be described in the _Description_ section. - -- [ ] Why is this change required? What problem does it solve? -- [ ] If it fixes an open issue, include the text `Closes issue #1` (where 1 would be the issue number) to your commit message. - -### Types of changes -What types of changes does your code introduce? Check all the boxes that apply: -- [ ] Bug fix (non-breaking change which fixes an issue) -- [ ] New feature (non-breaking change which adds functionality) -- [ ] Breaking change (fix or feature that would cause existing functionality to change) -- [ ] Cleanup (non-breaking change which cleans up the code) - -### Final checklist -Go over all the following points and check all the boxes that apply. -If you're unsure about any of these, don't hesitate to ask. We're here to help! -Various areas of the codebase have been worked on by different people in recent years, so if you are unfamiliar with the general area you're working in, please feel free to chat with people who have experience in that area. - -- [ ] My change requires a change to the documentation. -- [ ] I have updated the documentation accordingly. -- [ ] I have tested the functionality -- [ ] I have added tests for this functionality - -Your pull request will be tested via Travis CI to automatically indicate that your changes do not prevent compilation. - -If it reports back that there are problems, you go to [the Travis system](https://travis-ci.org/Parabot/Parabot) and check the log report for your pull request to see what the problem was. -If you add new code to fix a Travis building issue/problem, then take note that you need to check the next pull request in the Travis system. -Travis issue numbers are different from GitHub issue numbers. - ### Description ... \ No newline at end of file diff --git a/.github/labels.yml b/.github/labels.yml index b2323c2..0c578ea 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -1,30 +1,31 @@ -- name: priority:low - color: bfe5bf -- name: priority:medium - color: bfe5bf -- name: priority:high - color: bfe5bf -- name: status:accepted - color: fef2c0 -- name: status:unconfirmed - color: fef2c0 -- name: status:needs more info - color: fef2c0 -- name: status:rejected - color: fef2c0 -- name: status:under consideration - color: fef2c0 -- name: type:bug - color: f7c6c7 -- name: type:feature - color: f7c6c7 -- name: type:improvement - color: f7c6c7 -- name: type:question - color: f7c6c7 -- name: os:windows - color: "666699" -- name: os:mac - color: "666699" -- name: os:other - color: "666699" \ No newline at end of file +labels: + - name: priority:low + color: bfe5bf + - name: priority:medium + color: bfe5bf + - name: priority:high + color: bfe5bf + - name: status:accepted + color: fef2c0 + - name: status:unconfirmed + color: fef2c0 + - name: status:needs more info + color: fef2c0 + - name: status:rejected + color: fef2c0 + - name: status:under consideration + color: fef2c0 + - name: type:bug + color: f7c6c7 + - name: type:feature + color: f7c6c7 + - name: type:improvement + color: f7c6c7 + - name: type:question + color: f7c6c7 + - name: os:windows + color: "666699" + - name: os:mac + color: "666699" + - name: os:other + color: "666699" \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index ee17f98..49ff419 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,7 +34,7 @@ env: - secure: UG+b1tEgc8xv9x4r//2OAIK1RrYv6n209KTTFMMwcnAa7DI8HaP8nljRa5/VhDhuKHdlVrYH/tI90v7UVBs0GDVNwK5V17Io0fMm3FUGZekSthTCqqno5wAGa9r6a6mMLtSaSmIFeIKi0+0d2ZwplRuhj/dtEYjjBBj+kK8g4nE= - secure: St/fecUDInFBCRriYqgp2F8PU9/SooorgxD9Mrs+b0EsC7AbtSsQXvdIv2Lp6xzdQ0VSXPcLIhULPOYrmBKnGQ/NjXTIZXxnroyQxxnI6xyEWIZwiHRY/bKRJDRbQTxD9NL32szKiDSwnw7pu6llF4D64UqQvziq4Gm6VohU75M= - secure: bD15GVZWowiknbfLavh8CxSh0GsnF5kT4kZ6ggCuUDGyj0mzqf7dNRnchQIKkCG0WRYyTrFN4pEiygeywWsipEeAVv9Xhx3cuUZmzeQaR5KCWabSwJ8gK6jZd1YhcWmM9vrdPHobZr65MP0y/8mu/Fovgky9dY7KDf4G3SebNrM= - - PARABOT_VERSION=2.6.8 + - PARABOT_VERSION=2.7 cache: directories: diff --git a/README.md b/README.md index 8f46946..dfb55fb 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Parabot -Parabot V2.6.8. +Parabot V2.7 #### Links @@ -42,7 +42,7 @@ Then you'll need to add the dependency: org.parabot client - 2.6.3 + 2.7 ``` diff --git a/pom.xml b/pom.xml index 7a4d27a..04ea80a 100755 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.parabot client - 2.6.8 + 2.7 jar @@ -73,7 +73,7 @@ org.parabot internal-api - 1.51.1 + 1.52.1 diff --git a/src/main/java/org/parabot/Landing.java b/src/main/java/org/parabot/Landing.java index 51c6a67..f3cb7f9 100644 --- a/src/main/java/org/parabot/Landing.java +++ b/src/main/java/org/parabot/Landing.java @@ -15,10 +15,10 @@ import java.io.File; import java.io.IOException; /** - * Parabot v2.6 + * Parabot v2.7 * * @author Everel, JKetelaar, Matt, Dane - * @version 2.6 + * @version 2.7 * @see Homepage */ public final class Landing { @@ -73,6 +73,7 @@ public final class Landing { System.exit(0); break; case "-debug": + case "-offlinemode": Core.setDebug(true); break; case "-v": @@ -135,6 +136,9 @@ public final class Landing { case "-no_validation": Core.disableValidation(); break; + case "-uuid": + Core.setQuickLaunchByUuid(Integer.parseInt(args[++i])); + break; } } } diff --git a/src/main/java/org/parabot/core/Context.java b/src/main/java/org/parabot/core/Context.java index 943d051..0b2f0fa 100644 --- a/src/main/java/org/parabot/core/Context.java +++ b/src/main/java/org/parabot/core/Context.java @@ -32,11 +32,11 @@ import java.util.TimerTask; * @author Everel, JKetelaar, Matt */ public class Context { - public static final HashMap threadGroups = new HashMap(); - private static ArrayList paintables = new ArrayList(); + public static final HashMap threadGroups = new HashMap<>(); - private static Context instance; - private static String username; + private static ArrayList paintables = new ArrayList<>(); + private static Context instance; + private static String username; private ASMClassLoader classLoader; private ClassPath classPath; @@ -52,38 +52,58 @@ public class Context { private PBKeyListener pbKeyListener; private ServerProviderInfo providerInfo; private JSONParser jsonParser; - - private PrintStream defaultOut; - private PrintStream defaultErr = System.err; + private PrintStream defaultOut; + private PrintStream defaultErr; private Context(final ServerProvider serverProvider) { threadGroups.put(Thread.currentThread().getThreadGroup(), this); System.setProperty("sun.java.command", ""); + this.serverProvider = serverProvider; this.paintDebugger = new PaintDebugger(); this.classPath = new ClassPath(); this.classLoader = new ASMClassLoader(classPath); this.randomHandler = new RandomHandler(); - this.jsonParser = new JSONParser(); - this.defaultOut = System.out; this.defaultErr = System.err; } + /** + * Returns the instance of this class, based on a given ServerProvider + * + * @param serverProvider + * + * @return + */ public static Context getInstance(ServerProvider serverProvider) { return instance == null ? instance = new Context(serverProvider) : instance; } + /** + * Returns the instance of this class + * + * @return + */ public static Context getInstance() { return getInstance(null); } + /** + * Returns the username of the current logged in user to Parabot + * + * @return + */ public static String getUsername() { return username; } + /** + * Sets the username for the logged in user to Parabot + * + * @param username + */ public static void setUsername(String username) { Context.username = username; } @@ -185,9 +205,13 @@ public class Context { panel.add(gameApplet); panel.validate(); + serverProvider.preAppletInit(); + gameApplet.init(); gameApplet.start(); + serverProvider.postAppletStart(); + java.util.Timer t = new java.util.Timer(); t.schedule(new TimerTask() { @Override @@ -230,7 +254,8 @@ public class Context { Core.verbose(TranslationHelper.translate("DONE")); } Applet applet = serverProvider.fetchApplet(); - // if applet is null the server provider will call setApplet itself + + // If applet is null the server provider will call setApplet itself if (applet != null) { setApplet(applet); } @@ -363,10 +388,20 @@ public class Context { return this.randomHandler; } + /** + * Returns the JSON Parser instance + * + * @return + */ public JSONParser getJsonParser() { return jsonParser; } + /** + * Returns the PBKeyListener instance + * + * @return + */ public PBKeyListener getPbKeyListener() { return pbKeyListener; } diff --git a/src/main/java/org/parabot/core/Core.java b/src/main/java/org/parabot/core/Core.java index 68f8623..0688108 100644 --- a/src/main/java/org/parabot/core/Core.java +++ b/src/main/java/org/parabot/core/Core.java @@ -28,7 +28,8 @@ import java.security.NoSuchAlgorithmException; @SuppressWarnings("Duplicates") public class Core { - private static boolean debug; + private static int quickLaunchByUuid = -1; // used like -server, but denoted by an Int rather than the server name + private static boolean debug; // Debug mode is Offline Mode. No BDN connection for Servers/Scripts/User Login. Not related to debug messages. private static boolean verbose; private static boolean dump; private static boolean loadLocal; //Loads both local and public scripts/servers @@ -46,6 +47,14 @@ public class Core { return validate; } + public static int getQuickLaunchByUuid() { + return quickLaunchByUuid; + } + + public static void setQuickLaunchByUuid(int quickLaunchByUuid) { + Core.quickLaunchByUuid = quickLaunchByUuid; + } + /** * Enabled loadLocal mode * @@ -63,7 +72,7 @@ public class Core { } /** - * Enabled debug mode + * Set debug mode AKA Offline Mode. If true, BDN login will be skipped, so BDN Servers or Scripts will be unavailable. * * @param debug */ @@ -95,7 +104,7 @@ public class Core { } /** - * @return if the client is in debug mode. + * @return if the client is in debug mode AKA Offline Mode. BDN Servers and Scripts are unavailable. */ public static boolean inDebugMode() { return debug; @@ -161,7 +170,9 @@ public class Core { String result; if ((result = WebUtil.getContents(String.format(Configuration.COMPARE_CHECKSUM_URL, "client", currentVersion.get()), "checksum=" + URLEncoder.encode(sb.toString(), "UTF-8"))) != null) { JSONObject object = (JSONObject) WebUtil.getJsonParser().parse(result); - return (boolean) object.get("result"); + boolean upToDate = (boolean) object.get("result"); + Core.verbose("Local checksum: " + URLEncoder.encode(sb.toString(), "UTF-8") + ". " + (upToDate ? "This matches BDN and is up to date." : "BDN mismatch, must be Out Of Date.")); + return upToDate; } } } catch (NoSuchAlgorithmException | ParseException | IOException | URISyntaxException e) { @@ -187,6 +198,7 @@ public class Core { if (!latest) { Directories.clearCache(); } + Core.verbose("Local version: " + currentVersion.get() + ". " + (latest ? "This is up to date." : "This is Out Of Date. Cache will be cleared.")); return latest; } } catch (IOException | ParseException e) { diff --git a/src/main/java/org/parabot/core/asm/RedirectClassAdapter.java b/src/main/java/org/parabot/core/asm/RedirectClassAdapter.java index 9ecebad..6bd0e49 100644 --- a/src/main/java/org/parabot/core/asm/RedirectClassAdapter.java +++ b/src/main/java/org/parabot/core/asm/RedirectClassAdapter.java @@ -16,8 +16,12 @@ import java.util.Map; public class RedirectClassAdapter extends ClassVisitor implements Opcodes { + public static Map> getRedirects() { + return redirects; + } + private static final Map> redirects = new HashMap>(); - private static PrintStream str_out, class_out; + private static PrintStream str_out, class_out; static { redirects.put("java/awt/Toolkit", ToolkitRedirect.class); diff --git a/src/main/java/org/parabot/core/asm/adapters/AddCallbackAdapter.java b/src/main/java/org/parabot/core/asm/adapters/AddCallbackAdapter.java index 1384452..8b26227 100644 --- a/src/main/java/org/parabot/core/asm/adapters/AddCallbackAdapter.java +++ b/src/main/java/org/parabot/core/asm/adapters/AddCallbackAdapter.java @@ -40,17 +40,19 @@ public class AddCallbackAdapter implements Injectable, Opcodes { Label l0 = new Label(); inject.add(new LabelNode(l0)); int offset = 0; - for (int arg : args) { - if (Modifier.isStatic(method.access)) { - int loadOpcode = ASMUtils.getLoadOpcode(types[arg] - .getDescriptor()); - inject.add(new VarInsnNode(loadOpcode, arg + offset)); - if (loadOpcode == Opcodes.LLOAD) { - offset++; + if (args != null) { + for (int arg : args) { + if (Modifier.isStatic(method.access)) { + int loadOpcode = ASMUtils.getLoadOpcode(types[arg] + .getDescriptor()); + inject.add(new VarInsnNode(loadOpcode, arg + offset)); + if (loadOpcode == Opcodes.LLOAD) { + offset++; + } + } else { + inject.add(new VarInsnNode(ASMUtils.getLoadOpcode(types[arg] + .getDescriptor()), arg + 1)); } - } else { - inject.add(new VarInsnNode(ASMUtils.getLoadOpcode(types[arg] - .getDescriptor()), arg + 1)); } } inject.add(new MethodInsnNode(INVOKESTATIC, diff --git a/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java b/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java index dde1499..0925aeb 100644 --- a/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java +++ b/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java @@ -6,8 +6,7 @@ import org.parabot.environment.scripts.Script; import java.io.InputStream; import java.lang.annotation.Annotation; -import java.lang.reflect.Field; -import java.lang.reflect.Method; +import java.lang.reflect.*; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.ProtectionDomain; @@ -25,7 +24,7 @@ public class ClassRedirect { } public static Object newInstance(Class c) throws IllegalAccessException, InstantiationException { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.newInstance(); } @@ -34,7 +33,7 @@ public class ClassRedirect { } public static Field getDeclaredField(Class c, String s) throws NoSuchFieldException, SecurityException { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.getDeclaredField(s); } @@ -43,7 +42,7 @@ public class ClassRedirect { } public static Method getDeclaredMethod(Class c, String name, Class... params) throws NoSuchMethodException, SecurityException { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.getDeclaredMethod(name, params); } @@ -65,7 +64,7 @@ public class ClassRedirect { } public static Field[] getDeclaredFields(Class c) { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.getDeclaredFields(); } System.err.println(c.getName() + "#getDeclaredFields()" + " Blocked."); @@ -73,7 +72,7 @@ public class ClassRedirect { } public static Method[] getDeclaredMethods(Class c) { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.getDeclaredMethods(); } System.err.println(c.getName() + "#getDeclaredMethods()" + " Blocked."); @@ -81,7 +80,7 @@ public class ClassRedirect { } public static Field[] getFields(Class c) { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.getFields(); } System.err.println(c.getName() + "#getFields()" + " Blocked."); @@ -89,7 +88,7 @@ public class ClassRedirect { } public static Annotation[] getAnnotations(Class c) { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.getAnnotations(); } System.err.println(c.getName() + "#getFields()" + " Blocked."); @@ -105,7 +104,7 @@ public class ClassRedirect { } public static Method getMethod(Class c, String name, Class... params) throws NoSuchMethodException, SecurityException { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.getMethod(name, params); } System.err.println(c.getName() + "#getMethod()" + " Blocked."); @@ -114,7 +113,7 @@ public class ClassRedirect { public static Field getField(Class c, String name) throws NoSuchFieldException, SecurityException { - if (validStack()) { + if (validStack() || validRequest(c)) { return c.getField(name); } System.err.println(c.getName() + "#getField()" + " Blocked."); @@ -146,6 +145,62 @@ public class ClassRedirect { return !c.getName().contains("parabot") && c.desiredAssertionStatus(); } + public static Type getGenericSuperclass(Class c) { + return c.getGenericSuperclass(); + } + + public static boolean isArray(Class c) { + return c.isArray(); + } + + public static int getModifiers(Class c) { + return c.getModifiers(); + } + + public static Class getEnclosingClass(Class c) { + return c.getEnclosingClass(); + } + + public static boolean isPrimitive(Class c) { + return c.isPrimitive(); + } + + public static boolean isAssignableFrom(Class c1, Class c2) { + return c1.isAssignableFrom(c2); + } + + public static boolean isAnonymousClass(Class c) { + return c.isAnonymousClass(); + } + + public static boolean isLocalClass(Class c) { + return c.isLocalClass(); + } + + public static boolean isInterface(Class c) { + return c.isInterface(); + } + + public static Class[] getInterfaces(Class c) { + return c.getInterfaces(); + } + + public static Type[] getGenericInterfaces(Class c) { + return c.getGenericInterfaces(); + } + + public static TypeVariable[] getTypeParameters(Class c) { + return c.getTypeParameters(); + } + + public static Annotation getAnnotation(Class c, Class annotationClass) { + return c.getAnnotation(annotationClass); + } + + public static Constructor getDeclaredConstructor(Class c, Class[] parameterTypes) throws NoSuchMethodException, SecurityException { + return c.getDeclaredConstructor(parameterTypes); + } + private static boolean validStack() { Exception e = new Exception(); for (StackTraceElement elem : e.getStackTrace()) { @@ -155,4 +210,9 @@ public class ClassRedirect { } return false; } + + private static boolean validRequest(Class c) { + Core.verbose("Got request for class: " + c.getName()); + return !c.getName().toLowerCase().contains("parabot"); + } } diff --git a/src/main/java/org/parabot/core/asm/wrappers/Callback.java b/src/main/java/org/parabot/core/asm/wrappers/Callback.java index 039b41c..5917f0f 100644 --- a/src/main/java/org/parabot/core/asm/wrappers/Callback.java +++ b/src/main/java/org/parabot/core/asm/wrappers/Callback.java @@ -26,14 +26,16 @@ public class Callback implements Injectable { this.invokeMethod = callbackMethod; this.desc = callbackDesc; this.conditional = conditional; - if (args.contains(",")) { - final String[] strArgs = args.split(","); - this.args = new int[strArgs.length]; - for (int i = 0; i < this.args.length; i++) { - this.args[i] = Integer.parseInt(strArgs[i]); + if (args.length() > 0) { + if (args.contains(",")) { + final String[] strArgs = args.split(","); + this.args = new int[strArgs.length]; + for (int i = 0; i < this.args.length; i++) { + this.args[i] = Integer.parseInt(strArgs[i]); + } + } else { + this.args = new int[]{Integer.parseInt(args)}; } - } else { - this.args = new int[]{ Integer.parseInt(args) }; } } diff --git a/src/main/java/org/parabot/core/desc/ServerDescription.java b/src/main/java/org/parabot/core/desc/ServerDescription.java index 687410c..dcae4c8 100644 --- a/src/main/java/org/parabot/core/desc/ServerDescription.java +++ b/src/main/java/org/parabot/core/desc/ServerDescription.java @@ -9,6 +9,7 @@ public class ServerDescription implements Comparable { private String serverName; private String author; private double revision; + public int uuid; public ServerDescription(final String serverName, final String author, final double revision) { @@ -37,6 +38,12 @@ public class ServerDescription implements Comparable { @Override public int compareTo(ServerDescription o) { + if (this.getServerName().equalsIgnoreCase(o.getServerName())) { + if (getAuthor().equals(o.getAuthor())) { + return Double.compare(o.getRevision(), getRevision()); + } + return getAuthor().compareTo(o.getAuthor()); + } return this.getServerName().compareTo(o.getServerName()); } diff --git a/src/main/java/org/parabot/core/desc/ServerProviderInfo.java b/src/main/java/org/parabot/core/desc/ServerProviderInfo.java index 4172151..a4e8c98 100644 --- a/src/main/java/org/parabot/core/desc/ServerProviderInfo.java +++ b/src/main/java/org/parabot/core/desc/ServerProviderInfo.java @@ -50,7 +50,28 @@ public class ServerProviderInfo { } } + /** + * Initialize configuration with data provided by {@link org.parabot.core.parsers.servers.LocalServers} from a {@code /parabot/servers/config.json} file. Also loads the default Settings map from the BDN. + * @param clientJar Name of the client jar file + * @param hooks Name of the hooks file + * @param name Server name + * @param clientClass Entry class within the client jar + * @param bankTabs Bank tabs - only relevant for certain servers. Default 0 + */ public ServerProviderInfo(String clientJar, String hooks, String name, String clientClass, int bankTabs) { + this(clientJar, hooks, name, clientClass, bankTabs, null); + } + + /** + * Initialize configuration with data provided by {@link org.parabot.core.parsers.servers.LocalServers} from a {@code /parabot/servers/config.json} file. Also loads the default Settings map from the BDN. + * @param clientJar Name of the client jar file + * @param hooks Name of the hooks file + * @param name Server name + * @param clientClass Entry class within the client jar + * @param bankTabs Bank tabs - only relevant for certain servers. Default 0 + * @param randoms A URL to an endpoint where the Randoms are located. Can be Null, in which case getRandoms() will fallback to the default BDN Randoms URL. + */ + public ServerProviderInfo(String clientJar, String hooks, String name, String clientClass, int bankTabs, String randoms) { this.properties = new Properties(); this.settings = new HashMap<>(); @@ -69,6 +90,7 @@ public class ServerProviderInfo { this.properties.setProperty("provider_crc32", String.valueOf(getCRC32(name, "provider"))); this.properties.setProperty("client_crc32", String.valueOf(getCRC32(name, "client"))); this.properties.setProperty("bank_tabs", String.valueOf(bankTabs)); + this.properties.setProperty("randoms_jar", randoms); } private long getCRC32(String name, String type) { @@ -141,4 +163,23 @@ public class ServerProviderInfo { public HashMap getSettings() { return settings; } + + /** + * Gets the URL to download the Randoms JAR from. + * @return The provided URL in the server config JSON (denoted by 'randoms:') or, fallback to the default BDN URL. + */ + public URL getRandoms() { + try { + String randomsUrl = properties.getProperty("randoms_jar"); + if (randomsUrl == null || randomsUrl.length() == 0) { + // Fallback to default BDN URL if there is no 'randoms' specified in the server JSON configuration. + randomsUrl = Configuration.GET_RANDOMS + (Configuration.BOT_VERSION.isNightly() ? Configuration.NIGHTLY_APPEND : ""); + } + return new URL(randomsUrl); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + // Will never return null, unless the BDN URL is changed. It shouldn't be. + return null; + } } diff --git a/src/main/java/org/parabot/core/io/NoProgressListener.java b/src/main/java/org/parabot/core/io/NoProgressListener.java index 247bf39..2669b9b 100644 --- a/src/main/java/org/parabot/core/io/NoProgressListener.java +++ b/src/main/java/org/parabot/core/io/NoProgressListener.java @@ -13,4 +13,19 @@ public class NoProgressListener implements ProgressListener { public void updateDownloadSpeed(double mbPerSecond) { } + + @Override + public void updateMessage(String message) { + + } + + @Override + public void updateMessageAndProgress(String message, double progress) { + + } + + @Override + public double getCurrentProgress() { + return 0; + } } diff --git a/src/main/java/org/parabot/core/parsers/hooks/XMLHookParser.java b/src/main/java/org/parabot/core/parsers/hooks/XMLHookParser.java index af01411..e7fa473 100644 --- a/src/main/java/org/parabot/core/parsers/hooks/XMLHookParser.java +++ b/src/main/java/org/parabot/core/parsers/hooks/XMLHookParser.java @@ -65,8 +65,17 @@ public class XMLHookParser extends HookParser { } private static final String getValue(String tag, Element element) { + if (element.getElementsByTagName(tag).item(0) == null) { + throw new NullPointerException("MISSING HOOK TAG: The '" + tag + "' xml tag is missing from one of the hooks of type: " + element.getParentNode().getNodeName()); + } NodeList nodes = element.getElementsByTagName(tag).item(0) .getChildNodes(); + if (nodes.getLength() == 0 || nodes.item(0) == null) { + if (Core.inVerboseMode()) { + System.err.println("WARNING: Invalid Hook " + tag + " subnode. Tag is missing or empty?"); + } + return ""; + } Node node = (Node) nodes.item(0); return node.getNodeValue(); } diff --git a/src/main/java/org/parabot/core/parsers/randoms/PublicRandoms.java b/src/main/java/org/parabot/core/parsers/randoms/PublicRandoms.java index ed285ff..69973b4 100644 --- a/src/main/java/org/parabot/core/parsers/randoms/PublicRandoms.java +++ b/src/main/java/org/parabot/core/parsers/randoms/PublicRandoms.java @@ -1,18 +1,18 @@ package org.parabot.core.parsers.randoms; -import org.parabot.api.io.WebUtil; -import org.parabot.core.Configuration; -import org.parabot.core.Context; -import org.parabot.core.Core; -import org.parabot.core.Directories; -import org.parabot.core.io.NoProgressListener; - import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; +import org.parabot.api.io.WebUtil; +import org.parabot.api.output.Logger; +import org.parabot.core.Configuration; +import org.parabot.core.Context; +import org.parabot.core.Core; +import org.parabot.core.Directories; +import org.parabot.core.io.NoProgressListener; /** * @author JKetelaar @@ -23,12 +23,21 @@ public class PublicRandoms extends RandomParser { @Override public void parse() { - File myJar = new File(Directories.getCachePath() + File.separator + fileName); - if (!myJar.exists() || !myJar.canRead()) { - download(); + + final File destination = new File(Directories.getCachePath() + File.separator + fileName); + final URL overrideDownload = Context.getInstance().getServerProviderInfo().getRandoms(); + if (overrideDownload == null) { + throw new NullPointerException("Unable to grab URL for Randoms jar. Default URL for BDN randoms must have changed!"); + } + + Core.verbose(String.format("[%s] Destination: %s | dl: %s", getClass().getSimpleName(), destination, overrideDownload)); + + if (!destination.exists() || !destination.canRead()) { + Core.verbose(String.format("[%s] Missing %s - downloading from %s...", getClass().getSimpleName(), destination.getAbsolutePath(), overrideDownload)); + download(destination, overrideDownload); } try { - URL url = myJar.toURI().toURL(); + URL url = destination.toURI().toURL(); URL[] urls = new URL[]{ url }; String server = Context.getInstance().getServerProviderInfo().getServerName(); @@ -36,7 +45,7 @@ public class PublicRandoms extends RandomParser { Class classToLoad = Class.forName("org.parabot.randoms.Core", true, child); Method method = classToLoad.getDeclaredMethod("init", String.class); Object instance = classToLoad.newInstance(); - System.out.println(server); + Core.verbose(String.format("[%s] %s %s", getClass().getSimpleName(), "Initing core Randoms for", server)); method.invoke(instance, server); Core.verbose("Successfully parsed public random!"); } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException | ClassNotFoundException | MalformedURLException e) { @@ -45,17 +54,14 @@ public class PublicRandoms extends RandomParser { } } - private void download() { + private void download(final File destination, URL downloadLink) { try { - File random = new File(Directories.getCachePath() + File.separator + fileName); - if (random.exists()) { + if (destination.exists()) { Core.verbose("Public random dependency already exists, no need to download it..."); return; } - String downloadLink = ((Configuration.BOT_VERSION.isNightly()) ? Configuration.GET_RANDOMS + Configuration.NIGHTLY_APPEND : Configuration.GET_RANDOMS); - - WebUtil.downloadFile(new URL(downloadLink), random, new NoProgressListener()); + WebUtil.downloadFile(downloadLink, destination, new NoProgressListener()); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/main/java/org/parabot/core/parsers/servers/LocalServers.java b/src/main/java/org/parabot/core/parsers/servers/LocalServers.java index eb0f698..3bf725e 100644 --- a/src/main/java/org/parabot/core/parsers/servers/LocalServers.java +++ b/src/main/java/org/parabot/core/parsers/servers/LocalServers.java @@ -1,7 +1,13 @@ package org.parabot.core.parsers.servers; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.ArrayList; import org.json.simple.JSONObject; import org.json.simple.parser.ParseException; +import org.parabot.core.Configuration; import org.parabot.core.Core; import org.parabot.core.Directories; import org.parabot.core.classpath.ClassPath; @@ -13,12 +19,6 @@ import org.parabot.environment.servers.executers.LocalPublicServerExecuter; import org.parabot.environment.servers.executers.LocalServerExecuter; import org.parabot.environment.servers.loader.ServerLoader; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.util.ArrayList; - /** * Parses local server providers located in the servers directory * @@ -86,17 +86,28 @@ public class LocalServers extends ServerParser { if ((bank = object.get("bank")) != null) { bankTabs = (int) bank; } + String uuidStr = (String) object.get("uuid"); // optional + JSONObject locations = (JSONObject) object.get("locations"); String server = (String) locations.get("server"); String provider = (String) locations.get("provider"); String hooks = (String) locations.get("hooks"); + String randoms = (String) locations.get("randoms"); + + if (randoms == null) { + randoms = Configuration.GET_RANDOMS + (Configuration.BOT_VERSION.isNightly() ? Configuration.NIGHTLY_APPEND : ""); + } + + Core.verbose("[LocalServers]: Parsed server: " + name); + + ServerProviderInfo serverProviderInfo = new ServerProviderInfo(server, hooks, name, clientClass, bankTabs, randoms); - Core.verbose("[Local server]: " + name); - ServerProviderInfo serverProviderInfo = new ServerProviderInfo(server, hooks, name, clientClass, bankTabs); + ServerDescription desc = new ServerDescription(name, author, version); + if (uuidStr != null && uuidStr.length() > 0) { + desc.uuid = Integer.parseInt(uuidStr); + } - ServerDescription desc = new ServerDescription(name, - author, version); SERVER_CACHE.put(desc, new LocalPublicServerExecuter(name, serverProviderInfo, server, provider)); } catch (IOException | ParseException e) { e.printStackTrace(); diff --git a/src/main/java/org/parabot/core/ui/LoginUI.java b/src/main/java/org/parabot/core/ui/LoginUI.java index f93b723..2b888a6 100644 --- a/src/main/java/org/parabot/core/ui/LoginUI.java +++ b/src/main/java/org/parabot/core/ui/LoginUI.java @@ -6,6 +6,7 @@ import org.parabot.core.forum.AccountManager; import org.parabot.core.ui.images.Images; import org.parabot.core.ui.utils.SwingUtil; import org.parabot.core.ui.utils.UILog; +import org.parabot.environment.input.Keyboard; import javax.swing.*; import java.awt.*; @@ -72,7 +73,7 @@ public class LoginUI extends JFrame { txtUsername.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == 10 || e.getKeyCode() == 13) { + if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == Keyboard.ENTER_KEYCODE) { txtPassword.requestFocus(); } } @@ -85,7 +86,7 @@ public class LoginUI extends JFrame { txtPassword.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { - if (e.getKeyCode() == 10 || e.getKeyCode() == 13) { + if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == Keyboard.ENTER_KEYCODE) { attemptLogin(); } } diff --git a/src/main/java/org/parabot/core/ui/ServerSelector.java b/src/main/java/org/parabot/core/ui/ServerSelector.java index 31da8dd..8140122 100755 --- a/src/main/java/org/parabot/core/ui/ServerSelector.java +++ b/src/main/java/org/parabot/core/ui/ServerSelector.java @@ -1,5 +1,6 @@ package org.parabot.core.ui; +import org.parabot.core.Core; import org.parabot.core.desc.ServerDescription; import org.parabot.core.parsers.servers.ServerParser; import org.parabot.core.ui.components.ServerComponent; @@ -23,7 +24,7 @@ public class ServerSelector extends JPanel { public ServerSelector() { Queue widgets = getServers(); - if (initServer != null) { + if (initServer != null || Core.getQuickLaunchByUuid() > -1) { if (runServer(widgets)) { initServer = null; return; @@ -67,7 +68,7 @@ public class ServerSelector extends JPanel { } /** - * This method is called when -server argument is given + * This method is called when -server argument is given, or -uuid arg is given. * * @param widgets */ @@ -75,12 +76,25 @@ public class ServerSelector extends JPanel { if (widgets == null || widgets.isEmpty()) { return false; } - final String serverName = initServer.toLowerCase(); - for (ServerComponent widget : widgets) { - if (widget.desc.getServerName().toLowerCase().equals(serverName)) { - Environment.load(widget.desc); - return true; + if (Core.getQuickLaunchByUuid() > -1) { // match the pre-requested server config uuid to quick-launch + for (ServerComponent widget : widgets) { + if (widget.desc.uuid == Core.getQuickLaunchByUuid()) { + Environment.load(widget.desc); + return true; + } } + System.err.println("No server config with -uuid " + Core.getQuickLaunchByUuid() + " was found to quick launch."); + } + + if (initServer != null) { + final String serverName = initServer.toLowerCase(); // match the pre-requested server name to quick-launch + for (ServerComponent widget : widgets) { + if (widget.desc.getServerName().toLowerCase().equals(serverName)) { + Environment.load(widget.desc); + return true; + } + } + System.err.println("No server config with -server " + serverName + " was found to quick launch."); } return false; } diff --git a/src/main/java/org/parabot/core/ui/components/VerboseLoader.java b/src/main/java/org/parabot/core/ui/components/VerboseLoader.java index 929bdb8..9039f5f 100755 --- a/src/main/java/org/parabot/core/ui/components/VerboseLoader.java +++ b/src/main/java/org/parabot/core/ui/components/VerboseLoader.java @@ -275,4 +275,20 @@ public class VerboseLoader extends JPanel implements ProgressListener { public void updateDownloadSpeed(double mbPerSecond) { progressBar.setText(String.format("(%.2fMB/s)", mbPerSecond)); } + + @Override + public void updateMessage(String message) { + VerboseLoader.setState(message); + } + + @Override + public void updateMessageAndProgress(String message, double progress) { + VerboseLoader.setState(message); + onProgressUpdate(progress); + } + + @Override + public double getCurrentProgress() { + return progressBar.getValue(); + } } \ No newline at end of file diff --git a/src/main/java/org/parabot/environment/input/Keyboard.java b/src/main/java/org/parabot/environment/input/Keyboard.java index 5dd3a96..4386b2d 100644 --- a/src/main/java/org/parabot/environment/input/Keyboard.java +++ b/src/main/java/org/parabot/environment/input/Keyboard.java @@ -16,6 +16,12 @@ import java.util.Random; public class Keyboard implements KeyListener { private static HashMap specialChars; + /** + * {@code KeyEvent.VK_ENTER} is actually New Line, '\n'. + * The code for the Enter button is 13. It has no associated {@link KeyEvent} constant. + */ + public static final int ENTER_KEYCODE = 13; + static { char[] spChars = { '~', '!', '@', '#', '%', '^', '&', '*', '(', ')', '_', '+', '{', '}', ':', '<', '>', '?', '"', '|' }; @@ -38,12 +44,30 @@ public class Keyboard implements KeyListener { return Context.getInstance().getKeyboard(); } + /** + * Generates a random number in the range of 40-140. + * @return The random number + */ private static long getRandom() { Random rand = new Random(); return rand.nextInt(100) + 40; } + /** + * Types the given String and afterwards presses Enter. + * + * @param s The String to type. + */ public void sendKeys(String s) { + sendKeys(s, true); + } + + /** + * Types the given String and optionally presses Enter afterwards. + * @param s The String to type. + * @param enterAfter True if {@code KeyEvent.VK_ENTER} should be pressed afterwards. This is actually the '\n' character, for New Line. Useful for logging in. + */ + public void sendKeys(String s, boolean enterAfter) { pressTime = System.currentTimeMillis(); for (char c : s.toCharArray()) { @@ -56,9 +80,15 @@ public class Keyboard implements KeyListener { sendKeyEvent(ke); } } - clickKey(10); + if (enterAfter) { + clickKey(KeyEvent.VK_ENTER); + } } + /** + * Creates and sends a single KeyEvent using the given Char. + * @param c The char to send. + */ public void clickKey(char c) { pressTime = System.currentTimeMillis(); @@ -67,6 +97,11 @@ public class Keyboard implements KeyListener { } } + /** + * Creates and sends a given KeyEvent using the given keyCode. + *

Use constants where possible, from {@link KeyEvent}, such as {@code KeyEvent.VK_ENTER} + * @param keyCode The keycode to send. + */ public void clickKey(int keyCode) { pressTime = System.currentTimeMillis(); @@ -75,6 +110,12 @@ public class Keyboard implements KeyListener { } } + /** + * Creates and sends a given PRESS KeyEvent using the given keyCode. Note, this does not send a Release Event + * typically associated with a key click. + *

Use constants where possible, from {@link KeyEvent}, such as {@code KeyEvent.VK_ENTER} + * @param keyCode + */ public void pressKey(int keyCode) { pressTime = System.currentTimeMillis(); @@ -82,6 +123,12 @@ public class Keyboard implements KeyListener { sendKeyEvent(ke); } + /** + * Creates and sends a given RELEASE KeyEvent using the given keyCode. Note, this does not send a Press Event + * typically associated with a key click. + *

Use constants where possible, from {@link KeyEvent}, such as {@code KeyEvent.VK_ENTER} + * @param keyCode + */ public void releaseKey(int keyCode) { pressTime = System.currentTimeMillis(); @@ -89,6 +136,15 @@ public class Keyboard implements KeyListener { sendKeyEvent(ke); } + /** + * Creates KeyEvents to perform a Click of the given Char. This includes a Press, Typed and Release event + * in addition to an initial shiftDown and ending shiftUp if the character is a Special Char such as {@code !"£$%^&*(} + * + * {@see specialChars} + * @param target Component this event is linked to. + * @param c Char to send. + * @return KeyEvents for each action. + */ private KeyEvent[] createKeyClick(Component target, char c) { pressTime += 2 * getRandom(); @@ -129,27 +185,23 @@ public class Keyboard implements KeyListener { } } + /** + * Creates KeyEvents for Press and Release of the given keyCode. + * @param target + * @param keyCode + * @return An array containing Press and Release KeyEvents. + */ private KeyEvent[] createKeyClick(Component target, int keyCode) { - int modifier = 0; - switch (keyCode) { - case KeyEvent.VK_SHIFT: - modifier = KeyEvent.SHIFT_MASK; - break; - case KeyEvent.VK_ALT: - modifier = KeyEvent.ALT_MASK; - break; - case KeyEvent.VK_CONTROL: - modifier = KeyEvent.CTRL_MASK; - break; - } - KeyEvent pressed = new KeyEvent(target, KeyEvent.KEY_PRESSED, - pressTime, modifier, keyCode, KeyEvent.CHAR_UNDEFINED); - KeyEvent released = new KeyEvent(target, KeyEvent.KEY_RELEASED, - pressTime + getRandom(), 0, keyCode, KeyEvent.CHAR_UNDEFINED); - return new KeyEvent[]{ pressed, released }; + return new KeyEvent[]{ createKeyPress(target, keyCode), createKeyRelease(target, keyCode) }; } + /** + * Creates a Press type KeyEvent + * @param target + * @param keyCode + * @return + */ private KeyEvent createKeyPress(Component target, int keyCode) { int modifier = 0; switch (keyCode) { @@ -169,26 +221,23 @@ public class Keyboard implements KeyListener { return pressed; } + /** + * Creates a Release type KeyEvent + * @param target + * @param keyCode + * @return + */ private KeyEvent createKeyRelease(Component target, int keyCode) { - @SuppressWarnings("unused") - int modifier = 0; - switch (keyCode) { - case KeyEvent.VK_SHIFT: - modifier = KeyEvent.SHIFT_MASK; - break; - case KeyEvent.VK_ALT: - modifier = KeyEvent.ALT_MASK; - break; - case KeyEvent.VK_CONTROL: - modifier = KeyEvent.CTRL_MASK; - break; - } KeyEvent released = new KeyEvent(target, KeyEvent.KEY_RELEASED, pressTime + getRandom(), 0, keyCode, KeyEvent.CHAR_UNDEFINED); return released; } + /** + * Actually triggers sending of a given KeyEvent in the instance of KeyListeners' {@code component} field. + * @param e + */ public void sendKeyEvent(KeyEvent e) { for (KeyListener kl : component.getKeyListeners()) { if (kl instanceof Keyboard) { @@ -210,16 +259,28 @@ public class Keyboard implements KeyListener { } } + /** + * Allows the {@code KeyListener.keyPressed} event to be overridden. + * @param e + */ @Override public void keyPressed(KeyEvent e) { } + /** + * Allows the {@code KeyListener.keyReleased} event to be overridden. + * @param e + */ @Override public void keyReleased(KeyEvent e) { } + /** + * Allows the {@code KeyListener.keyTyped} event to be overridden. + * @param e + */ @Override public void keyTyped(KeyEvent e) { diff --git a/src/main/java/org/parabot/environment/scripts/Frameworks.java b/src/main/java/org/parabot/environment/scripts/Frameworks.java deleted file mode 100644 index af5d40f..0000000 --- a/src/main/java/org/parabot/environment/scripts/Frameworks.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.parabot.environment.scripts; - -import org.parabot.environment.scripts.framework.AbstractFramework; -import org.parabot.environment.scripts.framework.LoopTask; -import org.parabot.environment.scripts.framework.Strategy; - -import java.util.Collection; - -/** - * Holds various script frameworks - * - * @author Everel - */ -public class Frameworks { - - public static Looper getLooper(LoopTask loopTask) { - return new Looper(loopTask); - } - - public static StrategyWorker getStrategyWorker(Collection strategies) { - return new StrategyWorker(strategies); - } - -} - -class Looper extends AbstractFramework { - private LoopTask loopTask = null; - - public Looper(LoopTask loopTask) { - this.loopTask = loopTask; - } - - @Override - public boolean execute() { - int sleepTime = loopTask.loop(); - if (sleepTime < 0) { - return false; - } - try { - Thread.sleep(sleepTime); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return true; - } -} - -class StrategyWorker extends AbstractFramework { - private Collection strategies; - - public StrategyWorker(Collection strategies) { - this.strategies = strategies; - } - - @Override - public boolean execute() { - for (Strategy s : strategies) { - if (s.activate()) { - s.execute(); - return true; - } - } - return true; - } - -} diff --git a/src/main/java/org/parabot/environment/servers/ServerProvider.java b/src/main/java/org/parabot/environment/servers/ServerProvider.java index da9b465..6765781 100644 --- a/src/main/java/org/parabot/environment/servers/ServerProvider.java +++ b/src/main/java/org/parabot/environment/servers/ServerProvider.java @@ -146,4 +146,20 @@ public abstract class ServerProvider implements Opcodes { } + /** + * Called in Context.setApplet before applet.init() is called. Exclusively used for manipulating the Frame attached + * to the applet of Roatpkz. + */ + public void preAppletInit() { + + } + + /** + * Called in Context.setApplet before after applet.start() and applet.init() are called. Exclusively used for manipulating the Frame attached + * to the applet of Roatpkz. + */ + public void postAppletStart() { + + } + }