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 cc642d246b..844d0e3dde 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..bab04ecb05 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,16 @@ 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; } \ 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..153d800e01 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; @@ -889,6 +890,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..b159733af6 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 @@ -521,6 +521,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/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 3d86d52ad9..7e41725bee 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 @@ -1970,6 +1964,7 @@ 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 @@ -2152,6 +2147,11 @@ 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); } 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/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..6003c5a1e9 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 @@ -826,4 +826,34 @@ 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; + } + + enum UnchargedCellsMode + { + TAKE, + TAKE_1, + TAKE_5, + TAKE_10 + } + + @ConfigItem( + keyName = "swapUnchargedCells", + name = "Uncharged Cells", + description = "Swap the take option for Uncharged Cells in Guardians of the Rift.", + section = objectSection + ) + default UnchargedCellsMode swapUnchargedCells() + { + return UnchargedCellsMode.TAKE; + } } 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..f9b539cf0f 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 @@ -71,6 +71,7 @@ import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfi import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfig.KaramjaGlovesMode; import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfig.MorytaniaLegsMode; import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfig.RadasBlessingMode; +import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfig.UnchargedCellsMode; import net.runelite.client.util.Text; @PluginDescriptor( @@ -419,6 +420,12 @@ 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); + + swap("take", "uncharged cells", "take-1", () -> config.swapUnchargedCells() == UnchargedCellsMode.TAKE_1); + swap("take", "uncharged cells", "take-5", () -> config.swapUnchargedCells() == UnchargedCellsMode.TAKE_5); + swap("take", "uncharged cells", "take-10", () -> config.swapUnchargedCells() == UnchargedCellsMode.TAKE_10); } public Swap swap(String option, String swappedOption, Supplier enabled) 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/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/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/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java index 42d5b729c5..a928a5709a 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.); @@ -1074,4 +1089,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/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,