diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index e70d977a6e..d655f2cc95 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -25,9 +25,9 @@ object ProjectVersions { const val launcherVersion = "2.2.0" - const val rlVersion = "1.8.15.1" + const val rlVersion = "1.8.16" - const val openosrsVersion = "4.21.0" + const val openosrsVersion = "4.22.0" const val rsversion = 204 const val cacheversion = 165 diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/ScriptLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/ScriptLoader.java index 923f781237..0762bdffff 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/ScriptLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/ScriptLoader.java @@ -24,7 +24,7 @@ */ package net.runelite.cache.definitions.loaders; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import net.runelite.cache.definitions.ScriptDefinition; import net.runelite.cache.io.InputStream; @@ -61,7 +61,7 @@ public class ScriptLoader for (int i = 0; i < numSwitches; ++i) { - switches[i] = new HashMap<>(); + switches[i] = new LinkedHashMap<>(); int count = in.readUnsignedShort(); while (count-- > 0) diff --git a/runelite-api/src/main/java/com/jagex/oldscape/pub/OAuthApi.java b/runelite-api/src/main/java/com/jagex/oldscape/pub/OAuthApi.java new file mode 100644 index 0000000000..39565602fd --- /dev/null +++ b/runelite-api/src/main/java/com/jagex/oldscape/pub/OAuthApi.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jagex.oldscape.pub; + +public interface OAuthApi +{ + boolean isOnLoginScreen(); + + void setOtlTokenRequester(OtlTokenRequester otlTokenRequester); + + /** + * Gets a unique per-RuneScape-Account identifier or {@code -1} if the client has not logged in yet + */ + long getAccountHash(); +} diff --git a/runelite-api/src/main/java/com/jagex/oldscape/pub/OtlTokenRequester.java b/runelite-api/src/main/java/com/jagex/oldscape/pub/OtlTokenRequester.java new file mode 100644 index 0000000000..6d829b4aea --- /dev/null +++ b/runelite-api/src/main/java/com/jagex/oldscape/pub/OtlTokenRequester.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2022, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jagex.oldscape.pub; + +import java.net.URL; +import java.util.concurrent.Future; + +public interface OtlTokenRequester +{ + Future request(URL url); +} diff --git a/runelite-api/src/main/java/com/jagex/oldscape/pub/OtlTokenResponse.java b/runelite-api/src/main/java/com/jagex/oldscape/pub/OtlTokenResponse.java new file mode 100644 index 0000000000..d78bd75500 --- /dev/null +++ b/runelite-api/src/main/java/com/jagex/oldscape/pub/OtlTokenResponse.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2022, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jagex.oldscape.pub; + +public interface OtlTokenResponse +{ + boolean isSuccess(); + + String getToken(); +} diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 15f1cc03a9..a982820e0b 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -24,6 +24,7 @@ */ package net.runelite.api; +import com.jagex.oldscape.pub.OAuthApi; import java.awt.Canvas; import java.awt.Dimension; import java.math.BigInteger; @@ -52,7 +53,7 @@ import org.intellij.lang.annotations.MagicConstant; /** * Represents the RuneScape client. */ -public interface Client extends GameEngine +public interface Client extends OAuthApi, GameEngine { /** * The injected client invokes these callbacks to send events to us @@ -199,10 +200,13 @@ public interface Client extends GameEngine void setWorldSelectOpen(boolean open); /** + * DEPRECATED. See getAccountHash instead. * Gets the current logged in username. * * @return the logged in username + * @see OAuthApi#getAccountHash() */ + @Deprecated String getUsername(); /** diff --git a/runelite-api/src/main/java/net/runelite/api/ParamID.java b/runelite-api/src/main/java/net/runelite/api/ParamID.java index d726067c5a..d769a715ed 100644 --- a/runelite-api/src/main/java/net/runelite/api/ParamID.java +++ b/runelite-api/src/main/java/net/runelite/api/ParamID.java @@ -33,6 +33,7 @@ public final class ParamID * Long name for NPCs used in the HP hud */ public static final int NPC_HP_NAME = 510; + public static final int QUEST_NAME = 610; /** * @see SettingID */ diff --git a/runelite-api/src/main/java/net/runelite/api/ScriptID.java b/runelite-api/src/main/java/net/runelite/api/ScriptID.java index bf63a54f59..2e3a31ebe7 100644 --- a/runelite-api/src/main/java/net/runelite/api/ScriptID.java +++ b/runelite-api/src/main/java/net/runelite/api/ScriptID.java @@ -275,6 +275,12 @@ public final class ScriptID @ScriptArguments(integer = 3) public static final int GE_ITEM_SEARCH = 752; + /** + * On load listener for building the quest list interface + */ + @ScriptArguments(integer = 8) + public static final int QUESTLIST_INIT = 1350; + /** * Called when the friends list is updated *
    @@ -428,4 +434,19 @@ public final class ScriptID */ @ScriptArguments(integer = 1) public static final int NOTIFICATION_DELAY = 3347; + + /** + * Check if a quest should be filtered from the quest list + *
      + *
    • int (StructID) Quest struct
    • + *
    • int State filter
    • + *
    • int Requirement filter
    • + *
    • int Stats filter
    • + *
    + */ + @ScriptArguments(integer = 4) + public static final int QUEST_FILTER = 3238; + + @ScriptArguments(integer = 18, string = 1) + public static final int GROUP_IRONMAN_STORAGE_BUILD = 5269; } \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/events/AccountHashChanged.java b/runelite-api/src/main/java/net/runelite/api/events/AccountHashChanged.java new file mode 100644 index 0000000000..fa2d72078a --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/AccountHashChanged.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2022 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api.events; + +public class AccountHashChanged +{ +} \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index 938be8c9d9..46241c342c 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -132,6 +132,7 @@ public final class WidgetID public static final int SKOTIZO_GROUP_ID = 308; public static final int ENTERING_HOUSE_GROUP_ID = 71; public static final int FULLSCREEN_CONTAINER_TLI = 165; + public static final int QUESTLIST_GROUP_ID = 399; public static final int SKILLS_GROUP_ID = 320; public static final int MUSIC_GROUP_ID = 239; public static final int BARROWS_PUZZLE_GROUP_ID = 25; @@ -310,6 +311,7 @@ public final class WidgetID static class GroupStorage { + static final int UI = 2; static final int ITEM_CONTAINER = 10; } @@ -889,6 +891,12 @@ public final class WidgetID static final int CONTAINER = 2; } + static class QuestList + { + static final int BOX = 0; + static final int CONTAINER = 2; + } + static class Music { static final int CONTAINER = 0; diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 7d68c7c5c3..8c239ba65a 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -153,6 +153,7 @@ public enum WidgetInfo BANK_SETTINGS_BUTTON(WidgetID.BANK_GROUP_ID, WidgetID.Bank.SETTINGS_BUTTON), BANK_TUTORIAL_BUTTON(WidgetID.BANK_GROUP_ID, WidgetID.Bank.TUTORIAL_BUTTON), + GROUP_STORAGE_UI(WidgetID.GROUP_STORAGE_GROUP_ID, WidgetID.GroupStorage.UI), GROUP_STORAGE_ITEM_CONTAINER(WidgetID.GROUP_STORAGE_GROUP_ID, WidgetID.GroupStorage.ITEM_CONTAINER), GRAND_EXCHANGE_WINDOW_CONTAINER(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.WINDOW_CONTAINER), @@ -521,6 +522,9 @@ public enum WidgetInfo SKOTIZO_CONTAINER(WidgetID.SKOTIZO_GROUP_ID, WidgetID.Skotizo.CONTAINER), + QUESTLIST_BOX(WidgetID.QUESTLIST_GROUP_ID, WidgetID.QuestList.BOX), + QUESTLIST_CONTAINER(WidgetID.QUESTLIST_GROUP_ID, WidgetID.QuestList.CONTAINER), + SEED_VAULT_TITLE_CONTAINER(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.TITLE_CONTAINER), SEED_VAULT_ITEM_CONTAINER(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_CONTAINER), SEED_VAULT_ITEM_TEXT(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_TEXT), diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index 8f4325bfd7..36697576e4 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -27,9 +27,12 @@ package net.runelite.client; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; +import com.jagex.oldscape.pub.OAuthApi; +import com.jagex.oldscape.pub.OtlTokenResponse; import com.openosrs.client.OpenOSRS; import com.openosrs.client.game.PlayerManager; import com.openosrs.client.ui.OpenOSRSSplashScreen; @@ -50,6 +53,7 @@ import java.security.SecureRandom; import java.security.cert.X509Certificate; import java.util.Locale; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.Optional; import java.util.stream.Stream; @@ -92,6 +96,8 @@ import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.worlds.World; import net.runelite.http.api.worlds.WorldResult; import okhttp3.Cache; +import okhttp3.Call; +import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; @@ -120,6 +126,9 @@ public class RuneLite @Inject private net.runelite.client.plugins.PluginManager pluginManager; + @Inject + private OkHttpClient okHttpClient; + @Inject private ExternalPluginManager externalPluginManager; @@ -290,8 +299,8 @@ public class RuneLite try { - final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), (String) options.valueOf("jav_config")); final RuntimeConfigLoader runtimeConfigLoader = new RuntimeConfigLoader(okHttpClient); + final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), runtimeConfigLoader, (String) options.valueOf("jav_config")); new Thread(() -> { @@ -330,6 +339,7 @@ public class RuneLite log.error("Failure during startup", e); SwingUtilities.invokeLater(() -> new FatalErrorDialog("OpenOSRS has encountered an unexpected error during startup.") + .addHelpButtons() .open()); } finally @@ -371,6 +381,11 @@ public class RuneLite } applet.start(); + + if (applet instanceof OAuthApi) + { + setupJxAuth((OAuthApi) applet); + } } SplashScreen.stage(.57, null, "Loading configuration"); @@ -661,4 +676,83 @@ public class RuneLite System.setProperty(key, value); } } + + private void setupJxAuth(OAuthApi oAuthApi) + { + String accessToken = System.getenv("JX_ACCESS_TOKEN"); + if (Strings.isNullOrEmpty(accessToken)) + { + return; + } + + try + { + log.info("Initializing OTL token requester with access token"); + oAuthApi.setOtlTokenRequester(url -> + { + CompletableFuture f = new CompletableFuture<>(); + okHttpClient.newCall(new Request.Builder() + .url(url) + .header("Authorization", "Bearer " + accessToken) + .get() + .build()) + .enqueue(new Callback() + { + private void complete(boolean success, String token) + { + f.complete(new OtlTokenResponse() + { + @Override + public boolean isSuccess() + { + return success; + } + + @Override + public String getToken() + { + return token; + } + }); + } + + @Override + public void onFailure(Call call, IOException e) + { + log.error("HTTP error while performing OTL request", e); + complete(false, null); + } + + @Override + public void onResponse(Call call, Response response) throws IOException + { + if (response.code() != 200) + { + log.error("Non-OK response performing OTL request: {}", response.code()); + complete(false, null); + response.close(); + return; + } + + if (response.body() == null) + { + log.error("OK response with empty body from OTL request"); + complete(false, null); + response.close(); + return; + } + + log.debug("Successful OTL response"); + complete(true, response.body().string()); + response.close(); + } + }); + return f; + }); + } + catch (LinkageError ex) + { + log.error("error setting up OTL requester", ex); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java index 2d6a01e1d8..856a916352 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -50,6 +50,7 @@ public class RuneLiteProperties private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version"; private static final String API_BASE = "runelite.api.base"; private static final String RUNELITE_CONFIG = "runelite.config"; + private static final String OSRS_TWITTER_LINK = "runelite.osrstwitter.link"; @Getter(AccessLevel.PACKAGE) private static final Properties properties = new Properties(); @@ -148,4 +149,9 @@ public class RuneLiteProperties { return properties.getProperty(RUNELITE_CONFIG); } + + public static String getOSRSTwitterLink() + { + return properties.getProperty(OSRS_TWITTER_LINK); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java b/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java index 0d41a3573c..fc7fdb4492 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java @@ -24,13 +24,46 @@ */ package net.runelite.client; +import com.google.common.base.Strings; import java.util.Collections; import java.util.Map; +import javax.swing.SwingUtilities; import lombok.Data; +import net.runelite.client.ui.FatalErrorDialog; +import net.runelite.client.util.LinkBrowser; @Data public class RuntimeConfig { private Map props = Collections.emptyMap(); private Map sysProps = Collections.emptyMap(); + + private String outageMessage; + private Map outageLinks; + + public boolean showOutageMessage() + { + if (Strings.isNullOrEmpty(getOutageMessage())) + { + return false; + } + + SwingUtilities.invokeLater(() -> + { + FatalErrorDialog fed = new FatalErrorDialog(getOutageMessage()); + if (getOutageLinks() != null) + { + for (Map.Entry e : getOutageLinks().entrySet()) + { + fed.addButton(e.getKey(), () -> LinkBrowser.browse(e.getValue())); + } + } + else + { + fed.addButton("OSRS Twitter", () -> LinkBrowser.browse(RuneLiteProperties.getOSRSTwitterLink())); + } + fed.open(); + }); + return true; + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java b/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java index aa82fe44dc..470c1f93e7 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java @@ -31,7 +31,10 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Supplier; +import javax.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import net.runelite.http.api.RuneLiteAPI; import okhttp3.Call; @@ -41,7 +44,7 @@ import okhttp3.Request; import okhttp3.Response; @Slf4j -class RuntimeConfigLoader implements Supplier +public class RuntimeConfigLoader implements Supplier { private final OkHttpClient okHttpClient; private final CompletableFuture configFuture; @@ -66,6 +69,19 @@ class RuntimeConfigLoader implements Supplier } } + @Nullable + public RuntimeConfig tryGet() + { + try + { + return configFuture.get(0, TimeUnit.SECONDS); + } + catch (InterruptedException | ExecutionException | TimeoutException e) + { + return null; + } + } + private CompletableFuture fetch() { CompletableFuture future = new CompletableFuture<>(); diff --git a/runelite-client/src/main/java/net/runelite/client/callback/ClientThread.java b/runelite-client/src/main/java/net/runelite/client/callback/ClientThread.java index 6c1a9e7024..2a71c478d3 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/ClientThread.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/ClientThread.java @@ -121,7 +121,7 @@ public class ClientThread implements Executor } catch (Throwable e) { - log.warn("Exception in invoke", e); + log.error("Exception in invoke", e); } if (remove) { diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index 77bacede3f..054959adea 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -88,6 +88,7 @@ import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Player; import net.runelite.api.coords.WorldPoint; +import net.runelite.api.events.AccountHashChanged; import net.runelite.api.events.PlayerChanged; import net.runelite.api.events.UsernameChanged; import net.runelite.api.events.WorldChanged; @@ -115,6 +116,7 @@ public class ConfigManager private static final String RSPROFILE_TYPE = "type"; private static final String RSPROFILE_LOGIN_HASH = "loginHash"; private static final String RSPROFILE_LOGIN_SALT = "loginSalt"; + private static final String RSPROFILE_ACCOUNT_HASH = "accountHash"; private static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); @@ -544,17 +546,17 @@ public class ConfigManager displayName = p.getName(); } - String username = client.getUsername(); - if (Strings.isNullOrEmpty(username)) + RuneScapeProfile prof = findRSProfile(getRSProfiles(), RuneScapeProfileType.getCurrent(client), displayName, true); + if (prof == null) { - log.warn("trying to create profile without a set username"); + log.warn("trying to create a profile while not logged in"); return; } - RuneScapeProfile prof = findRSProfile(getRSProfiles(), username, RuneScapeProfileType.getCurrent(client), displayName, true); rsProfileKey = prof.getKey(); this.rsProfileKey = rsProfileKey; + log.debug("RS profile changed to {}", rsProfileKey); eventBus.post(new RuneScapeProfileChanged()); } setConfiguration(groupName, rsProfileKey, key, value); @@ -790,6 +792,10 @@ public class ConfigManager { return Integer.parseInt(str); } + if (type == long.class || type == Long.class) + { + return Long.parseLong(str); + } if (type == double.class || type == Double.class) { return Double.parseDouble(str); @@ -1129,10 +1135,12 @@ public class ConfigManager return profileKeys.stream() .map(key -> { + Long accid = getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_ACCOUNT_HASH, long.class); RuneScapeProfile prof = new RuneScapeProfile( getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_DISPLAY_NAME), getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_TYPE, RuneScapeProfileType.class), getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_LOGIN_HASH, byte[].class), + accid == null ? RuneScapeProfile.ACCOUNT_HASH_INVALID : accid, key ); @@ -1141,26 +1149,54 @@ public class ConfigManager .collect(Collectors.toList()); } - private synchronized RuneScapeProfile findRSProfile(List profiles, String username, RuneScapeProfileType type, String displayName, boolean create) + private synchronized RuneScapeProfile findRSProfile(List profiles, RuneScapeProfileType type, String displayName, boolean create) { - byte[] salt = getConfiguration(RSPROFILE_GROUP, RSPROFILE_LOGIN_SALT, byte[].class); - if (salt == null) + String username = client.getUsername(); + long accountHash = client.getAccountHash(); + + if (accountHash == RuneScapeProfile.ACCOUNT_HASH_INVALID && username == null) { - salt = new byte[15]; - new SecureRandom() - .nextBytes(salt); - log.info("creating new salt as there is no existing one {}", Base64.getUrlEncoder().encodeToString(salt)); - setConfiguration(RSPROFILE_GROUP, RSPROFILE_LOGIN_SALT, salt); + return null; } - Hasher h = Hashing.sha512().newHasher(); - h.putBytes(salt); - h.putString(username.toLowerCase(Locale.US), StandardCharsets.UTF_8); - byte[] loginHash = h.hash().asBytes(); + final byte[] loginHash; + byte[] salt = null; + if (username != null) + { + salt = getConfiguration(RSPROFILE_GROUP, RSPROFILE_LOGIN_SALT, byte[].class); + if (salt == null) + { + salt = new byte[15]; + new SecureRandom() + .nextBytes(salt); + log.info("creating new salt as there is no existing one {}", Base64.getUrlEncoder().encodeToString(salt)); + setConfiguration(RSPROFILE_GROUP, RSPROFILE_LOGIN_SALT, salt); + } - Set matches = profiles.stream() - .filter(p -> Arrays.equals(p.getLoginHash(), loginHash) && p.getType() == type) + Hasher h = Hashing.sha512().newHasher(); + h.putBytes(salt); + h.putString(username.toLowerCase(Locale.US), StandardCharsets.UTF_8); + loginHash = h.hash().asBytes(); + } + else + { + loginHash = null; + } + + Set matches = Collections.emptySet(); + if (accountHash != RuneScapeProfile.ACCOUNT_HASH_INVALID) + { + matches = profiles.stream() + .filter(p -> p.getType() == type && accountHash == p.getAccountHash()) .collect(Collectors.toSet()); + } + + if (matches.isEmpty() && loginHash != null) + { + matches = profiles.stream() + .filter(p -> p.getType() == type && Arrays.equals(loginHash, p.getLoginHash())) + .collect(Collectors.toSet()); + } if (matches.size() > 1) { @@ -1169,7 +1205,21 @@ public class ConfigManager if (matches.size() >= 1) { - return matches.iterator().next(); + RuneScapeProfile profile = matches.iterator().next(); + if (profile.getAccountHash() == RuneScapeProfile.ACCOUNT_HASH_INVALID && accountHash != RuneScapeProfile.ACCOUNT_HASH_INVALID) + { + int upgrades = 0; + for (RuneScapeProfile p : profiles) + { + if (p.getAccountHash() == RuneScapeProfile.ACCOUNT_HASH_INVALID && Arrays.equals(p.getLoginHash(), loginHash)) + { + setConfiguration(RSPROFILE_GROUP, p.getKey(), RSPROFILE_ACCOUNT_HASH, accountHash); + upgrades++; + } + } + log.info("Attaching account id to {} profiles", upgrades); + } + return profile; } if (!create) @@ -1179,22 +1229,41 @@ public class ConfigManager // generate the new key deterministically so if you "create" the same profile on 2 different clients it doesn't duplicate Set keys = profiles.stream().map(RuneScapeProfile::getKey).collect(Collectors.toSet()); - byte[] key = Arrays.copyOf(loginHash, 6); + byte[] key = accountHash == RuneScapeProfile.ACCOUNT_HASH_INVALID + ? Arrays.copyOf(loginHash, 6) + : new byte[] + { + (byte) accountHash, + (byte) (accountHash >> 8), + (byte) (accountHash >> 16), + (byte) (accountHash >> 24), + (byte) (accountHash >> 32), + (byte) (accountHash >> 40), + }; key[0] += type.ordinal(); for (int i = 0; i < 0xFF; i++, key[1]++) { String keyStr = RSPROFILE_GROUP + "." + Base64.getUrlEncoder().encodeToString(key); if (!keys.contains(keyStr)) { - log.info("creating new profile {} for user {} ({}) salt {}", keyStr, username, type, Base64.getUrlEncoder().encodeToString(salt)); + log.info("creating new profile {} for username {} account hash {} ({}) salt {}", + keyStr, username, accountHash, type, + salt == null ? "null" : Base64.getUrlEncoder().encodeToString(salt)); - setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_LOGIN_HASH, loginHash); + if (loginHash != null) + { + setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_LOGIN_HASH, loginHash); + } + if (accountHash != RuneScapeProfile.ACCOUNT_HASH_INVALID) + { + setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_ACCOUNT_HASH, accountHash); + } setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_TYPE, type); if (displayName != null) { setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_DISPLAY_NAME, displayName); } - return new RuneScapeProfile(displayName, type, loginHash, keyStr); + return new RuneScapeProfile(displayName, type, loginHash, accountHash, keyStr); } } throw new RuntimeException("too many rs profiles"); @@ -1208,7 +1277,7 @@ public class ConfigManager } List profiles = getRSProfiles(); - RuneScapeProfile prof = findRSProfile(profiles, client.getUsername(), RuneScapeProfileType.getCurrent(client), null, false); + RuneScapeProfile prof = findRSProfile(profiles, RuneScapeProfileType.getCurrent(client), null, false); String key = prof == null ? null : prof.getKey(); if (Objects.equals(key, rsProfileKey)) @@ -1217,6 +1286,7 @@ public class ConfigManager } rsProfileKey = key; + log.debug("RS profile changed to {}", key); eventBus.post(new RuneScapeProfileChanged()); } @@ -1226,6 +1296,12 @@ public class ConfigManager updateRSProfile(); } + @Subscribe + private void onAccountHashChanged(AccountHashChanged ev) + { + updateRSProfile(); + } + @Subscribe private void onWorldChanged(WorldChanged ev) { diff --git a/runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfile.java b/runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfile.java index f8f7d78310..2b8974c768 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfile.java +++ b/runelite-client/src/main/java/net/runelite/client/config/RuneScapeProfile.java @@ -33,9 +33,12 @@ import lombok.Data; @Data public class RuneScapeProfile { + public static final int ACCOUNT_HASH_INVALID = -1; + private final String displayName; private final RuneScapeProfileType type; private final byte[] loginHash; + private final long accountHash; /** * Profile key used to save configs for this profile to the config store. This will diff --git a/runelite-client/src/main/java/net/runelite/client/hiscore/HiscoreSkill.java b/runelite-client/src/main/java/net/runelite/client/hiscore/HiscoreSkill.java index 6cf3dc94ca..69b0f11b67 100644 --- a/runelite-client/src/main/java/net/runelite/client/hiscore/HiscoreSkill.java +++ b/runelite-client/src/main/java/net/runelite/client/hiscore/HiscoreSkill.java @@ -94,7 +94,7 @@ public enum HiscoreSkill KALPHITE_QUEEN("Kalphite Queen", BOSS), KING_BLACK_DRAGON("King Black Dragon", BOSS), KRAKEN("Kraken", BOSS), - KREEARRA("Kree'Arra", BOSS), + KREEARRA("Kree'arra", BOSS), KRIL_TSUTSAROTH("K'ril Tsutsaroth", BOSS), MIMIC("Mimic", BOSS), NEX("Nex", BOSS), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java index 71c2726f3a..de4bb16b82 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bank/BankPlugin.java @@ -264,32 +264,14 @@ public class BankPlugin extends Plugin { if (event.getScriptId() == ScriptID.BANKMAIN_BUILD) { - // Compute bank prices using only the shown items so that we can show bank value during searches - final Widget bankItemContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER); - final ItemContainer bankContainer = client.getItemContainer(InventoryID.BANK); - final Widget[] children = bankItemContainer.getChildren(); - long geTotal = 0, haTotal = 0; - - if (bankContainer != null && children != null) + ContainerPrices price = getWidgetContainerPrices(WidgetInfo.BANK_ITEM_CONTAINER, InventoryID.BANK); + if (price == null) { - log.debug("Computing bank price of {} items", bankContainer.size()); - - // The first components are the bank items, followed by tabs etc. There are always 816 components regardless - // of bank size, but we only need to check up to the bank size. - for (int i = 0; i < bankContainer.size(); ++i) - { - Widget child = children[i]; - if (child != null && !child.isSelfHidden() && child.getItemId() > -1) - { - final int alchPrice = getHaPrice(child.getItemId()); - geTotal += (long) itemManager.getItemPrice(child.getItemId()) * child.getItemQuantity(); - haTotal += (long) alchPrice * child.getItemQuantity(); - } - } - - Widget bankTitle = client.getWidget(WidgetInfo.BANK_TITLE_BAR); - bankTitle.setText(bankTitle.getText() + createValueText(geTotal, haTotal)); + return; } + + Widget bankTitle = client.getWidget(WidgetInfo.BANK_TITLE_BAR); + bankTitle.setText(bankTitle.getText() + createValueText(price.getGePrice(), price.getHighAlchPrice())); } else if (event.getScriptId() == ScriptID.BANKMAIN_SEARCH_REFRESH) { @@ -302,6 +284,17 @@ public class BankPlugin extends Plugin searchString = inputText; } } + else if (event.getScriptId() == ScriptID.GROUP_IRONMAN_STORAGE_BUILD) + { + ContainerPrices price = getWidgetContainerPrices(WidgetInfo.GROUP_STORAGE_ITEM_CONTAINER, InventoryID.GROUP_STORAGE); + if (price == null) + { + return; + } + + Widget bankTitle = client.getWidget(WidgetInfo.GROUP_STORAGE_UI).getChild(1); + bankTitle.setText(bankTitle.getText() + createValueText(price.getGePrice(), price.getHighAlchPrice())); + } } @Subscribe @@ -537,4 +530,35 @@ public class BankPlugin extends Plugin return itemManager.getItemComposition(itemId).getHaPrice(); } } + + private ContainerPrices getWidgetContainerPrices(WidgetInfo widgetInfo, InventoryID inventoryID) + { + final Widget widget = client.getWidget(widgetInfo); + final ItemContainer itemContainer = client.getItemContainer(inventoryID); + final Widget[] children = widget.getChildren(); + ContainerPrices prices = null; + + if (itemContainer != null && children != null) + { + long geTotal = 0, haTotal = 0; + log.debug("Computing bank price of {} items", itemContainer.size()); + + // In the bank, the first components are the bank items, followed by tabs etc. There are always 816 components regardless + // of bank size, but we only need to check up to the bank size. + for (int i = 0; i < itemContainer.size(); ++i) + { + Widget child = children[i]; + if (child != null && !child.isSelfHidden() && child.getItemId() > -1) + { + final int alchPrice = getHaPrice(child.getItemId()); + geTotal += (long) itemManager.getItemPrice(child.getItemId()) * child.getItemQuantity(); + haTotal += (long) alchPrice * child.getItemQuantity(); + } + } + + prices = new ContainerPrices(geTotal, haTotal); + } + + return prices; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsConfig.java index 91cec5c09c..0711d17fb3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsConfig.java @@ -54,7 +54,7 @@ public interface BoostsConfig extends Config ) default DisplayBoosts displayBoosts() { - return DisplayBoosts.BOTH; + return DisplayBoosts.COMBAT; } @ConfigItem( @@ -76,7 +76,7 @@ public interface BoostsConfig extends Config ) default boolean displayInfoboxes() { - return false; + return true; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 3d86d52ad9..ec92e92d39 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -110,10 +110,8 @@ public class ChatCommandsPlugin extends Plugin private static final String TEAM_SIZES = "(?:\\d+(?:\\+|-\\d+)? players|Solo)"; private static final Pattern RAIDS_PB_PATTERN = Pattern.compile("Congratulations - your raid is complete!
    Team size: " + TEAM_SIZES + " Duration: (?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); private static final Pattern RAIDS_DURATION_PATTERN = Pattern.compile("Congratulations - your raid is complete!
    Team size: " + TEAM_SIZES + " Duration: [0-9:.]+ Personal best: (?[0-9:]+(?:\\.[0-9]+)?)"); - private static final Pattern TOB_WAVE_PB_PATTERN = Pattern.compile("Theatre of Blood wave completion time: (?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); - private static final Pattern TOB_WAVE_DURATION_PATTERN = Pattern.compile("Theatre of Blood wave completion time: [0-9:.]+\\. Personal best: (?[0-9:]+(?:\\.[0-9]+)?)"); - private static final Pattern KILL_DURATION_PATTERN = Pattern.compile("(?i)(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in) [0-9:.]+\\. Personal best: (?:)?(?[0-9:]+(?:\\.[0-9]+)?)"); - private static final Pattern NEW_PB_PATTERN = Pattern.compile("(?i)(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in) (?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); + private static final Pattern KILL_DURATION_PATTERN = Pattern.compile("(?i)(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in|(?[0-9:.]+\\. Personal best: (?:)?(?[0-9:]+(?:\\.[0-9]+)?)"); + private static final Pattern NEW_PB_PATTERN = Pattern.compile("(?i)(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in|(?(?[0-9:]+(?:\\.[0-9]+)?) \\(new personal best\\)"); private static final Pattern DUEL_ARENA_WINS_PATTERN = Pattern.compile("You (were defeated|won)! You have(?: now)? won ([\\d,]+|one) duels?"); private static final Pattern DUEL_ARENA_LOSSES_PATTERN = Pattern.compile("You have(?: now)? lost ([\\d,]+|one) duels?"); private static final Pattern ADVENTURE_LOG_TITLE_PATTERN = Pattern.compile("The Exploits of (.+)"); @@ -123,6 +121,7 @@ public class ChatCommandsPlugin extends Plugin private static final Pattern HS_KC_FLOOR_PATTERN = Pattern.compile("You have completed Floor (\\d) of the Hallowed Sepulchre! Total completions: ([0-9,]+)\\."); private static final Pattern HS_KC_GHC_PATTERN = Pattern.compile("You have opened the Grand Hallowed Coffin ([0-9,]+) times?!"); private static final Pattern COLLECTION_LOG_ITEM_PATTERN = Pattern.compile("New item added to your collection log: (.*)"); + private static final Pattern GUARDIANS_OF_THE_RIFT_PATTERN = Pattern.compile("Amount of Rifts you have closed: ([0-9,]+)."); private static final String TOTAL_LEVEL_COMMAND_STRING = "!total"; private static final String PRICE_COMMAND_STRING = "!price"; @@ -469,18 +468,6 @@ public class ChatCommandsPlugin extends Plugin matchPb(matcher); } - matcher = TOB_WAVE_PB_PATTERN.matcher(message); - if (matcher.find()) - { - matchPb(matcher); - } - - matcher = TOB_WAVE_DURATION_PATTERN.matcher(message); - if (matcher.find()) - { - matchPb(matcher); - } - matcher = HS_PB_PATTERN.matcher(message); if (matcher.find()) { @@ -538,6 +525,13 @@ public class ChatCommandsPlugin extends Plugin } } } + + matcher = GUARDIANS_OF_THE_RIFT_PATTERN.matcher(message); + if (matcher.find()) + { + int kc = Integer.parseInt(matcher.group(1)); + setKc("Guardians of the Rift", kc); + } } @VisibleForTesting @@ -1345,13 +1339,8 @@ public class ChatCommandsPlugin extends Plugin search = message.substring(LEVEL_COMMAND_STRING.length() + 1); } - search = SkillAbbreviations.getFullName(search); - final HiscoreSkill skill; - try - { - skill = HiscoreSkill.valueOf(search.toUpperCase()); - } - catch (IllegalArgumentException i) + final HiscoreSkill skill = findHiscoreSkill(search); + if (skill == null) { return; } @@ -1970,13 +1959,9 @@ public class ChatCommandsPlugin extends Plugin case "tob hm": case "tob hard mode": case "tob hard": + case "hmt": return "Theatre of Blood Hard Mode"; - // agility course - case "prif": - case "prifddinas": - return "Prifddinas Agility Course"; - // The Gauntlet case "gaunt": case "gauntlet": @@ -2027,6 +2012,26 @@ public class ChatCommandsPlugin extends Plugin case "hs 5": return "Hallowed Sepulchre Floor 5"; + // Prifddinas Agility Course + case "prif": + case "prifddinas": + return "Prifddinas Agility Course"; + + // Shayzien Basic Agility Course + case "shayb": + case "sbac": + case "shayzienbasic": + case "shayzien basic": + return "Shayzien Basic Agility Course"; + + // Shayzien Advanced Agility Course + case "shaya": + case "saac": + case "shayadv": + case "shayadvanced": + case "shayzien advanced": + return "Shayzien Advanced Agility Course"; + // Ape Atoll Agility case "aa": case "ape atoll": @@ -2152,8 +2157,103 @@ public class ChatCommandsPlugin extends Plugin case "jad 6": return "TzHaar-Ket-Rak's Sixth Challenge"; + // Guardians of the Rift + case "gotr": + case "runetodt": + return "Guardians of the Rift"; + default: return WordUtils.capitalize(boss); } } + + private static String longSkillName(String skill) + { + switch (skill.toUpperCase()) + { + case "ATK": + case "ATT": + return net.runelite.api.Skill.ATTACK.getName(); + case "DEF": + return net.runelite.api.Skill.DEFENCE.getName(); + case "STR": + return net.runelite.api.Skill.STRENGTH.getName(); + case "HEALTH": + case "HIT": + case "HITPOINT": + case "HP": + return net.runelite.api.Skill.HITPOINTS.getName(); + case "RANGE": + case "RANGING": + case "RNG": + return net.runelite.api.Skill.RANGED.getName(); + case "PRAY": + return net.runelite.api.Skill.PRAYER.getName(); + case "MAG": + case "MAGE": + return net.runelite.api.Skill.MAGIC.getName(); + case "COOK": + return net.runelite.api.Skill.COOKING.getName(); + case "WC": + case "WOOD": + case "WOODCUT": + return net.runelite.api.Skill.WOODCUTTING.getName(); + case "FLETCH": + return net.runelite.api.Skill.FLETCHING.getName(); + case "FISH": + return net.runelite.api.Skill.FISHING.getName(); + case "FM": + case "FIRE": + return net.runelite.api.Skill.FIREMAKING.getName(); + case "CRAFT": + return net.runelite.api.Skill.CRAFTING.getName(); + case "SMITH": + return net.runelite.api.Skill.SMITHING.getName(); + case "MINE": + return net.runelite.api.Skill.MINING.getName(); + case "HL": + case "HERB": + return net.runelite.api.Skill.HERBLORE.getName(); + case "AGI": + case "AGIL": + return net.runelite.api.Skill.AGILITY.getName(); + case "THIEF": + return net.runelite.api.Skill.THIEVING.getName(); + case "SLAY": + return net.runelite.api.Skill.SLAYER.getName(); + case "FARM": + return net.runelite.api.Skill.FARMING.getName(); + case "RC": + case "RUNE": + case "RUNECRAFTING": + return net.runelite.api.Skill.RUNECRAFT.getName(); + case "HUNT": + return net.runelite.api.Skill.HUNTER.getName(); + case "CON": + case "CONSTRUCT": + return net.runelite.api.Skill.CONSTRUCTION.getName(); + case "ALL": + case "TOTAL": + return net.runelite.api.Skill.OVERALL.getName(); + default: + return skill; + } + } + + private static HiscoreSkill findHiscoreSkill(String search) + { + String s = longSkillName(search); + if (s == search) + { + s = longBossName(search); + } + for (HiscoreSkill skill : HiscoreSkill.values()) + { + if (skill.getName().equals(s)) + { + return skill; + } + } + return null; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/SkillAbbreviations.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/SkillAbbreviations.java deleted file mode 100644 index 754c2f869e..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/SkillAbbreviations.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.plugins.chatcommands; - -import com.google.common.collect.ImmutableMap; -import java.util.Map; -import net.runelite.api.Skill; - -class SkillAbbreviations -{ - private static final Map MAP; - - static - { - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - builder.put("ATK", Skill.ATTACK.getName()); - builder.put("ATT", Skill.ATTACK.getName()); - builder.put("DEF", Skill.DEFENCE.getName()); - builder.put("STR", Skill.STRENGTH.getName()); - builder.put("HEALTH", Skill.HITPOINTS.getName()); - builder.put("HIT", Skill.HITPOINTS.getName()); - builder.put("HITPOINT", Skill.HITPOINTS.getName()); - builder.put("HP", Skill.HITPOINTS.getName()); - builder.put("RANGE", Skill.RANGED.getName()); - builder.put("RANGING", Skill.RANGED.getName()); - builder.put("RNG", Skill.RANGED.getName()); - builder.put("PRAY", Skill.PRAYER.getName()); - builder.put("MAG", Skill.MAGIC.getName()); - builder.put("MAGE", Skill.MAGIC.getName()); - builder.put("COOK", Skill.COOKING.getName()); - builder.put("WC", Skill.WOODCUTTING.getName()); - builder.put("WOOD", Skill.WOODCUTTING.getName()); - builder.put("WOODCUT", Skill.WOODCUTTING.getName()); - builder.put("FLETCH", Skill.FLETCHING.getName()); - builder.put("FISH", Skill.FISHING.getName()); - builder.put("FM", Skill.FIREMAKING.getName()); - builder.put("FIRE", Skill.FIREMAKING.getName()); - builder.put("CRAFT", Skill.CRAFTING.getName()); - builder.put("SMITH", Skill.SMITHING.getName()); - builder.put("MINE", Skill.MINING.getName()); - builder.put("HL", Skill.HERBLORE.getName()); - builder.put("HERB", Skill.HERBLORE.getName()); - builder.put("AGI", Skill.AGILITY.getName()); - builder.put("AGIL", Skill.AGILITY.getName()); - builder.put("THIEF", Skill.THIEVING.getName()); - builder.put("SLAY", Skill.SLAYER.getName()); - builder.put("FARM", Skill.FARMING.getName()); - builder.put("RC", Skill.RUNECRAFT.getName()); - builder.put("RUNE", Skill.RUNECRAFT.getName()); - builder.put("RUNECRAFTING", Skill.RUNECRAFT.getName()); - builder.put("HUNT", Skill.HUNTER.getName()); - builder.put("CON", Skill.CONSTRUCTION.getName()); - builder.put("CONSTRUCT", Skill.CONSTRUCTION.getName()); - builder.put("ALL", Skill.OVERALL.getName()); - builder.put("TOTAL", Skill.OVERALL.getName()); - MAP = builder.build(); - } - - /** - * Takes a string representing the name of a skill, and if abbreviated, - * expands it into its full canonical name. Case-insensitive. - * - * @param abbrev Skill name that may be abbreviated. - * @return Full skill name if recognized, else the original string. - */ - static String getFullName(String abbrev) - { - return MAP.getOrDefault(abbrev.toUpperCase(), abbrev); - } -} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CipherClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CipherClue.java index 1c9bb01b7c..711725bc72 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CipherClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CipherClue.java @@ -52,7 +52,7 @@ public class CipherClue extends ClueScroll implements TextClueScroll, NpcClueScr new CipherClue("OVEXON", "Eluned", new WorldPoint(2289, 3144, 0), "Outside Lletya or in Prifddinas after Song of the Elves", "A question on elven crystal math. I have 5 and 3 crystals, large and small respectively. A large crystal is worth 10,000 coins and a small is worth but 1,000. How much are all my crystals worth?", "53,000"), new CipherClue("VTYR APCNTGLW", "King Percival", new WorldPoint(2634, 4682, 1), "Fisher Realm, first floor. Fairy ring BJR", "How many cannons are on this here castle?", "5"), new CipherClue("UZZU MUJHRKYYKJ", "Otto Godblessed", new WorldPoint(2501, 3487, 0), "Otto's Grotto", "How many pyre sites are found around this lake?", "3"), - new CipherClue("USBJCPSO", "Traiborn", new WorldPoint(3112, 3162, 0), "First floor of Wizards Tower. Fairy ring DIS", "How many air runes would I need to cast 630 wind waves?", "3150"), + new CipherClue("USBJCPSO", "Wizard Traiborn", new WorldPoint(3112, 3162, 0), "First floor of Wizards Tower. Fairy ring DIS", "How many air runes would I need to cast 630 wind waves?", "3150"), new CipherClue("HCKTA IQFHCVJGT", "Fairy Godfather", new WorldPoint(2446, 4428, 0), "Zanaris throne room", "There are 3 inputs and 4 letters on each ring How many total individual fairy ring codes are possible?", "64"), new CipherClue("ZSBKDO ZODO", "Pirate Pete", new WorldPoint(3680, 3537, 0), "Dock northeast of the Ectofunctus"), new CipherClue("GBJSZ RVFFO", "Fairy Queen", new WorldPoint(2347, 4435, 0), "Fairy Resistance Hideout"), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java index 028940f714..a76d50b556 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java @@ -103,8 +103,8 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu private static final List CLUES = ImmutableList.of( new EmoteClue("Beckon on the east coast of the Kharazi Jungle. Beware of double agents! Equip any vestment stole and a heraldic rune shield.", "Kharazi Jungle", NORTHEAST_CORNER_OF_THE_KHARAZI_JUNGLE, new WorldPoint(2954, 2933, 0), DOUBLE_AGENT_108, BECKON, any("Any stole", item(GUTHIX_STOLE), item(SARADOMIN_STOLE), item(ZAMORAK_STOLE), item(ARMADYL_STOLE), item(BANDOS_STOLE), item(ANCIENT_STOLE)), any("Any heraldic rune shield", item(RUNE_SHIELD_H1), item(RUNE_SHIELD_H2), item(RUNE_SHIELD_H3), item(RUNE_SHIELD_H4), item(RUNE_SHIELD_H5))), new EmoteClue("Cheer in the Barbarian Agility Arena. Headbang before you talk to me. Equip a steel platebody, maple shortbow and a Wilderness cape.", "Barbarian Outpost", BARBARIAN_OUTPOST_OBSTACLE_COURSE, new WorldPoint(2552, 3556, 0), CHEER, HEADBANG, item(STEEL_PLATEBODY), item(MAPLE_SHORTBOW), range("Any team cape", TEAM1_CAPE, TEAM50_CAPE)), - new EmoteClue("Bow upstairs in the Edgeville Monastery. Equip a completed prayer book.", "Edgeville Monastery", SOUTHEAST_CORNER_OF_THE_MONASTERY, new WorldPoint(3056, 3484, 1), BOW, any("Any god book", item(HOLY_BOOK), item(BOOK_OF_BALANCE), item(UNHOLY_BOOK), item(BOOK_OF_LAW), item(BOOK_OF_WAR), item(BOOK_OF_DARKNESS))), - new EmoteClue("Cheer in the Shadow dungeon. Equip a rune crossbow, climbing boots and any mitre.", "Shadow dungeon", ENTRANCE_OF_THE_CAVE_OF_DAMIS, new WorldPoint(2629, 5071, 0), CHEER, any("Any mitre", item(GUTHIX_MITRE), item(SARADOMIN_MITRE), item(ZAMORAK_MITRE), item(ANCIENT_MITRE), item(BANDOS_MITRE), item(ARMADYL_MITRE)), item(RUNE_CROSSBOW), item(CLIMBING_BOOTS), item(RING_OF_VISIBILITY)), + new EmoteClue("Bow upstairs in the Edgeville Monastery. Equip a completed prayer book.", "Edgeville Monastery", SOUTHEAST_CORNER_OF_THE_MONASTERY, new WorldPoint(3056, 3484, 1), BOW, any("Any god book", item(HOLY_BOOK), item(BOOK_OF_BALANCE), item(UNHOLY_BOOK), item(BOOK_OF_LAW), item(BOOK_OF_WAR), item(BOOK_OF_DARKNESS), item(HOLY_BOOK_OR), item(BOOK_OF_BALANCE_OR), item(UNHOLY_BOOK_OR), item(BOOK_OF_LAW_OR), item(BOOK_OF_WAR_OR), item(BOOK_OF_DARKNESS_OR))), + new EmoteClue("Cheer in the Shadow dungeon. Equip a rune crossbow, climbing boots and any mitre.", "Shadow dungeon", ENTRANCE_OF_THE_CAVE_OF_DAMIS, new WorldPoint(2629, 5071, 0), CHEER, any("Any mitre", item(GUTHIX_MITRE), item(SARADOMIN_MITRE), item(ZAMORAK_MITRE), item(ANCIENT_MITRE), item(BANDOS_MITRE), item(ARMADYL_MITRE)), any("Rune crossbow", item(RUNE_CROSSBOW), item(RUNE_CROSSBOW_OR)), item(CLIMBING_BOOTS), item(RING_OF_VISIBILITY)), new EmoteClue("Cheer at the top of the agility pyramid. Beware of double agents! Equip a blue mystic robe top, and any rune heraldic shield.", "Agility Pyramid", AGILITY_PYRAMID, new WorldPoint(3043, 4697, 3), DOUBLE_AGENT_108, CHEER, item(MYSTIC_ROBE_TOP), any("Any rune heraldic shield", item(RUNE_SHIELD_H1), item(RUNE_SHIELD_H2), item(RUNE_SHIELD_H3), item(RUNE_SHIELD_H4), item(RUNE_SHIELD_H5))), new EmoteClue("Dance in Iban's temple. Beware of double agents! Equip Iban's staff, a black mystic top and a black mystic bottom.", "Iban's temple", WELL_OF_VOYAGE, new WorldPoint(2011, 4712, 0), DOUBLE_AGENT_141, DANCE, any("Any iban's staff", item(IBANS_STAFF), item(IBANS_STAFF_U)), item(MYSTIC_ROBE_TOP_DARK), item(MYSTIC_ROBE_BOTTOM_DARK)), new EmoteClue("Dance on the Fishing Platform. Equip barrows gloves, an amulet of glory and a dragon med helm.", "Fishing Platform", SOUTHEAST_CORNER_OF_THE_FISHING_PLATFORM, new WorldPoint(2782, 3273, 0), DANCE, any("Any amulet of glory", item(AMULET_OF_GLORY), item(AMULET_OF_GLORY1), item(AMULET_OF_GLORY2), item(AMULET_OF_GLORY3), item(AMULET_OF_GLORY4), item(AMULET_OF_GLORY5), item(AMULET_OF_GLORY6)), item(BARROWS_GLOVES), item(DRAGON_MED_HELM)), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/FaloTheBardClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/FaloTheBardClue.java index 2135bbbe52..c7b0a89ca4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/FaloTheBardClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/FaloTheBardClue.java @@ -52,7 +52,7 @@ public class FaloTheBardClue extends ClueScroll implements TextClueScroll, NpcCl { private static final List CLUES = ImmutableList.of( new FaloTheBardClue("A blood red weapon, a strong curved sword, found on the island of primate lords.", any("Dragon scimitar", item(DRAGON_SCIMITAR), item(DRAGON_SCIMITAR_OR))), - new FaloTheBardClue("A book that preaches of some great figure, lending strength, might and vigour.", any("Any god book (must be complete)", item(HOLY_BOOK), item(BOOK_OF_BALANCE), item(UNHOLY_BOOK), item(BOOK_OF_LAW), item(BOOK_OF_WAR), item(BOOK_OF_DARKNESS))), + new FaloTheBardClue("A book that preaches of some great figure, lending strength, might and vigour.", any("Any god book (must be complete)", item(HOLY_BOOK), item(BOOK_OF_BALANCE), item(UNHOLY_BOOK), item(BOOK_OF_LAW), item(BOOK_OF_WAR), item(BOOK_OF_DARKNESS), item(HOLY_BOOK_OR), item(BOOK_OF_BALANCE_OR), item(UNHOLY_BOOK_OR), item(BOOK_OF_LAW_OR), item(BOOK_OF_WAR_OR), item(BOOK_OF_DARKNESS_OR))), new FaloTheBardClue("A bow of elven craft was made, it shimmers bright, but will soon fade.", any("Crystal Bow", item(CRYSTAL_BOW), item(CRYSTAL_BOW_24123))), new FaloTheBardClue("A fiery axe of great inferno, when you use it, you'll wonder where the logs go.", any("Infernal axe", item(INFERNAL_AXE), item(INFERNAL_AXE_OR))), new FaloTheBardClue("A mark used to increase one's grace, found atop a seer's place.", item(MARK_OF_GRACE)), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java index 3843b0483a..48201d2701 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java @@ -185,7 +185,7 @@ public enum HotColdLocation ZEAH_PISCARILUS_MINE(MASTER, new WorldPoint(1768, 3705, 0), ZEAH, "South of the Piscarilius mine.", ANCIENT_WIZARDS), ZEAH_GOLDEN_FIELD_TAVERN(MASTER, new WorldPoint(1718, 3643, 0), ZEAH, "South of the gravestone in Kingstown.", BRASSICAN_MAGE), ZEAH_MESS_HALL(MASTER, new WorldPoint(1656, 3621, 0), ZEAH, "East of the Mess hall.", BRASSICAN_MAGE), - ZEAH_WATSONS_HOUSE(MASTER, new WorldPoint(1653, 3573, 0), ZEAH, "East of Watson's house.", BRASSICAN_MAGE), + ZEAH_WATSONS_HOUSE(MASTER, new WorldPoint(1653, 3572, 0), ZEAH, "East of Watson's house.", BRASSICAN_MAGE), ZEAH_VANNAHS_FARM_STORE(MASTER, new WorldPoint(1807, 3523, 0), ZEAH, "North of Tithe Farm, next to the pond.", BRASSICAN_MAGE), ZEAH_FARMING_GUILD_W(MASTER, new WorldPoint(1208, 3736, 0), ZEAH, "West of the Farming Guild.", BRASSICAN_MAGE), ZEAH_DAIRY_COW(MASTER, new WorldPoint(1324, 3722, 0), ZEAH, "North-east of the Kebos Lowlands, east of the dairy cow.", BRASSICAN_MAGE), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java index a7674a28b1..feb05e899c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java @@ -312,6 +312,7 @@ enum DiscordGameEventType MG_TZHAAR_FIGHT_PITS("Tzhaar Fight Pits", DiscordAreaType.MINIGAMES, 9552), MG_VARROCK_RAT_PITS("Varrock Rat Pits", DiscordAreaType.MINIGAMES, 11599), MG_VOLCANIC_MINE("Volcanic Mine", DiscordAreaType.MINIGAMES, 15263, 15262), + MG_GUARDIANS_OF_THE_RIFT("Guardians of the Rift", DiscordAreaType.MINIGAMES, 14484), // Raids RAIDS_CHAMBERS_OF_XERIC("Chambers of Xeric", DiscordAreaType.RAIDS, 12889, 13136, 13137, 13138, 13139, 13140, 13141, 13145, 13393, 13394, 13395, 13396, 13397, 13401), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java index 18129f5870..e5cb354169 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java @@ -185,7 +185,7 @@ public class GrandExchangePlugin extends Plugin private boolean wasFuzzySearch; private String machineUuid; - private String lastUsername; + private long lastAccount; private int tradeSeq; /** @@ -298,7 +298,8 @@ public class GrandExchangePlugin extends Plugin clientToolbar.removeNavigation(button); mouseManager.unregisterMouseListener(inputListener); keyManager.unregisterKeyListener(inputListener); - lastUsername = machineUuid = null; + machineUuid = null; + lastAccount = -1L; tradeSeq = 0; } @@ -893,13 +894,13 @@ public class GrandExchangePlugin extends Plugin private String getMachineUuid() { - String username = client.getUsername(); - if (lastUsername == username) + long accountHash = client.getAccountHash(); + if (lastAccount == accountHash) { return machineUuid; } - lastUsername = username; + lastAccount = accountHash; try { @@ -922,7 +923,7 @@ public class GrandExchangePlugin extends Plugin hasher.putBytes(hardwareAddress); } } - hasher.putUnencodedChars(username); + hasher.putLong(accountHash); machineUuid = hasher.hash().toString(); tradeSeq = 0; return machineUuid; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java index c5915d9693..9b601c5e27 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/inventoryviewer/InventoryViewerOverlay.java @@ -36,6 +36,7 @@ import net.runelite.api.Item; import net.runelite.api.ItemComposition; import net.runelite.api.ItemContainer; import net.runelite.api.VarClientInt; +import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPosition; @@ -75,7 +76,8 @@ class InventoryViewerOverlay extends OverlayPanel return null; } - if (client.getVar(VarClientInt.INVENTORY_TAB) == 3 && config.hideIfInventoryActive()) + if ((client.getVar(VarClientInt.INVENTORY_TAB) == 3 || client.getWidget(WidgetInfo.BANK_CONTAINER) != null) + && config.hideIfInventoryActive()) { return null; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java index 5ff384a8d0..600b493fa8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java @@ -83,7 +83,7 @@ public class ItemChargePlugin extends Plugin private static final Pattern BINDING_CHECK_PATTERN = Pattern.compile( "You have ([0-9]+|one) charges? left before your Binding necklace disintegrates\\."); private static final Pattern BINDING_USED_PATTERN = Pattern.compile( - "You bind the temple's power into (mud|lava|steam|dust|smoke|mist) runes\\."); + "You (partially succeed to )?bind the temple's power into (mud|lava|steam|dust|smoke|mist) runes\\."); private static final String BINDING_BREAK_TEXT = "Your Binding necklace has disintegrated."; private static final Pattern RING_OF_FORGING_CHECK_PATTERN = Pattern.compile( "You can smelt ([0-9]+|one) more pieces? of iron ore before a ring melts\\."); @@ -303,7 +303,11 @@ public class ItemChargePlugin extends Plugin } else if (bindingNecklaceUsedMatcher.find()) { - updateBindingNecklaceCharges(getItemCharges(ItemChargeConfig.KEY_BINDING_NECKLACE) - 1); + final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT); + if (equipment.contains(ItemID.BINDING_NECKLACE)) + { + updateBindingNecklaceCharges(getItemCharges(ItemChargeConfig.KEY_BINDING_NECKLACE) - 1); + } } else if (bindingNecklaceCheckMatcher.find()) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 793b357fde..5d570d52ff 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -263,6 +263,12 @@ public class LootTrackerPlugin extends Plugin private static final String TEMPOROSS_LOOT_STRING = "You found some loot: "; private static final int TEMPOROSS_REGION = 12588; + // Guardians of the Rift + private static final String GUARDIANS_OF_THE_RIFT_EVENT = "Guardians of the Rift"; + private static final String INTRICATE_POUCH_EVENT = "Intricate pouch"; + private static final String GUARDIANS_OF_THE_RIFT_LOOT_STRING = "You found some loot: "; + private static final int GUARDIANS_OF_THE_RIFT_REGION = 14484; + // Mahogany Homes private static final String MAHOGANY_CRATE_EVENT = "Supply crate (Mahogany Homes)"; @@ -480,6 +486,13 @@ public class LootTrackerPlugin extends Plugin clientThread.invokeLater(() -> { + // convertToLootTrackerRecord requires item compositions to be available to get the item name, + // so it can't be run while the client is starting + if (client.getGameState().getState() < GameState.LOGIN_SCREEN.getState()) + { + return false; + } + // convertToLootTrackerRecord must be called on client thread List records = loots.stream() .map(this::convertToLootTrackerRecord) @@ -489,6 +502,8 @@ public class LootTrackerPlugin extends Plugin panel.clearRecords(); panel.addRecords(records); }); + + return true; }); }); } @@ -887,6 +902,12 @@ public class LootTrackerPlugin extends Plugin return; } + if (regionID == GUARDIANS_OF_THE_RIFT_REGION && message.startsWith(GUARDIANS_OF_THE_RIFT_LOOT_STRING)) + { + onInvChange(collectInvItems(LootRecordType.EVENT, GUARDIANS_OF_THE_RIFT_EVENT, client.getBoostedSkillLevel(Skill.RUNECRAFT))); + return; + } + if (message.equals(IMPLING_CATCH_MESSAGE)) { onInvChange(collectInvItems(LootRecordType.EVENT, client.getLocalPlayer().getInteracting().getName())); @@ -977,6 +998,9 @@ public class LootTrackerPlugin extends Plugin case ItemID.CASKET_25590: onInvChange(collectInvAndGroundItems(LootRecordType.EVENT, TEMPOROSS_CASKET_EVENT)); break; + case ItemID.INTRICATE_POUCH: + onInvChange(collectInvAndGroundItems(LootRecordType.EVENT, INTRICATE_POUCH_EVENT)); + break; case ItemID.SIMPLE_LOCKBOX_25647: case ItemID.ELABORATE_LOCKBOX_25649: case ItemID.ORNATE_LOCKBOX_25651: diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java index 82b89bea25..64eb37734b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java @@ -137,6 +137,18 @@ public interface MenuEntrySwapperConfig extends Config return true; } + @ConfigItem( + position = -2, + keyName = "objectLeftClickCustomization", + name = "Customizable left-click", + description = "Allows customization of left-clicks on objects", + section = objectSection + ) + default boolean objectLeftClickCustomization() + { + return true; + } + @ConfigItem( keyName = "swapAdmire", name = "Admire", @@ -826,4 +838,15 @@ public interface MenuEntrySwapperConfig extends Config { return false; } + + @ConfigItem( + keyName = "swapDepositPool", + name = "Deposit Pool - Only Runes", + description = "Swap Deposit with Deposit Runes on the Deposit Pool in Guardians of the Rift.", + section = objectSection + ) + default boolean swapDepositPool() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java index 709a4162aa..4ea328b280 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java @@ -29,7 +29,9 @@ package net.runelite.client.plugins.menuentryswapper; import com.google.common.annotations.VisibleForTesting; import static com.google.common.base.Predicates.alwaysTrue; import static com.google.common.base.Predicates.equalTo; +import com.google.common.base.Strings; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; @@ -43,6 +45,8 @@ import java.util.Set; import java.util.function.Predicate; import java.util.function.Supplier; import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.GameState; import net.runelite.api.ItemComposition; @@ -50,6 +54,7 @@ import net.runelite.api.KeyCode; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; import net.runelite.api.NPC; +import net.runelite.api.ObjectComposition; import net.runelite.api.events.ClientTick; import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuOpened; @@ -57,6 +62,9 @@ import net.runelite.api.events.PostItemComposition; import net.runelite.api.widgets.WidgetID; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.callback.ClientThread; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; @@ -79,6 +87,7 @@ import net.runelite.client.util.Text; tags = {"npcs", "inventory", "items", "objects"}, enabledByDefault = false ) +@Slf4j public class MenuEntrySwapperPlugin extends Plugin { private static final String CONFIGURE = "Configure"; @@ -89,6 +98,7 @@ public class MenuEntrySwapperPlugin extends Plugin private static final String SHIFTCLICK_CONFIG_GROUP = "shiftclick"; private static final String ITEM_KEY_PREFIX = "item_"; + private static final String OBJECT_KEY_PREFIX = "object_"; // Shift click private static final WidgetMenuOption FIXED_INVENTORY_TAB_CONFIGURE_SC = new WidgetMenuOption(CONFIGURE, @@ -139,6 +149,14 @@ public class MenuEntrySwapperPlugin extends Plugin MenuAction.NPC_FIFTH_OPTION, MenuAction.EXAMINE_NPC); + private static final List OBJECT_MENU_TYPES = ImmutableList.of( + MenuAction.GAME_OBJECT_FIRST_OPTION, + MenuAction.GAME_OBJECT_SECOND_OPTION, + MenuAction.GAME_OBJECT_THIRD_OPTION, + MenuAction.GAME_OBJECT_FOURTH_OPTION + // GAME_OBJECT_FIFTH_OPTION gets sorted underneath Walk here after we swap, so it doesn't work + ); + private static final Set ESSENCE_MINE_NPCS = ImmutableSet.of( "aubury", "sedridor", @@ -172,6 +190,9 @@ public class MenuEntrySwapperPlugin extends Plugin @Inject private ItemManager itemManager; + @Inject + private ChatMessageManager chatMessageManager; + private boolean configuringShiftClick = false; private boolean configuringLeftClick = false; @@ -419,6 +440,8 @@ public class MenuEntrySwapperPlugin extends Plugin swap("climb", "climb-up", () -> (shiftModifier() ? config.swapStairsShiftClick() : config.swapStairsLeftClick()) == MenuEntrySwapperConfig.StairsMode.CLIMB_UP); swap("climb", "climb-down", () -> (shiftModifier() ? config.swapStairsShiftClick() : config.swapStairsLeftClick()) == MenuEntrySwapperConfig.StairsMode.CLIMB_DOWN); + + swap("deposit", "deposit-runes", config::swapDepositPool); } public Swap swap(String option, String swappedOption, Supplier enabled) @@ -475,7 +498,7 @@ public class MenuEntrySwapperPlugin extends Plugin client.getItemCompositionCache().reset(); } - private Integer getSwapConfig(boolean shift, int itemId) + private Integer getItemSwapConfig(boolean shift, int itemId) { itemId = ItemVariationMapping.map(itemId); String config = configManager.getConfiguration(shift ? SHIFTCLICK_CONFIG_GROUP : MenuEntrySwapperConfig.GROUP, ITEM_KEY_PREFIX + itemId); @@ -487,13 +510,13 @@ public class MenuEntrySwapperPlugin extends Plugin return Integer.parseInt(config); } - private void setSwapConfig(boolean shift, int itemId, int index) + private void setItemSwapConfig(boolean shift, int itemId, int index) { itemId = ItemVariationMapping.map(itemId); configManager.setConfiguration(shift ? SHIFTCLICK_CONFIG_GROUP : MenuEntrySwapperConfig.GROUP, ITEM_KEY_PREFIX + itemId, index); } - private void unsetSwapConfig(boolean shift, int itemId) + private void unsetItemSwapConfig(boolean shift, int itemId) { itemId = ItemVariationMapping.map(itemId); configManager.unsetConfiguration(shift ? SHIFTCLICK_CONFIG_GROUP : MenuEntrySwapperConfig.GROUP, ITEM_KEY_PREFIX + itemId); @@ -519,6 +542,7 @@ public class MenuEntrySwapperPlugin extends Plugin { if (!configuringShiftClick && !configuringLeftClick) { + configureObjectClick(event); return; } @@ -556,7 +580,7 @@ public class MenuEntrySwapperPlugin extends Plugin else { // Otherwise it is possible that we have Use swap configured - Integer config = getSwapConfig(true, itemId); + Integer config = getItemSwapConfig(true, itemId); if (config != null && config == -1) { activeAction = MenuAction.ITEM_USE; @@ -566,7 +590,7 @@ public class MenuEntrySwapperPlugin extends Plugin else { // Apply left click action from configuration - Integer config = getSwapConfig(false, itemId); + Integer config = getItemSwapConfig(false, itemId); if (config != null) { activeAction = config >= 0 @@ -589,7 +613,7 @@ public class MenuEntrySwapperPlugin extends Plugin int index = menuAction == MenuAction.ITEM_USE ? -1 : menuAction.getId() - MenuAction.ITEM_FIRST_OPTION.getId(); - setSwapConfig(configuringShiftClick, itemId, index); + setItemSwapConfig(configuringShiftClick, itemId, index); }); if (activeAction == menuAction) @@ -603,7 +627,81 @@ public class MenuEntrySwapperPlugin extends Plugin .setOption(RESET) .setTarget(configuringShiftClick ? SHIFT_CLICK_MENU_TARGET : LEFT_CLICK_MENU_TARGET) .setType(MenuAction.RUNELITE) - .onClick(e -> unsetSwapConfig(configuringShiftClick, itemId)); + .onClick(e -> unsetItemSwapConfig(configuringShiftClick, itemId)); + } + + private void configureObjectClick(MenuOpened event) + { + if (!shiftModifier() || !config.objectLeftClickCustomization()) + { + return; + } + + MenuEntry[] entries = event.getMenuEntries(); + for (int idx = entries.length - 1; idx >= 0; --idx) + { + MenuEntry entry = entries[idx]; + if (entry.getType() == MenuAction.EXAMINE_OBJECT) + { + final ObjectComposition composition = client.getObjectDefinition(entry.getIdentifier()); + final String[] actions = composition.getActions(); + + final MenuAction currentAction; + Integer swapConfig = getObjectSwapConfig(composition.getId()); + if (swapConfig != null) + { + currentAction = OBJECT_MENU_TYPES.get(swapConfig); + } + else + { + currentAction = defaultAction(composition); + } + + for (int actionIdx = 0; actionIdx < OBJECT_MENU_TYPES.size(); ++actionIdx) + { + if (Strings.isNullOrEmpty(actions[actionIdx])) + { + continue; + } + + final int menuIdx = actionIdx; + final MenuAction menuAction = OBJECT_MENU_TYPES.get(actionIdx); + if (currentAction == menuAction) + { + continue; + } + + client.createMenuEntry(idx) + .setOption("Swap " + actions[actionIdx]) + .setTarget(entry.getTarget()) + .setType(MenuAction.RUNELITE) + .onClick(e -> + { + final String message = new ChatMessageBuilder() + .append("The default left click option for '").append(composition.getName()).append("' ") + .append("has been set to '").append(actions[menuIdx]).append("'.") + .build(); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(message) + .build()); + + log.debug("Set object swap for {} to {}", composition.getId(), menuAction); + + final MenuAction defaultAction = defaultAction(composition); + if (defaultAction != menuAction) + { + setObjectSwapConfig(composition.getId(), menuIdx); + } + else + { + unsetObjectSwapConfig(composition.getId()); + } + }); + } + } + } } @Subscribe @@ -708,7 +806,7 @@ public class MenuEntrySwapperPlugin extends Plugin // the client unable to perform the swap itself. if (config.shiftClickCustomization() && !option.equals("use")) { - Integer customOption = getSwapConfig(true, eventId); + Integer customOption = getItemSwapConfig(true, eventId); if (customOption != null && customOption == -1) { @@ -724,7 +822,7 @@ public class MenuEntrySwapperPlugin extends Plugin // Custom left-click item swap if (itemOp) { - Integer swapIndex = getSwapConfig(false, eventId); + Integer swapIndex = getItemSwapConfig(false, eventId); if (swapIndex != null) { MenuAction swapAction = swapIndex >= 0 @@ -739,6 +837,29 @@ public class MenuEntrySwapperPlugin extends Plugin } } + if (OBJECT_MENU_TYPES.contains(menuAction)) + { + // Get multiloc id + int objectId = eventId; + ObjectComposition objectComposition = client.getObjectDefinition(objectId); + if (objectComposition.getImpostorIds() != null) + { + objectComposition = objectComposition.getImpostor(); + objectId = objectComposition.getId(); + } + + Integer customOption = getObjectSwapConfig(objectId); + if (customOption != null) + { + MenuAction swapAction = OBJECT_MENU_TYPES.get(customOption); + if (swapAction == menuAction) + { + swap(optionIndexes, menuEntries, index, menuEntries.length - 1); + return; + } + } + } + // Built-in swaps Collection swaps = this.swaps.get(option); for (Swap swap : swaps) @@ -793,7 +914,7 @@ public class MenuEntrySwapperPlugin extends Plugin } ItemComposition itemComposition = event.getItemComposition(); - Integer option = getSwapConfig(true, itemComposition.getId()); + Integer option = getItemSwapConfig(true, itemComposition.getId()); if (option != null && option < itemComposition.getInventoryActions().length) { @@ -957,4 +1078,38 @@ public class MenuEntrySwapperPlugin extends Plugin configuringLeftClick = target.equals(LEFT_CLICK_MENU_TARGET); rebuildCustomizationMenus(); } + + private Integer getObjectSwapConfig(int objectId) + { + String config = configManager.getConfiguration(MenuEntrySwapperConfig.GROUP, OBJECT_KEY_PREFIX + objectId); + if (config == null || config.isEmpty()) + { + return null; + } + + return Integer.parseInt(config); + } + + private void setObjectSwapConfig(int objectId, int index) + { + configManager.setConfiguration(MenuEntrySwapperConfig.GROUP, OBJECT_KEY_PREFIX + objectId, index); + } + + private void unsetObjectSwapConfig(int objectId) + { + configManager.unsetConfiguration(MenuEntrySwapperConfig.GROUP, OBJECT_KEY_PREFIX + objectId); + } + + private static MenuAction defaultAction(ObjectComposition objectComposition) + { + String[] actions = objectComposition.getActions(); + for (int i = 0; i < OBJECT_MENU_TYPES.size(); ++i) + { + if (!Strings.isNullOrEmpty(actions[i])) + { + return OBJECT_MENU_TYPES.get(i); + } + } + return null; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaNotWorkingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaNotWorkingOverlay.java index 33e058f686..50b9cc8a35 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaNotWorkingOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaNotWorkingOverlay.java @@ -42,7 +42,7 @@ class NpcAggroAreaNotWorkingOverlay extends OverlayPanel this.plugin = plugin; panelComponent.getChildren().add(LineComponent.builder() - .left("Unaggressive NPC timers will start working when you teleport far away or enter a dungeon.") + .left("Unaggressive NPC timers require calibration. Teleport far away or enter a dungeon, then run until this overlay disappears.") .build()); setPriority(OverlayPriority.LOW); @@ -53,7 +53,7 @@ class NpcAggroAreaNotWorkingOverlay extends OverlayPanel @Override public Dimension render(Graphics2D graphics) { - if (!plugin.isActive() || plugin.getSafeCenters()[1] != null) + if (plugin.getSafeCenters()[1] != null) { return null; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/questlist/QuestListPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/questlist/QuestListPlugin.java new file mode 100644 index 0000000000..49bc77d81c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/questlist/QuestListPlugin.java @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2019 Spudjb + * Copyright (c) 2022 Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.questlist; + +import com.google.common.base.Strings; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.ParamID; +import net.runelite.api.ScriptID; +import net.runelite.api.SoundEffectID; +import net.runelite.api.SpriteID; +import net.runelite.api.VarClientInt; +import net.runelite.api.Varbits; +import net.runelite.api.events.ScriptCallbackEvent; +import net.runelite.api.events.ScriptPostFired; +import net.runelite.api.events.VarClientIntChanged; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.widgets.JavaScriptCallback; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetPositionMode; +import net.runelite.api.widgets.WidgetType; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.chatbox.ChatboxPanelManager; +import net.runelite.client.game.chatbox.ChatboxTextInput; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +@PluginDescriptor( + name = "Quest List", + description = "Adds a search filter to the quest list" +) +public class QuestListPlugin extends Plugin +{ + private static final String MENU_OPEN = "Open"; + private static final String MENU_CLOSE = "Close"; + private static final String MENU_SEARCH = "Search"; + + @Inject + private Client client; + + @Inject + private ChatboxPanelManager chatboxPanelManager; + + @Inject + private ClientThread clientThread; + + private ChatboxTextInput searchInput; + private Widget questSearchButton; + + @Override + protected void startUp() + { + clientThread.invoke(this::addQuestButtons); + } + + @Override + protected void shutDown() + { + Widget header = client.getWidget(WidgetInfo.QUESTLIST_BOX); + if (header != null) + { + header.deleteAllChildren(); + } + } + + @Subscribe + public void onScriptPostFired(ScriptPostFired event) + { + if (event.getScriptId() == ScriptID.QUESTLIST_INIT) + { + addQuestButtons(); + } + } + + private void addQuestButtons() + { + Widget header = client.getWidget(WidgetInfo.QUESTLIST_BOX); + if (header != null) + { + header.deleteAllChildren(); + + questSearchButton = header.createChild(-1, WidgetType.GRAPHIC); + questSearchButton.setSpriteId(SpriteID.GE_SEARCH); + questSearchButton.setOriginalWidth(18); + questSearchButton.setOriginalHeight(17); + questSearchButton.setXPositionMode(WidgetPositionMode.ABSOLUTE_RIGHT); + questSearchButton.setOriginalX(5); + questSearchButton.setOriginalY(0); + questSearchButton.setHasListener(true); + questSearchButton.setAction(1, MENU_OPEN); + questSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch()); + questSearchButton.setName(MENU_SEARCH); + questSearchButton.revalidate(); + } + } + + @Subscribe + public void onVarbitChanged(VarbitChanged varbitChanged) + { + if (isChatboxOpen() && !isOnQuestTab()) + { + chatboxPanelManager.close(); + } + } + + @Subscribe + public void onVarClientIntChanged(VarClientIntChanged varClientIntChanged) + { + if (varClientIntChanged.getIndex() == VarClientInt.INVENTORY_TAB.getIndex()) + { + if (isChatboxOpen() && !isOnQuestTab()) + { + chatboxPanelManager.close(); + } + } + } + + @Subscribe + public void onScriptCallbackEvent(ScriptCallbackEvent scriptCallbackEvent) + { + if (!"questFilter".equals(scriptCallbackEvent.getEventName()) || !isChatboxOpen()) + { + return; + } + + final String filter = searchInput.getValue(); + if (Strings.isNullOrEmpty(filter)) + { + return; + } + + final int[] intStack = client.getIntStack(); + final int intStackSize = client.getIntStackSize(); + + final int questStruct = intStack[intStackSize - 1]; + final String questName = client.getStructComposition(questStruct) + .getStringValue(ParamID.QUEST_NAME); + + intStack[intStackSize - 2] = questName.toLowerCase().contains(filter.toLowerCase()) ? 0 : 1; + } + + private boolean isOnQuestTab() + { + return client.getVar(Varbits.QUEST_TAB) == 0 && client.getVar(VarClientInt.INVENTORY_TAB) == 2; + } + + private boolean isChatboxOpen() + { + return searchInput != null && chatboxPanelManager.getCurrentInput() == searchInput; + } + + private void closeSearch() + { + chatboxPanelManager.close(); + redrawQuests(); + client.playSoundEffect(SoundEffectID.UI_BOOP); + } + + private void openSearch() + { + client.playSoundEffect(SoundEffectID.UI_BOOP); + questSearchButton.setAction(1, MENU_CLOSE); + questSearchButton.setOnOpListener((JavaScriptCallback) e -> closeSearch()); + searchInput = chatboxPanelManager.openTextInput("Search quest list") + .onChanged(s -> redrawQuests()) + .onDone(s -> false) + .onClose(() -> + { + redrawQuests(); + questSearchButton.setOnOpListener((JavaScriptCallback) e -> openSearch()); + questSearchButton.setAction(1, MENU_OPEN); + }) + .build(); + } + + private void redrawQuests() + { + Widget w = client.getWidget(WidgetInfo.QUESTLIST_CONTAINER); + if (w == null) + { + return; + } + + Object[] onVarTransmitListener = w.getOnVarTransmitListener(); + if (onVarTransmitListener == null) + { + return; + } + + clientThread.invokeLater(() -> + client.runScript(onVarTransmitListener)); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssMinimapOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssMinimapOverlay.java index 901fe5f805..cadc534e4e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssMinimapOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssMinimapOverlay.java @@ -71,7 +71,7 @@ class AbyssMinimapOverlay extends Overlay for (DecorativeObject object : plugin.getAbyssObjects()) { AbyssRifts rift = AbyssRifts.getRift(object.getId()); - if (rift == null || !plugin.getRifts().contains(rift)) + if (rift == null || !rift.getConfigEnabled().test(config)) { continue; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssOverlay.java index 9f3ee7686d..e772d2a152 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssOverlay.java @@ -73,7 +73,7 @@ class AbyssOverlay extends Overlay private void renderRift(Graphics2D graphics, DecorativeObject object) { AbyssRifts rift = AbyssRifts.getRift(object.getId()); - if (rift == null || !plugin.getRifts().contains(rift)) + if (rift == null || !rift.getConfigEnabled().test(config)) { return; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssRifts.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssRifts.java index dae33bdb84..cb94d6134c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssRifts.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/AbyssRifts.java @@ -42,13 +42,14 @@ import static net.runelite.api.ItemID.MIND_RUNE; import static net.runelite.api.ItemID.NATURE_RUNE; import static net.runelite.api.ItemID.SOUL_RUNE; import static net.runelite.api.ItemID.WATER_RUNE; +import net.runelite.api.NullObjectID; import net.runelite.api.ObjectID; @AllArgsConstructor enum AbyssRifts { AIR_RIFT(ObjectID.AIR_RIFT, AIR_RUNE, RunecraftConfig::showAir), - BLOOD_RIFT(ObjectID.BLOOD_RIFT, BLOOD_RUNE, RunecraftConfig::showBlood), + BLOOD_RIFT(NullObjectID.NULL_43848, BLOOD_RUNE, RunecraftConfig::showBlood), BODY_RIFT(ObjectID.BODY_RIFT, BODY_RUNE, RunecraftConfig::showBody), CHAOS_RIFT(ObjectID.CHAOS_RIFT, CHAOS_RUNE, RunecraftConfig::showChaos), COSMIC_RIFT(ObjectID.COSMIC_RIFT, COSMIC_RUNE, RunecraftConfig::showCosmic), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java index 6d9c7c3976..55339b884a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java @@ -27,7 +27,6 @@ package net.runelite.client.plugins.runecraft; import com.google.common.collect.ImmutableList; import com.google.inject.Provides; import java.awt.Color; -import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -52,7 +51,6 @@ import net.runelite.api.events.ItemContainerChanged; import net.runelite.client.Notifier; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; -import net.runelite.client.events.ConfigChanged; import net.runelite.client.game.npcoverlay.HighlightedNpc; import net.runelite.client.game.npcoverlay.NpcOverlayService; import net.runelite.client.plugins.Plugin; @@ -71,15 +69,13 @@ public class RunecraftPlugin extends Plugin private static final List DEGRADED_POUCHES = ImmutableList.of( ItemID.MEDIUM_POUCH_5511, ItemID.LARGE_POUCH_5513, - ItemID.GIANT_POUCH_5515 + ItemID.GIANT_POUCH_5515, + ItemID.COLOSSAL_POUCH_26786 ); @Getter(AccessLevel.PACKAGE) private final Set abyssObjects = new HashSet<>(); - @Getter(AccessLevel.PACKAGE) - private final Set rifts = new HashSet<>(); - private boolean degradedPouchInInventory; @Inject @@ -114,7 +110,6 @@ public class RunecraftPlugin extends Plugin npcOverlayService.registerHighlighter(highlightDarkMage); overlayManager.add(abyssOverlay); overlayManager.add(abyssMinimapOverlay); - updateRifts(); } @Override @@ -127,15 +122,6 @@ public class RunecraftPlugin extends Plugin degradedPouchInInventory = false; } - @Subscribe - public void onConfigChanged(ConfigChanged event) - { - if (event.getGroup().equals(RunecraftConfig.GROUP)) - { - updateRifts(); - } - } - @Subscribe public void onChatMessage(ChatMessage event) { @@ -205,12 +191,4 @@ public class RunecraftPlugin extends Plugin } return null; } - - private void updateRifts() - { - rifts.clear(); - Arrays.stream(AbyssRifts.values()) - .filter(r -> r.getConfigEnabled().test(config)) - .forEach(rifts::add); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java index 8f6f9d6dbc..97d70676fa 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java @@ -155,6 +155,7 @@ class XpInfoBox extends JPanel @Override public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent) { + openXpTracker.setVisible(xpTrackerConfig.wiseOldManOpenOption()); canvasItem.setText(xpTrackerPlugin.hasOverlay(skill) ? REMOVE_STATE : ADD_STATE); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java index 0ecce8290d..bf34ad75e5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java @@ -39,6 +39,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; import net.runelite.api.Actor; import net.runelite.api.Client; import net.runelite.api.Skill; @@ -112,6 +114,24 @@ class XpPanel extends PluginPanel popupMenu.add(resetPerHour); popupMenu.add(pauseAll); popupMenu.add(unpauseAll); + popupMenu.addPopupMenuListener(new PopupMenuListener() + { + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent) + { + openXpTracker.setVisible(xpTrackerConfig.wiseOldManOpenOption()); + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent popupMenuEvent) + { + } + + @Override + public void popupMenuCanceled(PopupMenuEvent popupMenuEvent) + { + } + }); overallPanel.setComponentPopupMenu(popupMenu); final JLabel overallIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(Skill.OVERALL))); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java index 9ad62e3414..b37c7153d3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerConfig.java @@ -211,4 +211,15 @@ public interface XpTrackerConfig extends Config { return false; } + + @ConfigItem( + position = 15, + keyName = "wiseOldManOpenOption", + name = "Wise Old Man Option", + description = "Adds an option to the XP info box right-click menu to open Wise Old Man" + ) + default boolean wiseOldManOpenOption() + { + return true; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java index 69ff78aa17..01aa70be93 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java @@ -34,7 +34,6 @@ import java.awt.image.BufferedImage; import java.time.temporal.ChronoUnit; import java.util.EnumSet; import java.util.List; -import java.util.Objects; import javax.inject.Inject; import lombok.AccessLevel; import lombok.Setter; @@ -123,7 +122,7 @@ public class XpTrackerPlugin extends Plugin @VisibleForTesting private XpPanel xpPanel; private XpWorldType lastWorldType; - private String lastUsername; + private long lastAccount; private long lastTickMillis = 0; private boolean fetchXp; // fetch lastXp for the online xp tracker private long lastXp = 0; @@ -162,6 +161,7 @@ public class XpTrackerPlugin extends Plugin // Initialize the tracker & last xp if already logged in fetchXp = true; initializeTracker = true; + lastAccount = -1L; } @Override @@ -182,15 +182,15 @@ public class XpTrackerPlugin extends Plugin // Check that the username changed or the world type changed. XpWorldType type = worldSetToType(client.getWorldType()); - if (!Objects.equals(client.getUsername(), lastUsername) || lastWorldType != type) + if (client.getAccountHash() != lastAccount || lastWorldType != type) { // Reset log.debug("World change: {} -> {}, {} -> {}", - lastUsername, client.getUsername(), + lastAccount, client.getAccountHash(), firstNonNull(lastWorldType, ""), firstNonNull(type, "")); - lastUsername = client.getUsername(); + lastAccount = client.getAccountHash(); // xp is not available until after login is finished, so fetch it on the next gametick fetchXp = true; lastWorldType = type; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java index 4a823d0727..38e3cc8b27 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xpupdater/XpUpdaterPlugin.java @@ -29,7 +29,6 @@ package net.runelite.client.plugins.xpupdater; import com.google.inject.Provides; import java.io.IOException; import java.util.EnumSet; -import java.util.Objects; import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; @@ -74,7 +73,7 @@ public class XpUpdaterPlugin extends Plugin @Inject private OkHttpClient okHttpClient; - private String lastUsername; + private long lastAccount; private boolean fetchXp; private long lastXp; @@ -88,6 +87,7 @@ public class XpUpdaterPlugin extends Plugin protected void startUp() { fetchXp = true; + lastAccount = -1L; } @Subscribe @@ -96,9 +96,9 @@ public class XpUpdaterPlugin extends Plugin GameState state = gameStateChanged.getGameState(); if (state == GameState.LOGGED_IN) { - if (!Objects.equals(client.getUsername(), lastUsername)) + if (lastAccount != client.getAccountHash()) { - lastUsername = client.getUsername(); + lastAccount = client.getAccountHash(); fetchXp = true; } } @@ -114,8 +114,8 @@ public class XpUpdaterPlugin extends Plugin // Don't submit update unless xp threshold is reached if (Math.abs(totalXp - lastXp) > XP_THRESHOLD) { - log.debug("Submitting update for {}", local.getName()); - update(local.getName()); + log.debug("Submitting update for {} accountHash {}", local.getName(), lastAccount); + update(lastAccount, local.getName()); lastXp = totalXp; } } @@ -131,12 +131,12 @@ public class XpUpdaterPlugin extends Plugin } } - private void update(String username) + private void update(long accountHash, String username) { EnumSet worldTypes = client.getWorldType(); username = username.replace(" ", "_"); updateCml(username, worldTypes); - updateTempleosrs(username, worldTypes); + updateTempleosrs(accountHash, username, worldTypes); updateWom(username, worldTypes); } @@ -165,7 +165,7 @@ public class XpUpdaterPlugin extends Plugin } } - private void updateTempleosrs(String username, EnumSet worldTypes) + private void updateTempleosrs(long accountHash, String username, EnumSet worldTypes) { if (config.templeosrs() && !worldTypes.contains(WorldType.SEASONAL) @@ -178,6 +178,7 @@ public class XpUpdaterPlugin extends Plugin .addPathSegment("php") .addPathSegment("add_datapoint.php") .addQueryParameter("player", username) + .addQueryParameter("accountHash", Long.toString(accountHash)) .build(); Request request = new Request.Builder() diff --git a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java index aca456df93..26242a6122 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/ClientLoader.java @@ -60,6 +60,8 @@ import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.client.RuneLite; import net.runelite.client.RuneLiteProperties; +import net.runelite.client.RuntimeConfig; +import net.runelite.client.RuntimeConfigLoader; import static net.runelite.client.rs.ClientUpdateCheckMode.AUTO; import static net.runelite.client.rs.ClientUpdateCheckMode.NONE; import static net.runelite.client.rs.ClientUpdateCheckMode.VANILLA; @@ -74,7 +76,7 @@ import okhttp3.Request; import okhttp3.Response; @Slf4j -@SuppressWarnings("deprecation") +@SuppressWarnings({"deprecation", "removal"}) public class ClientLoader implements Supplier { private static final String INJECTED_CLIENT_NAME = "/injected-client.oprs"; @@ -87,16 +89,18 @@ public class ClientLoader implements Supplier private final ClientConfigLoader clientConfigLoader; private ClientUpdateCheckMode updateCheckMode; private final WorldSupplier worldSupplier; + private final RuntimeConfigLoader runtimeConfigLoader; private final String javConfigUrl; private Object client; - public ClientLoader(OkHttpClient okHttpClient, ClientUpdateCheckMode updateCheckMode, String javConfigUrl) + public ClientLoader(OkHttpClient okHttpClient, ClientUpdateCheckMode updateCheckMode, RuntimeConfigLoader runtimeConfigLoader, String javConfigUrl) { this.okHttpClient = okHttpClient; this.clientConfigLoader = new ClientConfigLoader(okHttpClient); this.updateCheckMode = updateCheckMode; this.worldSupplier = new WorldSupplier(okHttpClient); + this.runtimeConfigLoader = runtimeConfigLoader; this.javConfigUrl = javConfigUrl; } @@ -183,12 +187,19 @@ public class ClientLoader implements Supplier return rs; } + catch (OutageException e) + { + return e; + } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException | VerificationException | SecurityException e) { log.error("Error loading RS!", e); - SwingUtilities.invokeLater(() -> FatalErrorDialog.showNetErrorWindow("loading the client", e)); + if (!checkOutages()) + { + SwingUtilities.invokeLater(() -> FatalErrorDialog.showNetErrorWindow("loading the client", e)); + } return e; } } @@ -213,6 +224,10 @@ public class ClientLoader implements Supplier catch (IOException e) { log.info("Failed to get jav_config from host \"{}\" ({})", url.host(), e.getMessage()); + if (checkOutages()) + { + throw new OutageException(e); + } if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig())) { @@ -301,6 +316,11 @@ public class ClientLoader implements Supplier } vanilla.position(0); + if (!vanillaCacheIsInvalid && "false".equals(System.getProperty("runelite.updateVanilla"))) + { + return; + } + // Start downloading the vanilla client HttpUrl url; if (config.isFallback()) @@ -412,6 +432,10 @@ public class ClientLoader implements Supplier catch (IOException e) { log.warn("Failed to download gamepack from \"{}\"", url, e); + if (checkOutages()) + { + throw new OutageException(e); + } // With fallback config do 1 attempt (there are no additional urls to try) if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig()) || config.isFallback() || attempt >= NUM_ATTEMPTS) @@ -438,6 +462,7 @@ public class ClientLoader implements Supplier SwingUtilities.invokeLater(() -> new FatalErrorDialog("The client-patch is missing from the classpath. If you are building " + "the client you need to re-run maven") + .addHelpButtons() .addBuildingGuide() .open()); throw new NullPointerException(); @@ -605,7 +630,7 @@ public class ClientLoader implements Supplier Class clientClass = classLoader.loadClass(initialClass); Applet rs = (Applet) clientClass.newInstance(); - rs.setStub(new RSAppletStub(config)); + rs.setStub(new RSAppletStub(config, runtimeConfigLoader)); if (rs instanceof Client) { @@ -658,4 +683,22 @@ public class ClientLoader implements Supplier verifyJarEntry(je, chains); } } + + private static class OutageException extends RuntimeException + { + private OutageException(Throwable cause) + { + super(cause); + } + } + + private boolean checkOutages() + { + RuntimeConfig rtc = runtimeConfigLoader.tryGet(); + if (rtc != null) + { + return rtc.showOutageMessage(); + } + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java b/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java index a3a2a0f14a..5c2c9f5da9 100644 --- a/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java +++ b/runelite-client/src/main/java/net/runelite/client/rs/RSAppletStub.java @@ -25,16 +25,28 @@ */ package net.runelite.client.rs; +import java.applet.Applet; import java.applet.AppletContext; import java.applet.AppletStub; +import java.applet.AudioClip; +import java.awt.Image; +import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.util.Enumeration; +import java.util.Iterator; +import javax.swing.SwingUtilities; import lombok.RequiredArgsConstructor; +import net.runelite.client.RuntimeConfig; +import net.runelite.client.RuntimeConfigLoader; +import net.runelite.client.ui.FatalErrorDialog; @RequiredArgsConstructor class RSAppletStub implements AppletStub { private final RSConfig config; + private final RuntimeConfigLoader runtimeConfigLoader; @Override public boolean isActive() @@ -70,7 +82,89 @@ class RSAppletStub implements AppletStub @Override public AppletContext getAppletContext() { - return null; + return new AppletContext() + { + @Override + public AudioClip getAudioClip(URL url) + { + return null; + } + + @Override + public Image getImage(URL url) + { + return null; + } + + @Override + public Applet getApplet(String name) + { + return null; + } + + @Override + public Enumeration getApplets() + { + return null; + } + + @Override + public void showDocument(URL url) + { + if (url.getPath().startsWith("/error_game_")) + { + try + { + RuntimeConfig rtc = runtimeConfigLoader.get(); + if (rtc.showOutageMessage()) + { + return; + } + } + catch (Exception e) + { + } + + String code = url.getPath() + .replace("/", "") + .replace(".ws", ""); + + SwingUtilities.invokeLater(() -> + new FatalErrorDialog("OldSchool RuneScape has crashed with the message: " + code + "") + .setTitle("OpenOSRS", "OldSchool RuneScape has crashed") + .addHelpButtons() + .open()); + } + } + + @Override + public void showDocument(URL url, String target) + { + showDocument(url); + } + + @Override + public void showStatus(String status) + { + } + + @Override + public void setStream(String key, InputStream stream) throws IOException + { + } + + @Override + public InputStream getStream(String key) + { + return null; + } + + @Override + public Iterator getStreamKeys() + { + return null; + } + }; } @Override diff --git a/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java b/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java index 71fbebf817..96fa2c9aa5 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/FatalErrorDialog.java @@ -61,6 +61,7 @@ public class FatalErrorDialog extends JDialog private final JPanel rightColumn = new JPanel(); private final Font font = new Font(Font.DIALOG, Font.PLAIN, 12); + private final JLabel title; public FatalErrorDialog(String message) { @@ -114,7 +115,7 @@ public class FatalErrorDialog extends JDialog leftPane.setBackground(ColorScheme.DARKER_GRAY_COLOR); leftPane.setLayout(new BorderLayout()); - JLabel title = new JLabel("There was a fatal error starting OpenOSRS"); + title = new JLabel("There was a fatal error starting OpenOSRS"); title.setForeground(Color.WHITE); title.setFont(font.deriveFont(16.f)); title.setBorder(new EmptyBorder(10, 10, 10, 10)); @@ -137,13 +138,6 @@ public class FatalErrorDialog extends JDialog rightColumn.setBackground(ColorScheme.DARK_GRAY_COLOR); rightColumn.setMaximumSize(new Dimension(200, Integer.MAX_VALUE)); - addButton("Open logs folder", () -> - { - LinkBrowser.open(RuneLite.LOGS_DIR.toString()); - }); - addButton("Get help on Discord", () -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite())); - addButton("Troubleshooting steps", () -> LinkBrowser.browse(RuneLiteProperties.getTroubleshootingLink())); - pane.add(rightColumn, BorderLayout.EAST); } @@ -193,6 +187,20 @@ public class FatalErrorDialog extends JDialog return this; } + public FatalErrorDialog setTitle(String windowTitle, String header) + { + super.setTitle(windowTitle); + title.setText(header); + return this; + } + + public FatalErrorDialog addHelpButtons() + { + return addButton("Open logs folder", () -> LinkBrowser.open(RuneLite.LOGS_DIR.toString())) + .addButton("Get help on Discord", () -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite())) + .addButton("Troubleshooting steps", () -> LinkBrowser.browse(RuneLiteProperties.getTroubleshootingLink())); + } + public FatalErrorDialog addBuildingGuide() { return addButton("Building guide", () -> LinkBrowser.browse(RuneLiteProperties.getBuildingLink())); @@ -205,6 +213,7 @@ public class FatalErrorDialog extends JDialog new FatalErrorDialog("OpenOSRS was unable to verify the security of its connection to the internet while " + action + ". You may have a misbehaving antivirus, internet service provider, a proxy, or an incomplete" + " java installation.") + .addHelpButtons() .open(); return; } @@ -213,6 +222,7 @@ public class FatalErrorDialog extends JDialog { new FatalErrorDialog("OpenOSRS is unable to connect to a required server while " + action + ". " + "Please check your internet connection") + .addHelpButtons() .open(); return; } @@ -222,11 +232,14 @@ public class FatalErrorDialog extends JDialog new FatalErrorDialog("OpenOSRS is unable to resolve the address of a required server while " + action + ". " + "Your DNS resolver may be misconfigured, pointing to an inaccurate resolver, or your internet connection may " + "be down. ") + .addHelpButtons() .addButton("Change your DNS resolver", () -> LinkBrowser.browse(RuneLiteProperties.getDNSChangeLink())) .open(); return; } - new FatalErrorDialog("OpenOSRS encountered a fatal error while " + action + ".").open(); + new FatalErrorDialog("RuneLite encountered a fatal error while " + action + ".") + .addHelpButtons() + .open(); } } diff --git a/runelite-client/src/main/resources/net/runelite/client/runelite.properties b/runelite-client/src/main/resources/net/runelite/client/runelite.properties index 0f6633c222..576a15c374 100644 --- a/runelite-client/src/main/resources/net/runelite/client/runelite.properties +++ b/runelite-client/src/main/resources/net/runelite/client/runelite.properties @@ -18,4 +18,5 @@ runelite.api.base=https://api.runelite.net/runelite-@project.version@ runelite.session=https://session.openosrs.dev runelite.static.base=https://static.runelite.net runelite.ws=https://api.runelite.net/ws -runelite.config=https://static.runelite.net/config.json \ No newline at end of file +runelite.config=https://static.runelite.net/config.json +runelite.osrstwitter.link=https://twitter.com/OldSchoolRS \ No newline at end of file diff --git a/runelite-client/src/main/scripts/ChatBuilder.rs2asm b/runelite-client/src/main/scripts/ChatBuilder.rs2asm index eeb6f602dd..ed476f8935 100644 --- a/runelite-client/src/main/scripts/ChatBuilder.rs2asm +++ b/runelite-client/src/main/scripts/ChatBuilder.rs2asm @@ -456,28 +456,28 @@ LABEL406: sstore 17 ; message channel iload 11 switch - 1: LABEL409 2: LABEL409 + 1: LABEL409 + 90: LABEL433 + 91: LABEL433 3: LABEL457 + 7: LABEL457 101: LABEL482 5: LABEL503 6: LABEL539 - 7: LABEL457 103: LABEL564 104: LABEL564 - 9: LABEL606 - 41: LABEL677 - 43: LABEL1061 - 107: LABEL1285 - 44: LABEL896 - 109: LABEL585 110: LABEL564 - 46: LABEL1200 - 14: LABEL1255 + 109: LABEL585 + 9: LABEL606 111: LABEL635 112: LABEL656 - 90: LABEL433 - 91: LABEL433 + 41: LABEL677 + 44: LABEL896 + 43: LABEL1061 + 46: LABEL1200 + 14: LABEL1255 + 107: LABEL1285 jump LABEL1324 LABEL409: sload 21 @@ -1481,22 +1481,22 @@ LABEL1341: 1: LABEL1346 2: LABEL1346 3: LABEL1346 - 101: LABEL1450 6: LABEL1346 7: LABEL1346 - 103: LABEL1493 - 104: LABEL1493 9: LABEL1346 - 41: LABEL1346 - 106: LABEL1346 - 44: LABEL1346 - 109: LABEL1596 - 110: LABEL1493 - 14: LABEL1536 - 111: LABEL1639 - 112: LABEL1682 90: LABEL1346 91: LABEL1346 + 106: LABEL1346 + 41: LABEL1346 + 44: LABEL1346 + 101: LABEL1450 + 103: LABEL1493 + 104: LABEL1493 + 110: LABEL1493 + 14: LABEL1536 + 109: LABEL1596 + 111: LABEL1639 + 112: LABEL1682 jump LABEL1725 LABEL1346: sconst "" diff --git a/runelite-client/src/main/scripts/ChatSplitBuilder.rs2asm b/runelite-client/src/main/scripts/ChatSplitBuilder.rs2asm index e68c16f9db..df7cef7dfa 100644 --- a/runelite-client/src/main/scripts/ChatSplitBuilder.rs2asm +++ b/runelite-client/src/main/scripts/ChatSplitBuilder.rs2asm @@ -430,9 +430,9 @@ LABEL369: iload 18 switch 3: LABEL372 - 5: LABEL430 - 6: LABEL401 7: LABEL372 + 6: LABEL401 + 5: LABEL430 jump LABEL468 LABEL372: iload 7 diff --git a/runelite-client/src/main/scripts/FriendUpdate.rs2asm b/runelite-client/src/main/scripts/FriendUpdate.rs2asm index d9a5ac3180..26d5d41094 100644 --- a/runelite-client/src/main/scripts/FriendUpdate.rs2asm +++ b/runelite-client/src/main/scripts/FriendUpdate.rs2asm @@ -192,10 +192,10 @@ LABEL157: 1: LABEL167 2: LABEL170 3: LABEL175 - 4: LABEL190 - 5: LABEL210 8: LABEL180 9: LABEL185 + 4: LABEL190 + 5: LABEL210 jump LABEL229 LABEL167: iconst 0 diff --git a/runelite-client/src/main/scripts/LayoutResizableStones.rs2asm b/runelite-client/src/main/scripts/LayoutResizableStones.rs2asm index de6ca3f0f8..6fdb3af703 100644 --- a/runelite-client/src/main/scripts/LayoutResizableStones.rs2asm +++ b/runelite-client/src/main/scripts/LayoutResizableStones.rs2asm @@ -14,10 +14,10 @@ istore 4 iload 1 switch - 1745: LABEL169 - 1129: LABEL149 - 1130: LABEL107 1131: LABEL9 + 1130: LABEL107 + 1129: LABEL149 + 1745: LABEL169 jump LABEL244 LABEL9: iconst 10747996 @@ -229,8 +229,8 @@ LABEL180: 2308 get_varbit 6255 switch - 1: LABEL197 2: LABEL189 + 1: LABEL197 3: LABEL205 jump LABEL213 LABEL189: diff --git a/runelite-client/src/main/scripts/PrivateMessage.rs2asm b/runelite-client/src/main/scripts/PrivateMessage.rs2asm index 4c9271aa4d..098eaa3f0b 100644 --- a/runelite-client/src/main/scripts/PrivateMessage.rs2asm +++ b/runelite-client/src/main/scripts/PrivateMessage.rs2asm @@ -30,24 +30,24 @@ LABEL21: get_varc_int 5 switch 1: LABEL24 - 2: LABEL47 - 3: LABEL47 4: LABEL26 5: LABEL26 + 2: LABEL47 + 3: LABEL47 6: LABEL47 7: LABEL111 + 19: LABEL111 8: LABEL117 9: LABEL125 - 10: LABEL202 - 11: LABEL260 - 12: LABEL219 - 13: LABEL237 15: LABEL125 - 16: LABEL266 - 18: LABEL260 - 19: LABEL111 20: LABEL125 21: LABEL125 + 10: LABEL202 + 12: LABEL219 + 13: LABEL237 + 11: LABEL260 + 18: LABEL260 + 16: LABEL266 jump LABEL268 LABEL24: return @@ -346,13 +346,13 @@ LABEL268: LABEL269: get_varc_int 5 switch - 16: LABEL274 - 20: LABEL272 - 21: LABEL272 7: LABEL272 8: LABEL272 9: LABEL272 15: LABEL272 + 20: LABEL272 + 21: LABEL272 + 16: LABEL274 jump LABEL275 LABEL272: return diff --git a/runelite-client/src/main/scripts/QuestFilter.hash b/runelite-client/src/main/scripts/QuestFilter.hash new file mode 100644 index 0000000000..8e30fb9e9e --- /dev/null +++ b/runelite-client/src/main/scripts/QuestFilter.hash @@ -0,0 +1 @@ +3FA5FFC8DB18A42971CED41A9BC7CEA407A0EC98061D56B2822F66CD910E4BAF \ No newline at end of file diff --git a/runelite-client/src/main/scripts/QuestFilter.rs2asm b/runelite-client/src/main/scripts/QuestFilter.rs2asm new file mode 100644 index 0000000000..15fd8f7a98 --- /dev/null +++ b/runelite-client/src/main/scripts/QuestFilter.rs2asm @@ -0,0 +1,129 @@ +.id 3238 +.int_stack_count 4 +.string_stack_count 0 +.int_var_count 5 ; +1 for filter result +.string_var_count 0 + iconst -1 ; set to 1 to hide, 0 to show + iload 0 ; quest struct + sconst "questFilter" + runelite_callback + pop_int ; quest struct + istore 4 ; save result + ; compare with -1 + iload 4 ; load result + iconst -1 + if_icmpeq continue + ; return value + iload 4 + return + +continue: + iload 0 + iconst 611 + struct_param + iconst 1 + if_icmpeq LABEL6 + jump LABEL12 +LABEL6: + invoke 4025 + iconst 1 + if_icmpeq LABEL10 + jump LABEL12 +LABEL10: + iconst 1 + return +LABEL12: + iload 0 + iconst 611 + struct_param + iconst 0 + if_icmpeq LABEL18 + jump LABEL24 +LABEL18: + get_varbit 13774 + iconst 1 + if_icmpeq LABEL22 + jump LABEL24 +LABEL22: + iconst 1 + return +LABEL24: + iload 1 + iconst 0 + if_icmpeq LABEL28 + jump LABEL34 +LABEL28: + get_varbit 13775 + iconst 1 + if_icmpeq LABEL32 + jump LABEL34 +LABEL32: + iconst 1 + return +LABEL34: + iload 1 + iconst 1 + if_icmpeq LABEL38 + jump LABEL44 +LABEL38: + get_varbit 13776 + iconst 1 + if_icmpeq LABEL42 + jump LABEL44 +LABEL42: + iconst 1 + return +LABEL44: + iload 1 + iconst 2 + if_icmpeq LABEL48 + jump LABEL54 +LABEL48: + get_varbit 13777 + iconst 1 + if_icmpeq LABEL52 + jump LABEL54 +LABEL52: + iconst 1 + return +LABEL54: + iload 1 + iconst 1 + if_icmpeq LABEL58 + jump LABEL68 +LABEL58: + get_varbit 13778 + iconst 2 + if_icmpeq LABEL62 + jump LABEL68 +LABEL62: + iload 2 + iconst 0 + if_icmpeq LABEL66 + jump LABEL68 +LABEL66: + iconst 1 + return +LABEL68: + iload 1 + iconst 1 + if_icmpeq LABEL72 + jump LABEL82 +LABEL72: + get_varbit 13779 + iconst 2 + if_icmpeq LABEL76 + jump LABEL82 +LABEL76: + iload 3 + iconst 0 + if_icmpeq LABEL80 + jump LABEL82 +LABEL80: + iconst 1 + return +LABEL82: + iconst 0 + return + iconst -1 + return diff --git a/runelite-client/src/main/scripts/SkillTabBuilder.rs2asm b/runelite-client/src/main/scripts/SkillTabBuilder.rs2asm index 87d1f1fa64..9ab40f1e80 100644 --- a/runelite-client/src/main/scripts/SkillTabBuilder.rs2asm +++ b/runelite-client/src/main/scripts/SkillTabBuilder.rs2asm @@ -166,10 +166,10 @@ LABEL141: iload 0 switch 0: LABEL144 - 1: LABEL234 2: LABEL144 - 4: LABEL189 6: LABEL144 + 4: LABEL189 + 1: LABEL234 jump LABEL278 LABEL144: iconst 20 diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java index 42d5b729c5..843158dd32 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java @@ -165,11 +165,16 @@ public class ChatCommandsPluginTest public void testTheatreOfBlood() { ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", - "Wave 'The Final Challenge' (Normal Mode) complete!
    Duration: 2:42.0
    Theatre of Blood wave completion time: 17:00.20 (new personal best)", null, 0); + "Wave 'The Final Challenge' (Normal Mode) complete!
    " + + "Duration: 2:42.0
    " + + "Theatre of Blood completion time: 17:00.20 (new personal best)", null, 0); chatCommandsPlugin.onChatMessage(chatMessage); - ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: 73.", null, 0); - chatCommandsPlugin.onChatMessage(chatMessageEvent); + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Theatre of Blood total completion time: 24:40.20. Personal best: 20:45.00", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: 73.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood", 73); verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood", 17 * 60 + .2); @@ -179,11 +184,16 @@ public class ChatCommandsPluginTest public void testTheatreOfBloodNoPb() { ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", - "Wave 'The Final Challenge' (Normal Mode) complete!
    Duration: 2:42
    Theatre of Blood wave completion time: 17:00. Personal best: 13:52.80", null, 0); + "Wave 'The Final Challenge' (Normal Mode) complete!
    " + + "Duration: 2:42
    " + + "Theatre of Blood completion time: 17:00. Personal best: 13:52.80", null, 0); chatCommandsPlugin.onChatMessage(chatMessage); - ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: 73.", null, 0); - chatCommandsPlugin.onChatMessage(chatMessageEvent); + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Theatre of Blood total completion time: 24:40.20. Personal best: 20:45.00", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: 73.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood", 73); verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood", 13 * 60 + 52.8); @@ -193,11 +203,16 @@ public class ChatCommandsPluginTest public void testTheatreOfBloodEntryMode() { ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", - "Wave 'The Final Challenge' (Entry Mode) complete!
    Duration: 2:42
    Theatre of Blood wave completion time: 17:00 (new personal best)", null, 0); + "Wave 'The Final Challenge' (Entry Mode) complete!
    " + + "Duration: 2:42
    " + + "Theatre of Blood completion time: 17:00 (new personal best)", null, 0); chatCommandsPlugin.onChatMessage(chatMessage); - ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood: Entry Mode count is: 73.", null, 0); - chatCommandsPlugin.onChatMessage(chatMessageEvent); + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Theatre of Blood total completion time: 24:40.20. Personal best: 20:45.00", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood: Entry Mode count is: 73.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood entry mode", 73); verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood entry mode", 17 * 60.); @@ -374,6 +389,46 @@ public class ChatCommandsPluginTest verify(configManager).setRSProfileConfiguration("personalbest", "prifddinas agility course", 61.2); } + @Test + public void testShayzienAdvancedAgilityLap() + { + // This sets lastBoss + ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your Shayzien Advanced Agility Course lap count is: 2.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Lap duration: 1:01 (new personal best).", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + verify(configManager).setRSProfileConfiguration("personalbest", "shayzien advanced agility course", 61.0); + verify(configManager).setRSProfileConfiguration("killcount", "shayzien advanced agility course", 2); + + // Precise times + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Lap duration: 1:01.20 (new personal best).", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + verify(configManager).setRSProfileConfiguration("personalbest", "shayzien advanced agility course", 61.2); + } + + @Test + public void testShayzienBasicAgilityLap() + { + // This sets lastBoss + ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your Shayzien Basic Agility Course lap count is: 2.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Lap duration: 1:01 (new personal best).", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + verify(configManager).setRSProfileConfiguration("personalbest", "shayzien basic agility course", 61.0); + verify(configManager).setRSProfileConfiguration("killcount", "shayzien basic agility course", 2); + + // Precise times + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Lap duration: 1:01.20 (new personal best).", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + verify(configManager).setRSProfileConfiguration("personalbest", "shayzien basic agility course", 61.2); + } + @Test public void testZukNewPb() { @@ -1074,4 +1129,13 @@ public class ChatCommandsPluginTest verify(configManager).setRSProfileConfiguration("personalbest", "tempoross", 234.0); verify(configManager).setRSProfileConfiguration("personalbest", "chambers of xeric", 1360.0); // the lowest time } + + @Test + public void testGuardiansOfTheRift() + { + ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Amount of Rifts you have closed: 7.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + verify(configManager).setRSProfileConfiguration("killcount", "guardians of the rift", 7); + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPluginTest.java index 4fa03023a1..f051cfaa2d 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPluginTest.java @@ -34,8 +34,10 @@ import net.runelite.api.GameState; import net.runelite.api.KeyCode; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; +import net.runelite.api.ObjectComposition; import net.runelite.api.events.ClientTick; import net.runelite.api.events.MenuEntryAdded; +import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.config.ConfigManager; import net.runelite.client.game.ItemManager; import net.runelite.client.menus.TestMenuEntry; @@ -45,10 +47,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import org.mockito.Mock; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -70,6 +74,10 @@ public class MenuEntrySwapperPluginTest @Bind ItemManager itemManager; + @Mock + @Bind + ChatMessageManager chatMessageManager; + @Mock @Bind MenuEntrySwapperConfig config; @@ -85,6 +93,7 @@ public class MenuEntrySwapperPluginTest Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); when(client.getGameState()).thenReturn(GameState.LOGGED_IN); + when(client.getObjectDefinition(anyInt())).thenReturn(mock(ObjectComposition.class)); when(client.getMenuEntries()).thenAnswer((Answer) invocationOnMock -> { diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingTrackerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingTrackerTest.java index f407fd0a34..f6ce2aef74 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingTrackerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingTrackerTest.java @@ -95,7 +95,7 @@ public class FarmingTrackerTest @Test(expected = IllegalStateException.class) public void testEmptyNotification() { - RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null); + RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, -1, null); PatchPrediction patchPrediction = new PatchPrediction(Produce.EMPTY_COMPOST_BIN, CropState.EMPTY, 0L, 0, 0); FarmingRegion region = new FarmingRegion("Ardougne", 10548, false, @@ -113,7 +113,7 @@ public class FarmingTrackerTest @Test public void testHarvestableNotification() { - RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null); + RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, -1, null); PatchPrediction patchPrediction = new PatchPrediction(Produce.RANARR, CropState.HARVESTABLE, 0L, 0, 0); FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,