diff --git a/.travis.yml b/.travis.yml index 49ff419..f660a2b 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.7 + - PARABOT_VERSION=2.8 cache: directories: diff --git a/README.md b/README.md index dfb55fb..03897aa 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # Parabot -Parabot V2.7 +Parabot V2.8 #### Links diff --git a/pom.xml b/pom.xml index 04ea80a..0fcc6fb 100755 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.parabot client - 2.7 + 2.8 jar @@ -73,7 +73,7 @@ org.parabot internal-api - 1.52.1 + 1.53.1 diff --git a/src/main/java/org/parabot/Landing.java b/src/main/java/org/parabot/Landing.java index f3cb7f9..b5ff725 100644 --- a/src/main/java/org/parabot/Landing.java +++ b/src/main/java/org/parabot/Landing.java @@ -1,6 +1,7 @@ package org.parabot; import org.parabot.api.translations.TranslationHelper; +import org.parabot.core.Context; import org.parabot.core.Core; import org.parabot.core.Directories; import org.parabot.core.forum.AccountManager; @@ -9,6 +10,7 @@ import org.parabot.core.network.proxy.ProxySocket; import org.parabot.core.network.proxy.ProxyType; import org.parabot.core.ui.BotUI; import org.parabot.core.ui.ServerSelector; +import org.parabot.core.ui.utils.UILog; import javax.swing.*; import java.io.File; @@ -27,6 +29,16 @@ public final class Landing { public static void main(String... args) throws IOException { + if (Context.getJavaVersion() >= 9) { + UILog.log("Parabot", "Parabot doesn't support Java 9+ currently. Please downgrade to Java 8 to ensure Parabot is working correctly."); + System.exit(0); + } + + if (!System.getProperty("os.arch").contains("64")) { + UILog.log("Parabot", "You are not running a 64-bit version of Java, this might cause the client to lag or crash unexpectedly.\r\n" + + "It's recommended to upgrade to a 64-bit version."); + } + parseArgs(args); Directories.validate(); @@ -139,6 +151,9 @@ public final class Landing { case "-uuid": Core.setQuickLaunchByUuid(Integer.parseInt(args[++i])); break; + default: + System.err.println(String.format("Unknown argument given: %s", arg.toLowerCase())); + break; } } } diff --git a/src/main/java/org/parabot/core/Configuration.java b/src/main/java/org/parabot/core/Configuration.java index 6217c67..08a8ca1 100644 --- a/src/main/java/org/parabot/core/Configuration.java +++ b/src/main/java/org/parabot/core/Configuration.java @@ -11,17 +11,21 @@ public class Configuration extends org.parabot.api.Configuration { public static final String LOGIN_SERVER = "http://bdn.parabot.org/api/v2/users/login"; public static final String GET_SCRIPTS = "http://bdn.parabot.org/api/get.php?action=scripts_scripts&server="; public static final String GET_SCRIPT = "http://bdn.parabot.org/api/get.php?action=scripts_script&id="; + public static final String GET_SERVER_PROVIDER_TYPE = "http://v3.bdn.parabot.org/api/bot/server/type?server=%s"; public static final String GET_SERVER_PROVIDERS = "http://bdn.parabot.org/api/get.php?action=server_providers"; public static final String GET_SERVER_PROVIDER = "http://v3.bdn.parabot.org/api/bot/download/provider?nightly=%s&server=%s"; + public static final String SERVER_PROVIDER_INFO = "http://v3.bdn.parabot.org/api/bot/list/%s?latest=true"; public static final String GET_SERVER_PROVIDER_INFO = "http://bdn.parabot.org/api/get.php?action=server_information&name="; public static final String GET_SERVER_SETTINGS = "http://bdn.parabot.org/api/get.php?action=get_settings"; public static final String GET_BOT_VERSION = "http://bdn.parabot.org/api/v2/bot/version"; public static final String API_DOWNLOAD_BOT = "http://v3.bdn.parabot.org/api/bot/download/client"; public static final String DOWNLOAD_BOT = "http://bdn.parabot.org/versions/"; - public static final String REGISTRATION_PAGE = "https://www.parabot.org/community/register/"; public static final String GET_RANDOMS = "http://v3.bdn.parabot.org/api/bot/download/randoms"; public static final String DATA_API = "http://bdn.parabot.org/api/v2/data/"; public static final String ITEM_API = DATA_API + "items/"; - public static final Version BOT_VERSION = ProjectProperties.getProjectVersion(); + public static final Version BOT_VERSION = ProjectProperties.getProjectVersion(); + + public static final String COMMUNITY_PAGE = "https://www.parabot.org/community/"; + public static final String REGISTRATION_PAGE = COMMUNITY_PAGE + "register/"; } diff --git a/src/main/java/org/parabot/core/Context.java b/src/main/java/org/parabot/core/Context.java index 0b2f0fa..cc0420d 100644 --- a/src/main/java/org/parabot/core/Context.java +++ b/src/main/java/org/parabot/core/Context.java @@ -70,6 +70,13 @@ public class Context { this.defaultErr = System.err; } + public static double getJavaVersion() { + String version = System.getProperty("java.version"); + int pos = version.indexOf('.'); + pos = version.indexOf('.', pos + 1); + return Double.parseDouble(version.substring(0, pos)); + } + /** * Returns the instance of this class, based on a given ServerProvider * diff --git a/src/main/java/org/parabot/core/asm/hooks/HookFile.java b/src/main/java/org/parabot/core/asm/hooks/HookFile.java index 05fa6f6..5aabd19 100644 --- a/src/main/java/org/parabot/core/asm/hooks/HookFile.java +++ b/src/main/java/org/parabot/core/asm/hooks/HookFile.java @@ -1,24 +1,29 @@ package org.parabot.core.asm.hooks; +import org.parabot.core.forum.AccountManager; import org.parabot.core.parsers.hooks.HookParser; import org.parabot.core.parsers.hooks.JSONHookParser; import org.parabot.core.parsers.hooks.XMLHookParser; import org.parabot.environment.api.utils.WebUtil; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; public class HookFile { - public static final int TYPE_XML = 0; + public static final int TYPE_XML = 0; public static final int TYPE_JSON = 1; private URL url; private int type; + private boolean isLocal; + public HookFile(File file, int type) throws MalformedURLException { this(file.toURI().toURL(), type); + this.isLocal = true; } public HookFile(URL url, int type) { @@ -26,17 +31,22 @@ public class HookFile { this.url = url; } - private void setType(int type) { - if (type < 0 || type > 1) { - throw new IllegalArgumentException("This type does not exist"); - } - this.type = type; - } - public InputStream getInputStream() { return WebUtil.getInputStream(url); } + public InputStream getInputStream(AccountManager manager) { + if (isLocal) { + return this.getInputStream(); + } else { + try { + return WebUtil.getConnection(url, "apikey=" + manager.getAccount().getApi()).getInputStream(); + } catch (IOException e) { + return null; + } + } + } + public HookParser getParser() { switch (type) { case TYPE_XML: @@ -47,4 +57,11 @@ public class HookFile { return null; } + private void setType(int type) { + if (type < 0 || type > 1) { + throw new IllegalArgumentException("This type does not exist"); + } + this.type = type; + } + } 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 0925aeb..c16c903 100644 --- a/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java +++ b/src/main/java/org/parabot/core/asm/redirect/ClassRedirect.java @@ -7,6 +7,7 @@ import org.parabot.environment.scripts.Script; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.*; +import java.net.URL; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.ProtectionDomain; @@ -58,6 +59,15 @@ public class ClassRedirect { return Class.forName(name); } + public static URL getResource(Class c, String path) { + if(validStack() || validRequest(c)) { + return c.getResource(path); + } + + System.err.println(c.getName() + "#getResource(" + path + ") Blocked."); + throw RedirectClassAdapter.createSecurityException(); + } + public static ClassLoader getClassLoader(Class c) { System.err.println(c.getName() + "#getClassLoader()" + " Blocked."); throw RedirectClassAdapter.createSecurityException(); diff --git a/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java b/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java index f5074c2..502ba88 100644 --- a/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java +++ b/src/main/java/org/parabot/core/asm/redirect/RuntimeRedirect.java @@ -31,7 +31,7 @@ public class RuntimeRedirect { if (s.contains("ping")) { System.out.println("Faked attempted command: " + s); try { - return r.exec("ping 127.0.0.1"); + return r.exec("ping 8.8.8.8"); } catch (IOException e) { throw RedirectClassAdapter.createSecurityException(); } diff --git a/src/main/java/org/parabot/core/asm/redirect/ThreadRedirect.java b/src/main/java/org/parabot/core/asm/redirect/ThreadRedirect.java index c40d4d2..a6a58a3 100644 --- a/src/main/java/org/parabot/core/asm/redirect/ThreadRedirect.java +++ b/src/main/java/org/parabot/core/asm/redirect/ThreadRedirect.java @@ -61,4 +61,12 @@ public class ThreadRedirect { public static void setUncaughtExceptionHandler(Thread t, Thread.UncaughtExceptionHandler handler) { t.setUncaughtExceptionHandler(handler); } + + public static boolean isInterrupted(Thread thread) { + return thread.isInterrupted(); + } + + public static long getId(Thread thread) { + return thread.getId(); + } } 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 5917f0f..5673d28 100644 --- a/src/main/java/org/parabot/core/asm/wrappers/Callback.java +++ b/src/main/java/org/parabot/core/asm/wrappers/Callback.java @@ -49,4 +49,19 @@ public class Callback implements Injectable { this.invokeMethod, this.desc, this.args, this.conditional); } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append("Injectable type: Callback"); + + if(method != null) { + sb.append(", intercepts method: ").append(method.name); + } + + sb.append(", calls class: ").append(invokeClass) + .append(", calls method: ").append(invokeMethod); + + return sb.toString(); + } } diff --git a/src/main/java/org/parabot/core/asm/wrappers/Getter.java b/src/main/java/org/parabot/core/asm/wrappers/Getter.java index f11d12f..e7ef7a7 100644 --- a/src/main/java/org/parabot/core/asm/wrappers/Getter.java +++ b/src/main/java/org/parabot/core/asm/wrappers/Getter.java @@ -38,7 +38,7 @@ public class Getter implements Injectable { this.fieldLocation = ASMUtils.getClass(fieldLocation); this.fieldNode = ASMUtils.getField(ASMUtils.getClass(fieldLocation), fieldNode, fieldDesc); this.methodName = methodName; - this.returnDesc = returnDesc == null ? this.fieldNode.desc : returnDesc; + this.returnDesc = (returnDesc == null && this.fieldNode != null) ? this.fieldNode.desc : returnDesc; this.staticMethod = staticMethod; this.multiplier = multiplier; Core.verbose(methodName + "[" + fieldLocation + "." + fieldNode + "]"); @@ -77,4 +77,21 @@ public class Getter implements Injectable { return new AddGetterAdapter(into, fieldLocation, fieldNode, methodName, returnDesc, staticMethod, multiplier); } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Injectable type: Getter"); + + if(fieldLocation.interfaces.size() > 0) { + sb.append(", accessor type: ").append(fieldLocation.interfaces.get(0).toString().replace('/', '.')); + } + + if(fieldNode != null) { + sb.append(", field: ").append(fieldNode.name); + } + + sb.append(", method name: ").append(methodName); + + return sb.toString(); + } } diff --git a/src/main/java/org/parabot/core/asm/wrappers/Invoker.java b/src/main/java/org/parabot/core/asm/wrappers/Invoker.java index 328e073..a8bc7d8 100644 --- a/src/main/java/org/parabot/core/asm/wrappers/Invoker.java +++ b/src/main/java/org/parabot/core/asm/wrappers/Invoker.java @@ -79,4 +79,8 @@ public class Invoker implements Injectable { this.argsDesc, this.returnDesc, this.methodName, this.isInterface, this.instanceCast, this.argsCheckCastDesc); } + @Override + public String toString() { + return String.format("Injectable type: Invoker, accessor: %s, method name: %s, invokes method: %s", methodLocation.name, methodName, mName); + } } diff --git a/src/main/java/org/parabot/core/asm/wrappers/Setter.java b/src/main/java/org/parabot/core/asm/wrappers/Setter.java index 99b4074..94ce25d 100644 --- a/src/main/java/org/parabot/core/asm/wrappers/Setter.java +++ b/src/main/java/org/parabot/core/asm/wrappers/Setter.java @@ -25,7 +25,7 @@ public class Setter implements Injectable { this.into = ASMUtils.getClass(into); this.field = ASMUtils.getField(this.fieldLocation, fieldName, fieldDesc); this.name = methodName; - this.desc = (desc == null) ? this.field.desc : desc; + this.desc = (desc == null && this.field != null) ? this.field.desc : desc; this.methodStatic = methodStatic; } @@ -52,4 +52,21 @@ public class Setter implements Injectable { return new AddSetterAdapter(fieldLocation, into, field, name, desc, methodStatic); } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Injectable type: Setter"); + + if(fieldLocation.interfaces.size() > 0) { + sb.append(", accessor type: ").append(fieldLocation.interfaces.get(0).toString().replace('/', '.')); + } + + if(field != null) { + sb.append(", field: ").append(field.name); + } + + sb.append(", method name: ").append(name); + + return sb.toString(); + } } diff --git a/src/main/java/org/parabot/core/desc/ServerProviderInfo.java b/src/main/java/org/parabot/core/desc/ServerProviderInfo.java index a4e8c98..b5e84ae 100644 --- a/src/main/java/org/parabot/core/desc/ServerProviderInfo.java +++ b/src/main/java/org/parabot/core/desc/ServerProviderInfo.java @@ -166,6 +166,7 @@ public class ServerProviderInfo { /** * 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() { @@ -182,4 +183,19 @@ public class ServerProviderInfo { // Will never return null, unless the BDN URL is changed. It shouldn't be. return null; } + + /** + * Gets the current provider version + * + * @return provider version + */ + public String getProviderVersion() { + String providerType = WebUtil.getJsonValue(String.format(Configuration.GET_SERVER_PROVIDER_TYPE , properties.getProperty("name")), "type"); + if(providerType != null) { + String providerInfo = String.format(Configuration.SERVER_PROVIDER_INFO, providerType); + return WebUtil.getJsonValue(providerInfo, "version"); + } + + return null; + } } diff --git a/src/main/java/org/parabot/core/forum/AccountManager.java b/src/main/java/org/parabot/core/forum/AccountManager.java index 6b729a5..bdd21f3 100644 --- a/src/main/java/org/parabot/core/forum/AccountManager.java +++ b/src/main/java/org/parabot/core/forum/AccountManager.java @@ -4,6 +4,7 @@ import org.json.simple.JSONObject; import org.parabot.core.Configuration; import org.parabot.core.Context; import org.parabot.core.Core; +import org.parabot.core.parsers.hooks.HookParser; import org.parabot.core.parsers.scripts.BDNScripts; import org.parabot.core.parsers.servers.PublicServers; import org.parabot.core.ui.components.VerboseLoader; @@ -48,6 +49,7 @@ public final class AccountManager { accessors.add(PublicServers.MANAGER_FETCHER); accessors.add(PublicServerExecuter.MANAGER_FETCHER); accessors.add(PBPreferences.MANAGER_FETCHER); + accessors.add(HookParser.MANAGER_FETCHER); for (final AccountManagerAccess accessor : accessors) { accessor.setManager(instance); diff --git a/src/main/java/org/parabot/core/network/proxy/ProxySocket.java b/src/main/java/org/parabot/core/network/proxy/ProxySocket.java index 46b7454..105c329 100644 --- a/src/main/java/org/parabot/core/network/proxy/ProxySocket.java +++ b/src/main/java/org/parabot/core/network/proxy/ProxySocket.java @@ -1,5 +1,6 @@ package org.parabot.core.network.proxy; +import org.parabot.core.Core; import org.parabot.core.ui.utils.UILog; import javax.swing.*; @@ -44,7 +45,7 @@ public class ProxySocket extends Socket { socket.close(); value++; } catch (Exception e) { - + Core.verbose("Error closing proxy connection: " + e.getMessage()); } } return value; diff --git a/src/main/java/org/parabot/core/parsers/hooks/HookParser.java b/src/main/java/org/parabot/core/parsers/hooks/HookParser.java index 03d8292..6a307f1 100644 --- a/src/main/java/org/parabot/core/parsers/hooks/HookParser.java +++ b/src/main/java/org/parabot/core/parsers/hooks/HookParser.java @@ -3,25 +3,39 @@ package org.parabot.core.parsers.hooks; import org.parabot.core.asm.hooks.HookFile; import org.parabot.core.asm.interfaces.Injectable; import org.parabot.core.asm.wrappers.*; +import org.parabot.core.forum.AccountManager; +import org.parabot.core.forum.AccountManagerAccess; +import org.parabot.environment.api.utils.PBPreferences; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Map; /** - * Parses an XML files which injects the hooks and other bytecode manipulation + * Parses a structured format file which injects the hooks and other bytecode manipulation * methods * - * @author Everel + * @author Everel, JKetelaar */ public abstract class HookParser { + protected static AccountManager manager; + public static final AccountManagerAccess MANAGER_FETCHER = new AccountManagerAccess() { + @Override + public final void setManager(AccountManager manager) { + HookParser.manager = manager; + } + }; + public HookParser(HookFile hookFile) { } public abstract Interface[] getInterfaces(); + public abstract Map getInterfaceMap(); + public abstract Super[] getSupers(); public abstract Getter[] getGetters(); diff --git a/src/main/java/org/parabot/core/parsers/hooks/JSONHookParser.java b/src/main/java/org/parabot/core/parsers/hooks/JSONHookParser.java index 2a230a0..30852fd 100644 --- a/src/main/java/org/parabot/core/parsers/hooks/JSONHookParser.java +++ b/src/main/java/org/parabot/core/parsers/hooks/JSONHookParser.java @@ -12,7 +12,10 @@ import java.util.HashMap; import java.util.Map; /** - * @author Dane + * Parses a JSON file which injects the hooks and other bytecode manipulation + * methods + * + * @author Dane, JKetelaar */ public class JSONHookParser extends HookParser { private JSONObject root; @@ -71,6 +74,11 @@ public class JSONHookParser extends HookParser { return null; } + @Override + public Map getInterfaceMap() { + return this.interfaces; + } + @Override public Super[] getSupers() { JSONArray a = (JSONArray) root.get("supers"); 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 e7fa473..c793c78 100644 --- a/src/main/java/org/parabot/core/parsers/hooks/XMLHookParser.java +++ b/src/main/java/org/parabot/core/parsers/hooks/XMLHookParser.java @@ -13,22 +13,29 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; +/** + * Parses an XML file which injects the hooks and other bytecode manipulation + * methods + * + * @author JKetelaar + */ public class XMLHookParser extends HookParser { - private Document doc; + private Document doc; private HashMap interfaceMap; private HashMap constants; - private boolean parsedInterfaces; + private boolean parsedInterfaces; public XMLHookParser(HookFile hookFile) { super(hookFile); - interfaceMap = new HashMap(); - constants = new HashMap(); + interfaceMap = new HashMap<>(); + constants = new HashMap<>(); try { DocumentBuilderFactory dbFactory = DocumentBuilderFactory .newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); - doc = dBuilder.parse(hookFile.getInputStream()); + doc = dBuilder.parse(hookFile.getInputStream(manager)); doc.getDocumentElement().normalize(); if (!doc.getDocumentElement().getNodeName().equals("injector")) { throw new RuntimeException("Incorrect hook file."); @@ -38,48 +45,6 @@ public class XMLHookParser extends HookParser { } } - private static String resolveDesc(String returnDesc) { - String array = ""; - if (returnDesc != null && returnDesc.contains("%s")) { - StringBuilder str = new StringBuilder(); - if (returnDesc.startsWith("[")) { - for (int i = 0; i < returnDesc.length(); i++) { - if (returnDesc.charAt(i) == '[') { - array += '['; - } - } - returnDesc = returnDesc.replaceAll("\\[", ""); - } - str.append(array) - .append('L') - .append(String.format(returnDesc, - AddInterfaceAdapter.getAccessorPackage())) - .append(";"); - returnDesc = str.toString(); - } - return returnDesc; - } - - private static final boolean isSet(String tag, Element element) { - return element.getElementsByTagName(tag).getLength() > 0; - } - - 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(); - } - @Override public Interface[] getInterfaces() { parsedInterfaces = true; @@ -98,8 +63,8 @@ public class XMLHookParser extends HookParser { if (node.getNodeType() != Node.ELEMENT_NODE) { return null; } - final Element interfaceRoot = (Element) node; - final NodeList interfaces = interfaceRoot.getElementsByTagName("add"); + final Element interfaceRoot = (Element) node; + final NodeList interfaces = interfaceRoot.getElementsByTagName("add"); if (interfaces.getLength() == 0) { return null; } @@ -109,9 +74,9 @@ public class XMLHookParser extends HookParser { if (n.getNodeType() != Node.ELEMENT_NODE) { continue; } - final Element addInterface = (Element) n; - final String className = getValue("classname", addInterface); - final String interfaceClass = getValue("interface", addInterface); + final Element addInterface = (Element) n; + final String className = getValue("classname", addInterface); + final String interfaceClass = getValue("interface", addInterface); interfaceMap.put(interfaceClass, className); final Interface inf = new Interface(className, interfaceClass); interfaceList.add(inf); @@ -119,6 +84,11 @@ public class XMLHookParser extends HookParser { return interfaceList.toArray(new Interface[interfaceList.size()]); } + @Override + public Map getInterfaceMap() { + return this.interfaceMap; + } + @Override public Super[] getSupers() { final NodeList interfaceRootList = doc.getElementsByTagName("supers"); @@ -135,8 +105,8 @@ public class XMLHookParser extends HookParser { if (node.getNodeType() != Node.ELEMENT_NODE) { return null; } - final Element superRoot = (Element) node; - final NodeList supers = superRoot.getElementsByTagName("add"); + final Element superRoot = (Element) node; + final NodeList supers = superRoot.getElementsByTagName("add"); if (supers.getLength() == 0) { return null; } @@ -146,10 +116,10 @@ public class XMLHookParser extends HookParser { if (n.getNodeType() != Node.ELEMENT_NODE) { continue; } - final Element addSuper = (Element) n; - final String className = getValue("classname", addSuper); - final String superClass = getValue("super", addSuper); - final Super sup = new Super(className, superClass); + final Element addSuper = (Element) n; + final String className = getValue("classname", addSuper); + final String superClass = getValue("super", addSuper); + final Super sup = new Super(className, superClass); superList.add(sup); } return superList.toArray(new Super[superList.size()]); @@ -171,8 +141,8 @@ public class XMLHookParser extends HookParser { if (node.getNodeType() != Node.ELEMENT_NODE) { return null; } - final Element getterRoot = (Element) node; - final NodeList getters = getterRoot.getElementsByTagName("add"); + final Element getterRoot = (Element) node; + final NodeList getters = getterRoot.getElementsByTagName("add"); if (getters.getLength() == 0) { return null; } @@ -196,9 +166,9 @@ public class XMLHookParser extends HookParser { "accessor", addGetter)); final String into = isSet("into", addGetter) ? getValue("into", addGetter) : className; - final long multiplier = isSet("multiplier", addGetter) ? Long.parseLong(getValue("multiplier", addGetter)) : 0L; - final String fieldName = getValue("field", addGetter); - final String fieldDesc = isSet("descfield", addGetter) ? getValue("descfield", addGetter) : null; + final long multiplier = isSet("multiplier", addGetter) ? Long.parseLong(getValue("multiplier", addGetter)) : 0L; + final String fieldName = getValue("field", addGetter); + final String fieldDesc = isSet("descfield", addGetter) ? getValue("descfield", addGetter) : null; final String methodName = getValue("methodname", addGetter); boolean staticMethod = isSet("methstatic", addGetter) ? (getValue( "methstatic", addGetter).equals("true")) : false; @@ -246,8 +216,8 @@ public class XMLHookParser extends HookParser { if (node.getNodeType() != Node.ELEMENT_NODE) { return null; } - final Element setterRoot = (Element) node; - final NodeList setters = setterRoot.getElementsByTagName("add"); + final Element setterRoot = (Element) node; + final NodeList setters = setterRoot.getElementsByTagName("add"); if (setters.getLength() == 0) { return null; } @@ -271,8 +241,8 @@ public class XMLHookParser extends HookParser { "accessor", addSetter)); final String into = isSet("into", addSetter) ? getValue("into", addSetter) : className; - final String fieldName = getValue("field", addSetter); - final String fieldDesc = isSet("descfield", addSetter) ? getValue("descfield", addSetter) : null; + final String fieldName = getValue("field", addSetter); + final String fieldDesc = isSet("descfield", addSetter) ? getValue("descfield", addSetter) : null; final String methodName = getValue("methodname", addSetter); boolean staticMethod = isSet("methstatic", addSetter) ? (getValue( "methstatic", addSetter).equals("true")) : false; @@ -319,8 +289,8 @@ public class XMLHookParser extends HookParser { if (node.getNodeType() != Node.ELEMENT_NODE) { return null; } - final Element invokerRoot = (Element) node; - final NodeList invokers = invokerRoot.getElementsByTagName("add"); + final Element invokerRoot = (Element) node; + final NodeList invokers = invokerRoot.getElementsByTagName("add"); if (invokers.getLength() == 0) { return null; } @@ -344,15 +314,15 @@ public class XMLHookParser extends HookParser { "accessor", addInvoker)); final String into = isSet("into", addInvoker) ? getValue("into", addInvoker) : className; - final String methodName = getValue("methodname", addInvoker); + final String methodName = getValue("methodname", addInvoker); final String invMethodName = getValue("invokemethod", addInvoker); - final String argsDesc = getValue("argsdesc", addInvoker); + final String argsDesc = getValue("argsdesc", addInvoker); String returnDesc = isSet("desc", addInvoker) ? resolveDesc(getValue( "desc", addInvoker)) : null; - final boolean isInterface = isSet("interface", addInvoker) ? Boolean.parseBoolean(getValue("interface", addInvoker)) : false; - final String instanceCast = isSet("instancecast", addInvoker) ? getValue("instancecast", addInvoker) : null; - final String checkCastArgsDesc = isSet("castargs", addInvoker) ? getValue("castargs", addInvoker) : null; + final boolean isInterface = isSet("interface", addInvoker) ? Boolean.parseBoolean(getValue("interface", addInvoker)) : false; + final String instanceCast = isSet("instancecast", addInvoker) ? getValue("instancecast", addInvoker) : null; + final String checkCastArgsDesc = isSet("castargs", addInvoker) ? getValue("castargs", addInvoker) : null; final Invoker invoker = new Invoker(into, className, invMethodName, argsDesc, returnDesc, methodName, isInterface, instanceCast, checkCastArgsDesc); @@ -381,7 +351,7 @@ public class XMLHookParser extends HookParser { if (node.getNodeType() != Node.ELEMENT_NODE) { return null; } - final Element constantRoot = (Element) node; + final Element constantRoot = (Element) node; final NodeList constantsList = constantRoot.getElementsByTagName("add"); if (constantsList.getLength() == 0) { // return empty hashmap @@ -393,8 +363,8 @@ public class XMLHookParser extends HookParser { continue; } final Element addConstant = (Element) n; - final String key = getValue("key", addConstant); - final String value = getValue("value", addConstant); + final String key = getValue("key", addConstant); + final String value = getValue("value", addConstant); constants.put(key, value); } return constants; @@ -416,8 +386,8 @@ public class XMLHookParser extends HookParser { if (node.getNodeType() != Node.ELEMENT_NODE) { return null; } - final Element callbackRoot = (Element) node; - final NodeList callbacks = callbackRoot.getElementsByTagName("add"); + final Element callbackRoot = (Element) node; + final NodeList callbacks = callbackRoot.getElementsByTagName("add"); if (callbacks.getLength() == 0) { return null; } @@ -441,12 +411,12 @@ public class XMLHookParser extends HookParser { "classname", addCallback) : interfaceMap.get(getValue( "accessor", addCallback)); - final String methodName = getValue("methodname", addCallback); - final String callClass = getValue("callclass", addCallback); - final String callMethod = getValue("callmethod", addCallback); - final String callDesc = getValue("calldesc", addCallback); - final String callArgs = getValue("callargs", addCallback); - final String desc = getValue("desc", addCallback); + final String methodName = getValue("methodname", addCallback); + final String callClass = getValue("callclass", addCallback); + final String callMethod = getValue("callmethod", addCallback); + final String callDesc = getValue("calldesc", addCallback); + final String callArgs = getValue("callargs", addCallback); + final String desc = getValue("desc", addCallback); final boolean conditional = isSet("conditional", addCallback); final Callback callback = new Callback(className, methodName, desc, @@ -456,4 +426,46 @@ public class XMLHookParser extends HookParser { return callbackList.toArray(new Callback[callbackList.size()]); } + private static String resolveDesc(String returnDesc) { + String array = ""; + if (returnDesc != null && returnDesc.contains("%s")) { + StringBuilder str = new StringBuilder(); + if (returnDesc.startsWith("[")) { + for (int i = 0; i < returnDesc.length(); i++) { + if (returnDesc.charAt(i) == '[') { + array += '['; + } + } + returnDesc = returnDesc.replaceAll("\\[", ""); + } + str.append(array) + .append('L') + .append(String.format(returnDesc, + AddInterfaceAdapter.getAccessorPackage())) + .append(";"); + returnDesc = str.toString(); + } + return returnDesc; + } + + private static final boolean isSet(String tag, Element element) { + return element.getElementsByTagName(tag).getLength() > 0; + } + + 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/ui/BotUI.java b/src/main/java/org/parabot/core/ui/BotUI.java index 90b384f..4765394 100644 --- a/src/main/java/org/parabot/core/ui/BotUI.java +++ b/src/main/java/org/parabot/core/ui/BotUI.java @@ -1,6 +1,5 @@ package org.parabot.core.ui; -import javafx.application.Application; import org.parabot.core.Configuration; import org.parabot.core.Context; import org.parabot.core.Directories; @@ -248,7 +247,7 @@ public class BotUI extends JFrame implements ActionListener, ComponentListener, Directories.clearCache(); break; case "Notifications": - Application.launch(NotificationUI.class); + NotificationUI.create(); break; default: System.out.println("Invalid command: " + command); diff --git a/src/main/java/org/parabot/core/ui/components/notifications/NotificationUI.java b/src/main/java/org/parabot/core/ui/components/notifications/NotificationUI.java index bc55311..72b8a34 100644 --- a/src/main/java/org/parabot/core/ui/components/notifications/NotificationUI.java +++ b/src/main/java/org/parabot/core/ui/components/notifications/NotificationUI.java @@ -1,20 +1,56 @@ package org.parabot.core.ui.components.notifications; -import javafx.application.Application; -import javafx.fxml.FXMLLoader; -import javafx.scene.Scene; -import javafx.scene.layout.BorderPane; -import javafx.stage.Stage; -import org.parabot.core.Configuration; +import org.parabot.api.Configuration; +import org.parabot.api.output.Verboser; +import org.parabot.api.ui.JavaFxUtil; -public class NotificationUI extends Application { +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; + +/** + * A JavaFX Panel embedded into a Swing JFrame - handles notification settings + * + * @author Shadowrs + */ +public class NotificationUI extends JavaFxUtil { + + final NotificationUI n; + + private NotificationUI() throws URISyntaxException, MalformedURLException { + super(Configuration.class.getClass().getResource("/storage/ui/notifications.fxml").toURI().toURL(), NotificationUIController.class); + this.n = this; + } + + public static void create() { + try { + new NotificationUI(); + } catch (URISyntaxException | MalformedURLException e) { + e.printStackTrace(); + } + } @Override - public void start(Stage stage) throws Exception { - //noinspection RedundantCast - BorderPane root = (BorderPane) FXMLLoader.load(this.getClass().getResource("/storage/ui/notifications.fxml")); - stage.setTitle(Configuration.BOT_TITLE); - stage.setScene(new Scene(root)); - stage.show(); + public WindowAdapter getWindowAdapter() { + return new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + Verboser.verbose("NotificationUI closed " + e); + } + + @Override + public void windowClosing(WindowEvent e) { + // Anything here. JFrame hides on exit. + Verboser.verbose("NotificationUI closing " + e); + n.getFrame().dispose(); + } + }; + } + + @Override + protected void onLaunched() { + n.getFrame().setTitle("Notifications"); } } \ No newline at end of file diff --git a/src/main/java/org/parabot/core/ui/utils/UILog.java b/src/main/java/org/parabot/core/ui/utils/UILog.java index ff00260..d1261f6 100644 --- a/src/main/java/org/parabot/core/ui/utils/UILog.java +++ b/src/main/java/org/parabot/core/ui/utils/UILog.java @@ -30,4 +30,13 @@ public class UILog { public static int alert(final String title, final String message, int optionType, int messageType) { return JOptionPane.showConfirmDialog(null, message, title, optionType, messageType); } + + public static int alert(final String title, final String message, Object[] options) { + return alert(title, message, options, JOptionPane.INFORMATION_MESSAGE); + } + + public static int alert(final String title, final String message, Object[] options, int messageType) { + return JOptionPane.showOptionDialog(null, message, title, + JOptionPane.YES_NO_CANCEL_OPTION, messageType, null, options, null); + } } diff --git a/src/main/java/org/parabot/environment/Environment.java b/src/main/java/org/parabot/environment/Environment.java index 88cf093..0e05b0d 100644 --- a/src/main/java/org/parabot/environment/Environment.java +++ b/src/main/java/org/parabot/environment/Environment.java @@ -33,7 +33,7 @@ public class Environment extends org.parabot.api.io.libraries.Environment { loadLibrary(lib, true); } - Core.verbose("Loading server: " + desc.toString() + "..."); + Core.verbose("[Environment] Loading server: " + desc.toString() + "..."); ServerParser.SERVER_CACHE.get(desc).run(); } diff --git a/src/main/java/org/parabot/environment/api/utils/FileUtil.java b/src/main/java/org/parabot/environment/api/utils/FileUtil.java index ce954d3..a7e2fd5 100644 --- a/src/main/java/org/parabot/environment/api/utils/FileUtil.java +++ b/src/main/java/org/parabot/environment/api/utils/FileUtil.java @@ -1,10 +1,8 @@ package org.parabot.environment.api.utils; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.nio.channels.FileChannel; +import java.nio.file.Files; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -88,4 +86,29 @@ public class FileUtil { } destination.close(); } + + /** + * Reads the contents of a text file + * + * @param file file to get contents from + * @return file contents + * @throws IOException when anything goes wrong + */ + public static String getFileContents(File file) throws IOException { + return new String(Files.readAllBytes(file.toPath())); + } + + /** + * Writes a string to a file overwriting the existing contents if present + * + * @param file file to write to + * @param contents contents to write to given file + * @throws IOException when anything goes wrong + */ + public static void writeFileContents(File file, String contents) throws IOException { + BufferedWriter writer = new BufferedWriter(new FileWriter(file.getAbsolutePath())); + writer.write(contents); + + writer.close(); + } } diff --git a/src/main/java/org/parabot/environment/api/utils/PBLocalPreferences.java b/src/main/java/org/parabot/environment/api/utils/PBLocalPreferences.java new file mode 100644 index 0000000..74eb1d7 --- /dev/null +++ b/src/main/java/org/parabot/environment/api/utils/PBLocalPreferences.java @@ -0,0 +1,118 @@ +package org.parabot.environment.api.utils; + +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.parabot.core.Directories; + +import java.io.File; +import java.util.HashMap; + +/** + * Manages preferences in a local file in JSON format in the Parabot settings folder + * + * @author AlexanderBielen + */ +public class PBLocalPreferences { + private static JSONParser parser = new JSONParser(); + private File settingsFile; + + public PBLocalPreferences(String fileName) { + settingsFile = new File(Directories.getSettingsPath() + "/" + secureFileName(fileName)); + } + + /** + * Gets all settings inside the file + * + * @return JSONObject or null if anything went wrong + */ + public JSONObject getSettings() { + try { + String stringContents = FileUtil.getFileContents(settingsFile); + return (JSONObject) parser.parse(stringContents); + } catch(Exception ex) { + return null; + } + } + + /** + * Convert a HashMap to json and writes it to the file + * + * @param settings HashMap + * @param append If true, append to existing settings in file + */ + public void writeSettings(HashMap settings, boolean append) { + JSONObject existingSettings; + if(append && (existingSettings = getSettings()) != null) { + existingSettings.putAll(settings); + settings = existingSettings; + } + + try { + if (!settingsFile.exists()) { + settingsFile.createNewFile(); + } + FileUtil.writeFileContents(settingsFile, new JSONObject(settings).toJSONString()); + + } catch (Exception ignore) { + ignore.printStackTrace(); + } + + } + + /** + * Adds a setting, or overwrites it if it exists + * + * @param key key of the setting + * @param value value of the setting + */ + public void addSetting(String key, String value) { + HashMap pair = new HashMap<>(); + pair.put(key, value); + writeSettings(pair, true); + } + + /** + * Fetches a setting + * + * @param key key to get the value for + * @return value that belongs to given key or null if non-existent + */ + public String getSetting(String key) { + if(getSettings() == null) { + return null; + } + + return getSettings().get(key).toString(); + } + + /** + * Adjusts an existing setting + * + * @param key key to adjust the value for + * @param value value for the key + */ + public void adjustSetting(String key, String value) { + addSetting(key, value); + } + + /** + * Removes a setting + * + * @param key key to remove + */ + public void removeSetting(String key) { + JSONObject json = getSettings(); + json.remove(key); + writeSettings(json, false); + } + + /** + * Replaces all double dots to make sure the link does not leave the settings folder + * + * @param filePath path to secure + * @return secured string + */ + private static String secureFileName(String filePath) { + return filePath.replace("..", ""); + } +} diff --git a/src/main/java/org/parabot/environment/api/utils/Time.java b/src/main/java/org/parabot/environment/api/utils/Time.java index 5418f42..c790ed0 100644 --- a/src/main/java/org/parabot/environment/api/utils/Time.java +++ b/src/main/java/org/parabot/environment/api/utils/Time.java @@ -53,6 +53,30 @@ public final class Time { return true; } + /** + * Sleeps until SleepCondition is valid, but with a minimum timeout. + * + * @param conn the condition. + * @param timeout the time in milliseconds before it stops sleeping. + * @param minimumTimeout the minimum time to sleep. + * + * @return whether it ran successfully without timing out. + */ + public static boolean sleep(SleepCondition conn, int timeout, int minimumTimeout) { + long start = System.currentTimeMillis(); + + if(!sleep(conn, timeout)) { + return false; + } + + long t; + if((t = System.currentTimeMillis() - start) < minimumTimeout) { + Time.sleep((int)(minimumTimeout - t)); + } + + return true; + } + /** * Gets current time in milliseconds * diff --git a/src/main/java/org/parabot/environment/api/utils/Timer.java b/src/main/java/org/parabot/environment/api/utils/Timer.java index fa3c07e..5a457cf 100644 --- a/src/main/java/org/parabot/environment/api/utils/Timer.java +++ b/src/main/java/org/parabot/environment/api/utils/Timer.java @@ -105,7 +105,18 @@ public class Timer { * @return hourly gains */ public int getPerHour(final int gained) { - return (int) ((gained) * 3600000D / (System.currentTimeMillis() - start)); + return getPerHour(gained, 0); + } + + /** + * Calculates hourly gains based on given variable, with variable start amount + * + * @param gained total gained amount + * @param startAmount start amount + * @return hourly gains + */ + public int getPerHour(final int gained, final int startAmount) { + return (int) (((gained - startAmount) * 3600000D) / (System.currentTimeMillis() - start)); } /** diff --git a/src/main/java/org/parabot/environment/api/utils/WebUtil.java b/src/main/java/org/parabot/environment/api/utils/WebUtil.java index 14c175b..8f1532d 100644 --- a/src/main/java/org/parabot/environment/api/utils/WebUtil.java +++ b/src/main/java/org/parabot/environment/api/utils/WebUtil.java @@ -1,5 +1,7 @@ package org.parabot.environment.api.utils; +import org.json.simple.JSONObject; + /** * A WebUtil class fetches data from an URL * @@ -7,4 +9,27 @@ package org.parabot.environment.api.utils; */ public class WebUtil extends org.parabot.api.io.WebUtil { + /** + * Fetches a single value from a JSON string at the given url + * + * @param url url to get the JSON string from + * @param key key to search for in the JSON string + * @return value that belongs to given key + */ + public static String getJsonValue(String url, String key) { + try { + String response = WebUtil.getContents(url); + + if(response.length() > 0) { + JSONObject jsonObject = (JSONObject) WebUtil.getJsonParser().parse(response); + if (jsonObject.get(key) != null) { + return jsonObject.get(key).toString(); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + + return null; + } } diff --git a/src/main/java/org/parabot/environment/servers/ServerProvider.java b/src/main/java/org/parabot/environment/servers/ServerProvider.java index 6765781..ae9b5ba 100644 --- a/src/main/java/org/parabot/environment/servers/ServerProvider.java +++ b/src/main/java/org/parabot/environment/servers/ServerProvider.java @@ -1,10 +1,12 @@ package org.parabot.environment.servers; import org.objectweb.asm.Opcodes; +import org.parabot.core.Configuration; import org.parabot.core.Context; import org.parabot.core.asm.hooks.HookFile; import org.parabot.core.asm.interfaces.Injectable; import org.parabot.core.parsers.hooks.HookParser; +import org.parabot.core.ui.utils.UILog; import org.parabot.environment.input.Keyboard; import org.parabot.environment.input.Mouse; import org.parabot.environment.scripts.Script; @@ -13,6 +15,8 @@ import javax.swing.*; import java.applet.Applet; import java.applet.AppletStub; import java.awt.*; +import java.io.IOException; +import java.net.URI; import java.net.URL; /** @@ -21,6 +25,7 @@ import java.net.URL; * @author Everel */ public abstract class ServerProvider implements Opcodes { + private boolean crashed = false; /** * Get the game/applet dimensions @@ -74,12 +79,36 @@ public abstract class ServerProvider implements Opcodes { HookParser parser = hookFile.getParser(); Injectable[] injectables = parser.getInjectables(); + if (injectables == null) { return; } - for (Injectable inj : injectables) { - inj.inject(); + + int index = 0; + + try { + for (Injectable inj : injectables) { + inj.inject(); + index++; + } + } catch (NullPointerException ex) { + if(!crashed) { + Injectable inj = injectables[index]; + + int resp = UILog.alert("Outdated client", "This server currently has outdated hooks, please report it to a member of the Parabot staff.\r\n\r\n" + + "Broken hook:\r\n"+inj, new Object[]{"Close", "Report here..."}, JOptionPane.ERROR_MESSAGE); + + if(resp == 1) { + URI uri = URI.create(Configuration.COMMUNITY_PAGE + "forum/135-reports/"); + try { + Desktop.getDesktop().browse(uri); + } catch (IOException ignore) {} + } + } + crashed = true; + throw ex; } + Context.getInstance().setHookParser(parser); } diff --git a/src/main/java/org/parabot/environment/servers/executers/PublicServerExecuter.java b/src/main/java/org/parabot/environment/servers/executers/PublicServerExecuter.java index b98bd3c..a316586 100644 --- a/src/main/java/org/parabot/environment/servers/executers/PublicServerExecuter.java +++ b/src/main/java/org/parabot/environment/servers/executers/PublicServerExecuter.java @@ -11,6 +11,7 @@ import org.parabot.core.forum.AccountManager; import org.parabot.core.forum.AccountManagerAccess; import org.parabot.core.ui.components.VerboseLoader; import org.parabot.core.ui.utils.UILog; +import org.parabot.environment.api.utils.PBLocalPreferences; import org.parabot.environment.api.utils.WebUtil; import org.parabot.environment.servers.ServerProvider; import org.parabot.environment.servers.loader.ServerLoader; @@ -37,6 +38,9 @@ public class PublicServerExecuter extends ServerExecuter { }; private String serverName; + private PBLocalPreferences settings; + private final String cacheVersionKey = "cachedProviderVersion"; + public PublicServerExecuter(final String serverName) { this.serverName = serverName; } @@ -53,6 +57,24 @@ public class PublicServerExecuter extends ServerExecuter { Core.verbose("Downloading: " + jarUrl + " ..."); + String providerVersion = serverProviderInfo.getProviderVersion(); + if(providerVersion == null) { + providerVersion = "error"; + } + + settings = new PBLocalPreferences(serverProviderInfo.getClientCRC32()+".json"); + if(settings.getSetting(cacheVersionKey) != null) { + Core.verbose(String.format("Latest provider version: %s, local provider version: %s", settings.getSetting(cacheVersionKey), providerVersion)); + if(!settings.getSetting(cacheVersionKey).equals(providerVersion)) { + Core.verbose("Local provider outdated, clearing cache."); + Directories.clearCache(); + } + } else { + Core.verbose("No local provider version in settings, adding to settings file"); + } + + settings.addSetting(cacheVersionKey, providerVersion); + if (destination.exists()) { Core.verbose("Found cached server provider [CRC32: " + serverProviderInfo.getCRC32() + "]"); } else { diff --git a/src/main/resources/storage/ui/notifications.fxml b/src/main/resources/storage/ui/notifications.fxml index 1e6ac7b..6577abd 100644 --- a/src/main/resources/storage/ui/notifications.fxml +++ b/src/main/resources/storage/ui/notifications.fxml @@ -4,8 +4,7 @@ + prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1">