Merge pull request #3152 from Owain94/upstream-3003

This commit is contained in:
Owain van Brakel
2022-04-06 23:36:17 +02:00
committed by GitHub
60 changed files with 1540 additions and 317 deletions

View File

@@ -25,9 +25,9 @@
object ProjectVersions { object ProjectVersions {
const val launcherVersion = "2.2.0" 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 rsversion = 204
const val cacheversion = 165 const val cacheversion = 165

View File

@@ -24,7 +24,7 @@
*/ */
package net.runelite.cache.definitions.loaders; package net.runelite.cache.definitions.loaders;
import java.util.HashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import net.runelite.cache.definitions.ScriptDefinition; import net.runelite.cache.definitions.ScriptDefinition;
import net.runelite.cache.io.InputStream; import net.runelite.cache.io.InputStream;
@@ -61,7 +61,7 @@ public class ScriptLoader
for (int i = 0; i < numSwitches; ++i) for (int i = 0; i < numSwitches; ++i)
{ {
switches[i] = new HashMap<>(); switches[i] = new LinkedHashMap<>();
int count = in.readUnsignedShort(); int count = in.readUnsignedShort();
while (count-- > 0) while (count-- > 0)

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2022, Adam <Adam@sigterm.info>
* 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();
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (c) 2022, Adam <Adam@sigterm.info>
* 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<OtlTokenResponse> request(URL url);
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (c) 2022, Adam <Adam@sigterm.info>
* 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();
}

View File

@@ -24,6 +24,7 @@
*/ */
package net.runelite.api; package net.runelite.api;
import com.jagex.oldscape.pub.OAuthApi;
import java.awt.Canvas; import java.awt.Canvas;
import java.awt.Dimension; import java.awt.Dimension;
import java.math.BigInteger; import java.math.BigInteger;
@@ -52,7 +53,7 @@ import org.intellij.lang.annotations.MagicConstant;
/** /**
* Represents the RuneScape client. * 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 * The injected client invokes these callbacks to send events to us
@@ -199,10 +200,13 @@ public interface Client extends GameEngine
void setWorldSelectOpen(boolean open); void setWorldSelectOpen(boolean open);
/** /**
* DEPRECATED. See getAccountHash instead.
* Gets the current logged in username. * Gets the current logged in username.
* *
* @return the logged in username * @return the logged in username
* @see OAuthApi#getAccountHash()
*/ */
@Deprecated
String getUsername(); String getUsername();
/** /**

View File

@@ -33,6 +33,7 @@ public final class ParamID
* Long name for NPCs used in the HP hud * Long name for NPCs used in the HP hud
*/ */
public static final int NPC_HP_NAME = 510; public static final int NPC_HP_NAME = 510;
public static final int QUEST_NAME = 610;
/** /**
* @see SettingID * @see SettingID
*/ */

View File

@@ -275,6 +275,12 @@ public final class ScriptID
@ScriptArguments(integer = 3) @ScriptArguments(integer = 3)
public static final int GE_ITEM_SEARCH = 752; 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 * Called when the friends list is updated
* <ul> * <ul>
@@ -428,4 +434,19 @@ public final class ScriptID
*/ */
@ScriptArguments(integer = 1) @ScriptArguments(integer = 1)
public static final int NOTIFICATION_DELAY = 3347; public static final int NOTIFICATION_DELAY = 3347;
/**
* Check if a quest should be filtered from the quest list
* <ul>
* <li> int (StructID) Quest struct </li>
* <li> int State filter </li>
* <li> int Requirement filter </li>
* <li> int Stats filter </li>
* </ul>
*/
@ScriptArguments(integer = 4)
public static final int QUEST_FILTER = 3238;
@ScriptArguments(integer = 18, string = 1)
public static final int GROUP_IRONMAN_STORAGE_BUILD = 5269;
} }

View File

@@ -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
{
}

View File

@@ -132,6 +132,7 @@ public final class WidgetID
public static final int SKOTIZO_GROUP_ID = 308; public static final int SKOTIZO_GROUP_ID = 308;
public static final int ENTERING_HOUSE_GROUP_ID = 71; public static final int ENTERING_HOUSE_GROUP_ID = 71;
public static final int FULLSCREEN_CONTAINER_TLI = 165; 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 SKILLS_GROUP_ID = 320;
public static final int MUSIC_GROUP_ID = 239; public static final int MUSIC_GROUP_ID = 239;
public static final int BARROWS_PUZZLE_GROUP_ID = 25; public static final int BARROWS_PUZZLE_GROUP_ID = 25;
@@ -310,6 +311,7 @@ public final class WidgetID
static class GroupStorage static class GroupStorage
{ {
static final int UI = 2;
static final int ITEM_CONTAINER = 10; static final int ITEM_CONTAINER = 10;
} }
@@ -889,6 +891,12 @@ public final class WidgetID
static final int CONTAINER = 2; static final int CONTAINER = 2;
} }
static class QuestList
{
static final int BOX = 0;
static final int CONTAINER = 2;
}
static class Music static class Music
{ {
static final int CONTAINER = 0; static final int CONTAINER = 0;

View File

@@ -153,6 +153,7 @@ public enum WidgetInfo
BANK_SETTINGS_BUTTON(WidgetID.BANK_GROUP_ID, WidgetID.Bank.SETTINGS_BUTTON), BANK_SETTINGS_BUTTON(WidgetID.BANK_GROUP_ID, WidgetID.Bank.SETTINGS_BUTTON),
BANK_TUTORIAL_BUTTON(WidgetID.BANK_GROUP_ID, WidgetID.Bank.TUTORIAL_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), 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), 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), 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_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_CONTAINER(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_CONTAINER),
SEED_VAULT_ITEM_TEXT(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_TEXT), SEED_VAULT_ITEM_TEXT(WidgetID.SEED_VAULT_GROUP_ID, WidgetID.SeedVault.ITEM_TEXT),

View File

@@ -27,9 +27,12 @@ package net.runelite.client;
import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.Logger;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.inject.Guice; import com.google.inject.Guice;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Injector; 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.OpenOSRS;
import com.openosrs.client.game.PlayerManager; import com.openosrs.client.game.PlayerManager;
import com.openosrs.client.ui.OpenOSRSSplashScreen; import com.openosrs.client.ui.OpenOSRSSplashScreen;
@@ -50,6 +53,7 @@ import java.security.SecureRandom;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Stream; 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.World;
import net.runelite.http.api.worlds.WorldResult; import net.runelite.http.api.worlds.WorldResult;
import okhttp3.Cache; import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@@ -120,6 +126,9 @@ public class RuneLite
@Inject @Inject
private net.runelite.client.plugins.PluginManager pluginManager; private net.runelite.client.plugins.PluginManager pluginManager;
@Inject
private OkHttpClient okHttpClient;
@Inject @Inject
private ExternalPluginManager externalPluginManager; private ExternalPluginManager externalPluginManager;
@@ -290,8 +299,8 @@ public class RuneLite
try try
{ {
final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), (String) options.valueOf("jav_config"));
final RuntimeConfigLoader runtimeConfigLoader = new RuntimeConfigLoader(okHttpClient); final RuntimeConfigLoader runtimeConfigLoader = new RuntimeConfigLoader(okHttpClient);
final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), runtimeConfigLoader, (String) options.valueOf("jav_config"));
new Thread(() -> new Thread(() ->
{ {
@@ -330,6 +339,7 @@ public class RuneLite
log.error("Failure during startup", e); log.error("Failure during startup", e);
SwingUtilities.invokeLater(() -> SwingUtilities.invokeLater(() ->
new FatalErrorDialog("OpenOSRS has encountered an unexpected error during startup.") new FatalErrorDialog("OpenOSRS has encountered an unexpected error during startup.")
.addHelpButtons()
.open()); .open());
} }
finally finally
@@ -371,6 +381,11 @@ public class RuneLite
} }
applet.start(); applet.start();
if (applet instanceof OAuthApi)
{
setupJxAuth((OAuthApi) applet);
}
} }
SplashScreen.stage(.57, null, "Loading configuration"); SplashScreen.stage(.57, null, "Loading configuration");
@@ -661,4 +676,83 @@ public class RuneLite
System.setProperty(key, value); 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<OtlTokenResponse> 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);
}
}
} }

View File

@@ -50,6 +50,7 @@ public class RuneLiteProperties
private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version"; private static final String PLUGINHUB_VERSION = "runelite.pluginhub.version";
private static final String API_BASE = "runelite.api.base"; private static final String API_BASE = "runelite.api.base";
private static final String RUNELITE_CONFIG = "runelite.config"; private static final String RUNELITE_CONFIG = "runelite.config";
private static final String OSRS_TWITTER_LINK = "runelite.osrstwitter.link";
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private static final Properties properties = new Properties(); private static final Properties properties = new Properties();
@@ -148,4 +149,9 @@ public class RuneLiteProperties
{ {
return properties.getProperty(RUNELITE_CONFIG); return properties.getProperty(RUNELITE_CONFIG);
} }
public static String getOSRSTwitterLink()
{
return properties.getProperty(OSRS_TWITTER_LINK);
}
} }

View File

@@ -24,13 +24,46 @@
*/ */
package net.runelite.client; package net.runelite.client;
import com.google.common.base.Strings;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import javax.swing.SwingUtilities;
import lombok.Data; import lombok.Data;
import net.runelite.client.ui.FatalErrorDialog;
import net.runelite.client.util.LinkBrowser;
@Data @Data
public class RuntimeConfig public class RuntimeConfig
{ {
private Map<String, ?> props = Collections.emptyMap(); private Map<String, ?> props = Collections.emptyMap();
private Map<String, String> sysProps = Collections.emptyMap(); private Map<String, String> sysProps = Collections.emptyMap();
private String outageMessage;
private Map<String, String> outageLinks;
public boolean showOutageMessage()
{
if (Strings.isNullOrEmpty(getOutageMessage()))
{
return false;
}
SwingUtilities.invokeLater(() ->
{
FatalErrorDialog fed = new FatalErrorDialog(getOutageMessage());
if (getOutageLinks() != null)
{
for (Map.Entry<String, String> e : getOutageLinks().entrySet())
{
fed.addButton(e.getKey(), () -> LinkBrowser.browse(e.getValue()));
}
}
else
{
fed.addButton("OSRS Twitter", () -> LinkBrowser.browse(RuneLiteProperties.getOSRSTwitterLink()));
}
fed.open();
});
return true;
}
} }

View File

@@ -31,7 +31,10 @@ import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier; import java.util.function.Supplier;
import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
import okhttp3.Call; import okhttp3.Call;
@@ -41,7 +44,7 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@Slf4j @Slf4j
class RuntimeConfigLoader implements Supplier<RuntimeConfig> public class RuntimeConfigLoader implements Supplier<RuntimeConfig>
{ {
private final OkHttpClient okHttpClient; private final OkHttpClient okHttpClient;
private final CompletableFuture<RuntimeConfig> configFuture; private final CompletableFuture<RuntimeConfig> configFuture;
@@ -66,6 +69,19 @@ class RuntimeConfigLoader implements Supplier<RuntimeConfig>
} }
} }
@Nullable
public RuntimeConfig tryGet()
{
try
{
return configFuture.get(0, TimeUnit.SECONDS);
}
catch (InterruptedException | ExecutionException | TimeoutException e)
{
return null;
}
}
private CompletableFuture<RuntimeConfig> fetch() private CompletableFuture<RuntimeConfig> fetch()
{ {
CompletableFuture<RuntimeConfig> future = new CompletableFuture<>(); CompletableFuture<RuntimeConfig> future = new CompletableFuture<>();

View File

@@ -121,7 +121,7 @@ public class ClientThread implements Executor
} }
catch (Throwable e) catch (Throwable e)
{ {
log.warn("Exception in invoke", e); log.error("Exception in invoke", e);
} }
if (remove) if (remove)
{ {

View File

@@ -88,6 +88,7 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.Player; import net.runelite.api.Player;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
import net.runelite.api.events.AccountHashChanged;
import net.runelite.api.events.PlayerChanged; import net.runelite.api.events.PlayerChanged;
import net.runelite.api.events.UsernameChanged; import net.runelite.api.events.UsernameChanged;
import net.runelite.api.events.WorldChanged; 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_TYPE = "type";
private static final String RSPROFILE_LOGIN_HASH = "loginHash"; private static final String RSPROFILE_LOGIN_HASH = "loginHash";
private static final String RSPROFILE_LOGIN_SALT = "loginSalt"; 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"); private static final DateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
@@ -544,17 +546,17 @@ public class ConfigManager
displayName = p.getName(); displayName = p.getName();
} }
String username = client.getUsername(); RuneScapeProfile prof = findRSProfile(getRSProfiles(), RuneScapeProfileType.getCurrent(client), displayName, true);
if (Strings.isNullOrEmpty(username)) 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; return;
} }
RuneScapeProfile prof = findRSProfile(getRSProfiles(), username, RuneScapeProfileType.getCurrent(client), displayName, true);
rsProfileKey = prof.getKey(); rsProfileKey = prof.getKey();
this.rsProfileKey = rsProfileKey; this.rsProfileKey = rsProfileKey;
log.debug("RS profile changed to {}", rsProfileKey);
eventBus.post(new RuneScapeProfileChanged()); eventBus.post(new RuneScapeProfileChanged());
} }
setConfiguration(groupName, rsProfileKey, key, value); setConfiguration(groupName, rsProfileKey, key, value);
@@ -790,6 +792,10 @@ public class ConfigManager
{ {
return Integer.parseInt(str); return Integer.parseInt(str);
} }
if (type == long.class || type == Long.class)
{
return Long.parseLong(str);
}
if (type == double.class || type == Double.class) if (type == double.class || type == Double.class)
{ {
return Double.parseDouble(str); return Double.parseDouble(str);
@@ -1129,10 +1135,12 @@ public class ConfigManager
return profileKeys.stream() return profileKeys.stream()
.map(key -> .map(key ->
{ {
Long accid = getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_ACCOUNT_HASH, long.class);
RuneScapeProfile prof = new RuneScapeProfile( RuneScapeProfile prof = new RuneScapeProfile(
getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_DISPLAY_NAME), getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_DISPLAY_NAME),
getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_TYPE, RuneScapeProfileType.class), getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_TYPE, RuneScapeProfileType.class),
getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_LOGIN_HASH, byte[].class), getConfiguration(RSPROFILE_GROUP, key, RSPROFILE_LOGIN_HASH, byte[].class),
accid == null ? RuneScapeProfile.ACCOUNT_HASH_INVALID : accid,
key key
); );
@@ -1141,26 +1149,54 @@ public class ConfigManager
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private synchronized RuneScapeProfile findRSProfile(List<RuneScapeProfile> profiles, String username, RuneScapeProfileType type, String displayName, boolean create) private synchronized RuneScapeProfile findRSProfile(List<RuneScapeProfile> profiles, RuneScapeProfileType type, String displayName, boolean create)
{ {
byte[] salt = getConfiguration(RSPROFILE_GROUP, RSPROFILE_LOGIN_SALT, byte[].class); String username = client.getUsername();
if (salt == null) long accountHash = client.getAccountHash();
if (accountHash == RuneScapeProfile.ACCOUNT_HASH_INVALID && username == null)
{ {
salt = new byte[15]; return null;
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);
} }
Hasher h = Hashing.sha512().newHasher(); final byte[] loginHash;
h.putBytes(salt); byte[] salt = null;
h.putString(username.toLowerCase(Locale.US), StandardCharsets.UTF_8); if (username != null)
byte[] loginHash = h.hash().asBytes(); {
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<RuneScapeProfile> matches = profiles.stream() Hasher h = Hashing.sha512().newHasher();
.filter(p -> Arrays.equals(p.getLoginHash(), loginHash) && p.getType() == type) h.putBytes(salt);
h.putString(username.toLowerCase(Locale.US), StandardCharsets.UTF_8);
loginHash = h.hash().asBytes();
}
else
{
loginHash = null;
}
Set<RuneScapeProfile> matches = Collections.emptySet();
if (accountHash != RuneScapeProfile.ACCOUNT_HASH_INVALID)
{
matches = profiles.stream()
.filter(p -> p.getType() == type && accountHash == p.getAccountHash())
.collect(Collectors.toSet()); .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) if (matches.size() > 1)
{ {
@@ -1169,7 +1205,21 @@ public class ConfigManager
if (matches.size() >= 1) 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) 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 // generate the new key deterministically so if you "create" the same profile on 2 different clients it doesn't duplicate
Set<String> keys = profiles.stream().map(RuneScapeProfile::getKey).collect(Collectors.toSet()); Set<String> 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(); key[0] += type.ordinal();
for (int i = 0; i < 0xFF; i++, key[1]++) for (int i = 0; i < 0xFF; i++, key[1]++)
{ {
String keyStr = RSPROFILE_GROUP + "." + Base64.getUrlEncoder().encodeToString(key); String keyStr = RSPROFILE_GROUP + "." + Base64.getUrlEncoder().encodeToString(key);
if (!keys.contains(keyStr)) 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); setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_TYPE, type);
if (displayName != null) if (displayName != null)
{ {
setConfiguration(RSPROFILE_GROUP, keyStr, RSPROFILE_DISPLAY_NAME, displayName); 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"); throw new RuntimeException("too many rs profiles");
@@ -1208,7 +1277,7 @@ public class ConfigManager
} }
List<RuneScapeProfile> profiles = getRSProfiles(); List<RuneScapeProfile> 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(); String key = prof == null ? null : prof.getKey();
if (Objects.equals(key, rsProfileKey)) if (Objects.equals(key, rsProfileKey))
@@ -1217,6 +1286,7 @@ public class ConfigManager
} }
rsProfileKey = key; rsProfileKey = key;
log.debug("RS profile changed to {}", key);
eventBus.post(new RuneScapeProfileChanged()); eventBus.post(new RuneScapeProfileChanged());
} }
@@ -1226,6 +1296,12 @@ public class ConfigManager
updateRSProfile(); updateRSProfile();
} }
@Subscribe
private void onAccountHashChanged(AccountHashChanged ev)
{
updateRSProfile();
}
@Subscribe @Subscribe
private void onWorldChanged(WorldChanged ev) private void onWorldChanged(WorldChanged ev)
{ {

View File

@@ -33,9 +33,12 @@ import lombok.Data;
@Data @Data
public class RuneScapeProfile public class RuneScapeProfile
{ {
public static final int ACCOUNT_HASH_INVALID = -1;
private final String displayName; private final String displayName;
private final RuneScapeProfileType type; private final RuneScapeProfileType type;
private final byte[] loginHash; private final byte[] loginHash;
private final long accountHash;
/** /**
* Profile key used to save configs for this profile to the config store. This will * Profile key used to save configs for this profile to the config store. This will

View File

@@ -94,7 +94,7 @@ public enum HiscoreSkill
KALPHITE_QUEEN("Kalphite Queen", BOSS), KALPHITE_QUEEN("Kalphite Queen", BOSS),
KING_BLACK_DRAGON("King Black Dragon", BOSS), KING_BLACK_DRAGON("King Black Dragon", BOSS),
KRAKEN("Kraken", BOSS), KRAKEN("Kraken", BOSS),
KREEARRA("Kree'Arra", BOSS), KREEARRA("Kree'arra", BOSS),
KRIL_TSUTSAROTH("K'ril Tsutsaroth", BOSS), KRIL_TSUTSAROTH("K'ril Tsutsaroth", BOSS),
MIMIC("Mimic", BOSS), MIMIC("Mimic", BOSS),
NEX("Nex", BOSS), NEX("Nex", BOSS),

View File

@@ -264,32 +264,14 @@ public class BankPlugin extends Plugin
{ {
if (event.getScriptId() == ScriptID.BANKMAIN_BUILD) if (event.getScriptId() == ScriptID.BANKMAIN_BUILD)
{ {
// Compute bank prices using only the shown items so that we can show bank value during searches ContainerPrices price = getWidgetContainerPrices(WidgetInfo.BANK_ITEM_CONTAINER, InventoryID.BANK);
final Widget bankItemContainer = client.getWidget(WidgetInfo.BANK_ITEM_CONTAINER); if (price == null)
final ItemContainer bankContainer = client.getItemContainer(InventoryID.BANK);
final Widget[] children = bankItemContainer.getChildren();
long geTotal = 0, haTotal = 0;
if (bankContainer != null && children != null)
{ {
log.debug("Computing bank price of {} items", bankContainer.size()); return;
// 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));
} }
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) else if (event.getScriptId() == ScriptID.BANKMAIN_SEARCH_REFRESH)
{ {
@@ -302,6 +284,17 @@ public class BankPlugin extends Plugin
searchString = inputText; 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 @Subscribe
@@ -537,4 +530,35 @@ public class BankPlugin extends Plugin
return itemManager.getItemComposition(itemId).getHaPrice(); 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;
}
} }

View File

@@ -54,7 +54,7 @@ public interface BoostsConfig extends Config
) )
default DisplayBoosts displayBoosts() default DisplayBoosts displayBoosts()
{ {
return DisplayBoosts.BOTH; return DisplayBoosts.COMBAT;
} }
@ConfigItem( @ConfigItem(
@@ -76,7 +76,7 @@ public interface BoostsConfig extends Config
) )
default boolean displayInfoboxes() default boolean displayInfoboxes()
{ {
return false; return true;
} }
@ConfigItem( @ConfigItem(

View File

@@ -110,10 +110,8 @@ public class ChatCommandsPlugin extends Plugin
private static final String TEAM_SIZES = "(?:\\d+(?:\\+|-\\d+)? players|Solo)"; private static final String TEAM_SIZES = "(?:\\d+(?:\\+|-\\d+)? players|Solo)";
private static final Pattern RAIDS_PB_PATTERN = Pattern.compile("<col=ef20ff>Congratulations - your raid is complete!</col><br>Team size: <col=ff0000>" + TEAM_SIZES + "</col> Duration:</col> <col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col> \\(new personal best\\)</col>"); private static final Pattern RAIDS_PB_PATTERN = Pattern.compile("<col=ef20ff>Congratulations - your raid is complete!</col><br>Team size: <col=ff0000>" + TEAM_SIZES + "</col> Duration:</col> <col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col> \\(new personal best\\)</col>");
private static final Pattern RAIDS_DURATION_PATTERN = Pattern.compile("<col=ef20ff>Congratulations - your raid is complete!</col><br>Team size: <col=ff0000>" + TEAM_SIZES + "</col> Duration:</col> <col=ff0000>[0-9:.]+</col> Personal best: </col><col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col>"); private static final Pattern RAIDS_DURATION_PATTERN = Pattern.compile("<col=ef20ff>Congratulations - your raid is complete!</col><br>Team size: <col=ff0000>" + TEAM_SIZES + "</col> Duration:</col> <col=ff0000>[0-9:.]+</col> Personal best: </col><col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col>");
private static final Pattern TOB_WAVE_PB_PATTERN = Pattern.compile("Theatre of Blood wave completion time: <col=ff0000>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col> \\(new personal best\\)"); private static final Pattern KILL_DURATION_PATTERN = Pattern.compile("(?i)(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in|(?<!total )completion time:) <col=[0-9a-f]{6}>[0-9:.]+</col>\\. Personal best: (?:<col=ff0000>)?(?<pb>[0-9:]+(?:\\.[0-9]+)?)");
private static final Pattern TOB_WAVE_DURATION_PATTERN = Pattern.compile("Theatre of Blood wave completion time: <col=ff0000>[0-9:.]+</col>\\. Personal best: (?<pb>[0-9:]+(?:\\.[0-9]+)?)"); private static final Pattern NEW_PB_PATTERN = Pattern.compile("(?i)(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in|(?<!total )completion time:) <col=[0-9a-f]{6}>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col> \\(new personal best\\)");
private static final Pattern KILL_DURATION_PATTERN = Pattern.compile("(?i)(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in) <col=[0-9a-f]{6}>[0-9:.]+</col>\\. Personal best: (?:<col=ff0000>)?(?<pb>[0-9:]+(?:\\.[0-9]+)?)");
private static final Pattern NEW_PB_PATTERN = Pattern.compile("(?i)(?:(?:Fight |Lap |Challenge |Corrupted challenge )?duration:|Subdued in) <col=[0-9a-f]{6}>(?<pb>[0-9:]+(?:\\.[0-9]+)?)</col> \\(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_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 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 (.+)"); 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: <col=ff0000>([0-9,]+)</col>\\."); private static final Pattern HS_KC_FLOOR_PATTERN = Pattern.compile("You have completed Floor (\\d) of the Hallowed Sepulchre! Total completions: <col=ff0000>([0-9,]+)</col>\\.");
private static final Pattern HS_KC_GHC_PATTERN = Pattern.compile("You have opened the Grand Hallowed Coffin <col=ff0000>([0-9,]+)</col> times?!"); private static final Pattern HS_KC_GHC_PATTERN = Pattern.compile("You have opened the Grand Hallowed Coffin <col=ff0000>([0-9,]+)</col> times?!");
private static final Pattern COLLECTION_LOG_ITEM_PATTERN = Pattern.compile("New item added to your collection log: (.*)"); 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: <col=ff0000>([0-9,]+)</col>.");
private static final String TOTAL_LEVEL_COMMAND_STRING = "!total"; private static final String TOTAL_LEVEL_COMMAND_STRING = "!total";
private static final String PRICE_COMMAND_STRING = "!price"; private static final String PRICE_COMMAND_STRING = "!price";
@@ -469,18 +468,6 @@ public class ChatCommandsPlugin extends Plugin
matchPb(matcher); 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); matcher = HS_PB_PATTERN.matcher(message);
if (matcher.find()) 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 @VisibleForTesting
@@ -1345,13 +1339,8 @@ public class ChatCommandsPlugin extends Plugin
search = message.substring(LEVEL_COMMAND_STRING.length() + 1); search = message.substring(LEVEL_COMMAND_STRING.length() + 1);
} }
search = SkillAbbreviations.getFullName(search); final HiscoreSkill skill = findHiscoreSkill(search);
final HiscoreSkill skill; if (skill == null)
try
{
skill = HiscoreSkill.valueOf(search.toUpperCase());
}
catch (IllegalArgumentException i)
{ {
return; return;
} }
@@ -1970,13 +1959,9 @@ public class ChatCommandsPlugin extends Plugin
case "tob hm": case "tob hm":
case "tob hard mode": case "tob hard mode":
case "tob hard": case "tob hard":
case "hmt":
return "Theatre of Blood Hard Mode"; return "Theatre of Blood Hard Mode";
// agility course
case "prif":
case "prifddinas":
return "Prifddinas Agility Course";
// The Gauntlet // The Gauntlet
case "gaunt": case "gaunt":
case "gauntlet": case "gauntlet":
@@ -2027,6 +2012,26 @@ public class ChatCommandsPlugin extends Plugin
case "hs 5": case "hs 5":
return "Hallowed Sepulchre Floor 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 // Ape Atoll Agility
case "aa": case "aa":
case "ape atoll": case "ape atoll":
@@ -2152,8 +2157,103 @@ public class ChatCommandsPlugin extends Plugin
case "jad 6": case "jad 6":
return "TzHaar-Ket-Rak's Sixth Challenge"; return "TzHaar-Ket-Rak's Sixth Challenge";
// Guardians of the Rift
case "gotr":
case "runetodt":
return "Guardians of the Rift";
default: default:
return WordUtils.capitalize(boss); 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;
}
} }

View File

@@ -1,92 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* 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<String, String> MAP;
static
{
ImmutableMap.Builder<String, String> 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);
}
}

View File

@@ -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("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("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("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("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("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"), new CipherClue("GBJSZ RVFFO", "Fairy Queen", new WorldPoint(2347, 4435, 0), "Fairy Resistance Hideout"),

View File

@@ -103,8 +103,8 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu
private static final List<EmoteClue> CLUES = ImmutableList.of( private static final List<EmoteClue> 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("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("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("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)), item(RUNE_CROSSBOW), item(CLIMBING_BOOTS), item(RING_OF_VISIBILITY)), 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("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 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)), 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)),

View File

@@ -52,7 +52,7 @@ public class FaloTheBardClue extends ClueScroll implements TextClueScroll, NpcCl
{ {
private static final List<FaloTheBardClue> CLUES = ImmutableList.of( private static final List<FaloTheBardClue> 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 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 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 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)), new FaloTheBardClue("A mark used to increase one's grace, found atop a seer's place.", item(MARK_OF_GRACE)),

View File

@@ -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_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_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_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_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_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), ZEAH_DAIRY_COW(MASTER, new WorldPoint(1324, 3722, 0), ZEAH, "North-east of the Kebos Lowlands, east of the dairy cow.", BRASSICAN_MAGE),

View File

@@ -312,6 +312,7 @@ enum DiscordGameEventType
MG_TZHAAR_FIGHT_PITS("Tzhaar Fight Pits", DiscordAreaType.MINIGAMES, 9552), MG_TZHAAR_FIGHT_PITS("Tzhaar Fight Pits", DiscordAreaType.MINIGAMES, 9552),
MG_VARROCK_RAT_PITS("Varrock Rat Pits", DiscordAreaType.MINIGAMES, 11599), MG_VARROCK_RAT_PITS("Varrock Rat Pits", DiscordAreaType.MINIGAMES, 11599),
MG_VOLCANIC_MINE("Volcanic Mine", DiscordAreaType.MINIGAMES, 15263, 15262), MG_VOLCANIC_MINE("Volcanic Mine", DiscordAreaType.MINIGAMES, 15263, 15262),
MG_GUARDIANS_OF_THE_RIFT("Guardians of the Rift", DiscordAreaType.MINIGAMES, 14484),
// Raids // Raids
RAIDS_CHAMBERS_OF_XERIC("Chambers of Xeric", DiscordAreaType.RAIDS, 12889, 13136, 13137, 13138, 13139, 13140, 13141, 13145, 13393, 13394, 13395, 13396, 13397, 13401), RAIDS_CHAMBERS_OF_XERIC("Chambers of Xeric", DiscordAreaType.RAIDS, 12889, 13136, 13137, 13138, 13139, 13140, 13141, 13145, 13393, 13394, 13395, 13396, 13397, 13401),

View File

@@ -185,7 +185,7 @@ public class GrandExchangePlugin extends Plugin
private boolean wasFuzzySearch; private boolean wasFuzzySearch;
private String machineUuid; private String machineUuid;
private String lastUsername; private long lastAccount;
private int tradeSeq; private int tradeSeq;
/** /**
@@ -298,7 +298,8 @@ public class GrandExchangePlugin extends Plugin
clientToolbar.removeNavigation(button); clientToolbar.removeNavigation(button);
mouseManager.unregisterMouseListener(inputListener); mouseManager.unregisterMouseListener(inputListener);
keyManager.unregisterKeyListener(inputListener); keyManager.unregisterKeyListener(inputListener);
lastUsername = machineUuid = null; machineUuid = null;
lastAccount = -1L;
tradeSeq = 0; tradeSeq = 0;
} }
@@ -893,13 +894,13 @@ public class GrandExchangePlugin extends Plugin
private String getMachineUuid() private String getMachineUuid()
{ {
String username = client.getUsername(); long accountHash = client.getAccountHash();
if (lastUsername == username) if (lastAccount == accountHash)
{ {
return machineUuid; return machineUuid;
} }
lastUsername = username; lastAccount = accountHash;
try try
{ {
@@ -922,7 +923,7 @@ public class GrandExchangePlugin extends Plugin
hasher.putBytes(hardwareAddress); hasher.putBytes(hardwareAddress);
} }
} }
hasher.putUnencodedChars(username); hasher.putLong(accountHash);
machineUuid = hasher.hash().toString(); machineUuid = hasher.hash().toString();
tradeSeq = 0; tradeSeq = 0;
return machineUuid; return machineUuid;

View File

@@ -36,6 +36,7 @@ import net.runelite.api.Item;
import net.runelite.api.ItemComposition; import net.runelite.api.ItemComposition;
import net.runelite.api.ItemContainer; import net.runelite.api.ItemContainer;
import net.runelite.api.VarClientInt; import net.runelite.api.VarClientInt;
import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.overlay.OverlayPanel; import net.runelite.client.ui.overlay.OverlayPanel;
import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.OverlayPosition;
@@ -75,7 +76,8 @@ class InventoryViewerOverlay extends OverlayPanel
return null; 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; return null;
} }

View File

@@ -83,7 +83,7 @@ public class ItemChargePlugin extends Plugin
private static final Pattern BINDING_CHECK_PATTERN = Pattern.compile( private static final Pattern BINDING_CHECK_PATTERN = Pattern.compile(
"You have ([0-9]+|one) charges? left before your Binding necklace disintegrates\\."); "You have ([0-9]+|one) charges? left before your Binding necklace disintegrates\\.");
private static final Pattern BINDING_USED_PATTERN = Pattern.compile( 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 String BINDING_BREAK_TEXT = "Your Binding necklace has disintegrated.";
private static final Pattern RING_OF_FORGING_CHECK_PATTERN = Pattern.compile( 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\\."); "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()) 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()) else if (bindingNecklaceCheckMatcher.find())
{ {

View File

@@ -263,6 +263,12 @@ public class LootTrackerPlugin extends Plugin
private static final String TEMPOROSS_LOOT_STRING = "You found some loot: "; private static final String TEMPOROSS_LOOT_STRING = "You found some loot: ";
private static final int TEMPOROSS_REGION = 12588; 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 // Mahogany Homes
private static final String MAHOGANY_CRATE_EVENT = "Supply crate (Mahogany Homes)"; private static final String MAHOGANY_CRATE_EVENT = "Supply crate (Mahogany Homes)";
@@ -480,6 +486,13 @@ public class LootTrackerPlugin extends Plugin
clientThread.invokeLater(() -> 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 // convertToLootTrackerRecord must be called on client thread
List<LootTrackerRecord> records = loots.stream() List<LootTrackerRecord> records = loots.stream()
.map(this::convertToLootTrackerRecord) .map(this::convertToLootTrackerRecord)
@@ -489,6 +502,8 @@ public class LootTrackerPlugin extends Plugin
panel.clearRecords(); panel.clearRecords();
panel.addRecords(records); panel.addRecords(records);
}); });
return true;
}); });
}); });
} }
@@ -887,6 +902,12 @@ public class LootTrackerPlugin extends Plugin
return; 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)) if (message.equals(IMPLING_CATCH_MESSAGE))
{ {
onInvChange(collectInvItems(LootRecordType.EVENT, client.getLocalPlayer().getInteracting().getName())); onInvChange(collectInvItems(LootRecordType.EVENT, client.getLocalPlayer().getInteracting().getName()));
@@ -977,6 +998,9 @@ public class LootTrackerPlugin extends Plugin
case ItemID.CASKET_25590: case ItemID.CASKET_25590:
onInvChange(collectInvAndGroundItems(LootRecordType.EVENT, TEMPOROSS_CASKET_EVENT)); onInvChange(collectInvAndGroundItems(LootRecordType.EVENT, TEMPOROSS_CASKET_EVENT));
break; break;
case ItemID.INTRICATE_POUCH:
onInvChange(collectInvAndGroundItems(LootRecordType.EVENT, INTRICATE_POUCH_EVENT));
break;
case ItemID.SIMPLE_LOCKBOX_25647: case ItemID.SIMPLE_LOCKBOX_25647:
case ItemID.ELABORATE_LOCKBOX_25649: case ItemID.ELABORATE_LOCKBOX_25649:
case ItemID.ORNATE_LOCKBOX_25651: case ItemID.ORNATE_LOCKBOX_25651:

View File

@@ -137,6 +137,18 @@ public interface MenuEntrySwapperConfig extends Config
return true; 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( @ConfigItem(
keyName = "swapAdmire", keyName = "swapAdmire",
name = "Admire", name = "Admire",
@@ -826,4 +838,15 @@ public interface MenuEntrySwapperConfig extends Config
{ {
return false; 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;
}
} }

View File

@@ -29,7 +29,9 @@ package net.runelite.client.plugins.menuentryswapper;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import static com.google.common.base.Predicates.alwaysTrue; import static com.google.common.base.Predicates.alwaysTrue;
import static com.google.common.base.Predicates.equalTo; 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.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
@@ -43,6 +45,8 @@ import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.ItemComposition; import net.runelite.api.ItemComposition;
@@ -50,6 +54,7 @@ import net.runelite.api.KeyCode;
import net.runelite.api.MenuAction; import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry; import net.runelite.api.MenuEntry;
import net.runelite.api.NPC; import net.runelite.api.NPC;
import net.runelite.api.ObjectComposition;
import net.runelite.api.events.ClientTick; import net.runelite.api.events.ClientTick;
import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuEntryAdded;
import net.runelite.api.events.MenuOpened; 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.WidgetID;
import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetInfo;
import net.runelite.client.callback.ClientThread; 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.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.ConfigChanged;
@@ -79,6 +87,7 @@ import net.runelite.client.util.Text;
tags = {"npcs", "inventory", "items", "objects"}, tags = {"npcs", "inventory", "items", "objects"},
enabledByDefault = false enabledByDefault = false
) )
@Slf4j
public class MenuEntrySwapperPlugin extends Plugin public class MenuEntrySwapperPlugin extends Plugin
{ {
private static final String CONFIGURE = "Configure"; 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 SHIFTCLICK_CONFIG_GROUP = "shiftclick";
private static final String ITEM_KEY_PREFIX = "item_"; private static final String ITEM_KEY_PREFIX = "item_";
private static final String OBJECT_KEY_PREFIX = "object_";
// Shift click // Shift click
private static final WidgetMenuOption FIXED_INVENTORY_TAB_CONFIGURE_SC = new WidgetMenuOption(CONFIGURE, 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.NPC_FIFTH_OPTION,
MenuAction.EXAMINE_NPC); MenuAction.EXAMINE_NPC);
private static final List<MenuAction> 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<String> ESSENCE_MINE_NPCS = ImmutableSet.of( private static final Set<String> ESSENCE_MINE_NPCS = ImmutableSet.of(
"aubury", "aubury",
"sedridor", "sedridor",
@@ -172,6 +190,9 @@ public class MenuEntrySwapperPlugin extends Plugin
@Inject @Inject
private ItemManager itemManager; private ItemManager itemManager;
@Inject
private ChatMessageManager chatMessageManager;
private boolean configuringShiftClick = false; private boolean configuringShiftClick = false;
private boolean configuringLeftClick = 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-up", () -> (shiftModifier() ? config.swapStairsShiftClick() : config.swapStairsLeftClick()) == MenuEntrySwapperConfig.StairsMode.CLIMB_UP);
swap("climb", "climb-down", () -> (shiftModifier() ? config.swapStairsShiftClick() : config.swapStairsLeftClick()) == MenuEntrySwapperConfig.StairsMode.CLIMB_DOWN); 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<Boolean> enabled) public Swap swap(String option, String swappedOption, Supplier<Boolean> enabled)
@@ -475,7 +498,7 @@ public class MenuEntrySwapperPlugin extends Plugin
client.getItemCompositionCache().reset(); client.getItemCompositionCache().reset();
} }
private Integer getSwapConfig(boolean shift, int itemId) private Integer getItemSwapConfig(boolean shift, int itemId)
{ {
itemId = ItemVariationMapping.map(itemId); itemId = ItemVariationMapping.map(itemId);
String config = configManager.getConfiguration(shift ? SHIFTCLICK_CONFIG_GROUP : MenuEntrySwapperConfig.GROUP, ITEM_KEY_PREFIX + 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); 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); itemId = ItemVariationMapping.map(itemId);
configManager.setConfiguration(shift ? SHIFTCLICK_CONFIG_GROUP : MenuEntrySwapperConfig.GROUP, ITEM_KEY_PREFIX + itemId, index); 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); itemId = ItemVariationMapping.map(itemId);
configManager.unsetConfiguration(shift ? SHIFTCLICK_CONFIG_GROUP : MenuEntrySwapperConfig.GROUP, ITEM_KEY_PREFIX + 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) if (!configuringShiftClick && !configuringLeftClick)
{ {
configureObjectClick(event);
return; return;
} }
@@ -556,7 +580,7 @@ public class MenuEntrySwapperPlugin extends Plugin
else else
{ {
// Otherwise it is possible that we have Use swap configured // 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) if (config != null && config == -1)
{ {
activeAction = MenuAction.ITEM_USE; activeAction = MenuAction.ITEM_USE;
@@ -566,7 +590,7 @@ public class MenuEntrySwapperPlugin extends Plugin
else else
{ {
// Apply left click action from configuration // Apply left click action from configuration
Integer config = getSwapConfig(false, itemId); Integer config = getItemSwapConfig(false, itemId);
if (config != null) if (config != null)
{ {
activeAction = config >= 0 activeAction = config >= 0
@@ -589,7 +613,7 @@ public class MenuEntrySwapperPlugin extends Plugin
int index = menuAction == MenuAction.ITEM_USE int index = menuAction == MenuAction.ITEM_USE
? -1 ? -1
: menuAction.getId() - MenuAction.ITEM_FIRST_OPTION.getId(); : menuAction.getId() - MenuAction.ITEM_FIRST_OPTION.getId();
setSwapConfig(configuringShiftClick, itemId, index); setItemSwapConfig(configuringShiftClick, itemId, index);
}); });
if (activeAction == menuAction) if (activeAction == menuAction)
@@ -603,7 +627,81 @@ public class MenuEntrySwapperPlugin extends Plugin
.setOption(RESET) .setOption(RESET)
.setTarget(configuringShiftClick ? SHIFT_CLICK_MENU_TARGET : LEFT_CLICK_MENU_TARGET) .setTarget(configuringShiftClick ? SHIFT_CLICK_MENU_TARGET : LEFT_CLICK_MENU_TARGET)
.setType(MenuAction.RUNELITE) .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 @Subscribe
@@ -708,7 +806,7 @@ public class MenuEntrySwapperPlugin extends Plugin
// the client unable to perform the swap itself. // the client unable to perform the swap itself.
if (config.shiftClickCustomization() && !option.equals("use")) if (config.shiftClickCustomization() && !option.equals("use"))
{ {
Integer customOption = getSwapConfig(true, eventId); Integer customOption = getItemSwapConfig(true, eventId);
if (customOption != null && customOption == -1) if (customOption != null && customOption == -1)
{ {
@@ -724,7 +822,7 @@ public class MenuEntrySwapperPlugin extends Plugin
// Custom left-click item swap // Custom left-click item swap
if (itemOp) if (itemOp)
{ {
Integer swapIndex = getSwapConfig(false, eventId); Integer swapIndex = getItemSwapConfig(false, eventId);
if (swapIndex != null) if (swapIndex != null)
{ {
MenuAction swapAction = swapIndex >= 0 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 // Built-in swaps
Collection<Swap> swaps = this.swaps.get(option); Collection<Swap> swaps = this.swaps.get(option);
for (Swap swap : swaps) for (Swap swap : swaps)
@@ -793,7 +914,7 @@ public class MenuEntrySwapperPlugin extends Plugin
} }
ItemComposition itemComposition = event.getItemComposition(); ItemComposition itemComposition = event.getItemComposition();
Integer option = getSwapConfig(true, itemComposition.getId()); Integer option = getItemSwapConfig(true, itemComposition.getId());
if (option != null && option < itemComposition.getInventoryActions().length) if (option != null && option < itemComposition.getInventoryActions().length)
{ {
@@ -957,4 +1078,38 @@ public class MenuEntrySwapperPlugin extends Plugin
configuringLeftClick = target.equals(LEFT_CLICK_MENU_TARGET); configuringLeftClick = target.equals(LEFT_CLICK_MENU_TARGET);
rebuildCustomizationMenus(); 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;
}
} }

View File

@@ -42,7 +42,7 @@ class NpcAggroAreaNotWorkingOverlay extends OverlayPanel
this.plugin = plugin; this.plugin = plugin;
panelComponent.getChildren().add(LineComponent.builder() 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()); .build());
setPriority(OverlayPriority.LOW); setPriority(OverlayPriority.LOW);
@@ -53,7 +53,7 @@ class NpcAggroAreaNotWorkingOverlay extends OverlayPanel
@Override @Override
public Dimension render(Graphics2D graphics) public Dimension render(Graphics2D graphics)
{ {
if (!plugin.isActive() || plugin.getSafeCenters()[1] != null) if (plugin.getSafeCenters()[1] != null)
{ {
return null; return null;
} }

View File

@@ -0,0 +1,218 @@
/*
* Copyright (c) 2019 Spudjb <https://github.com/spudjb>
* Copyright (c) 2022 Adam <Adam@sigterm.info>
* 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));
}
}

View File

@@ -71,7 +71,7 @@ class AbyssMinimapOverlay extends Overlay
for (DecorativeObject object : plugin.getAbyssObjects()) for (DecorativeObject object : plugin.getAbyssObjects())
{ {
AbyssRifts rift = AbyssRifts.getRift(object.getId()); AbyssRifts rift = AbyssRifts.getRift(object.getId());
if (rift == null || !plugin.getRifts().contains(rift)) if (rift == null || !rift.getConfigEnabled().test(config))
{ {
continue; continue;
} }

View File

@@ -73,7 +73,7 @@ class AbyssOverlay extends Overlay
private void renderRift(Graphics2D graphics, DecorativeObject object) private void renderRift(Graphics2D graphics, DecorativeObject object)
{ {
AbyssRifts rift = AbyssRifts.getRift(object.getId()); AbyssRifts rift = AbyssRifts.getRift(object.getId());
if (rift == null || !plugin.getRifts().contains(rift)) if (rift == null || !rift.getConfigEnabled().test(config))
{ {
return; return;
} }

View File

@@ -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.NATURE_RUNE;
import static net.runelite.api.ItemID.SOUL_RUNE; import static net.runelite.api.ItemID.SOUL_RUNE;
import static net.runelite.api.ItemID.WATER_RUNE; import static net.runelite.api.ItemID.WATER_RUNE;
import net.runelite.api.NullObjectID;
import net.runelite.api.ObjectID; import net.runelite.api.ObjectID;
@AllArgsConstructor @AllArgsConstructor
enum AbyssRifts enum AbyssRifts
{ {
AIR_RIFT(ObjectID.AIR_RIFT, AIR_RUNE, RunecraftConfig::showAir), 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), BODY_RIFT(ObjectID.BODY_RIFT, BODY_RUNE, RunecraftConfig::showBody),
CHAOS_RIFT(ObjectID.CHAOS_RIFT, CHAOS_RUNE, RunecraftConfig::showChaos), CHAOS_RIFT(ObjectID.CHAOS_RIFT, CHAOS_RUNE, RunecraftConfig::showChaos),
COSMIC_RIFT(ObjectID.COSMIC_RIFT, COSMIC_RUNE, RunecraftConfig::showCosmic), COSMIC_RIFT(ObjectID.COSMIC_RIFT, COSMIC_RUNE, RunecraftConfig::showCosmic),

View File

@@ -27,7 +27,6 @@ package net.runelite.client.plugins.runecraft;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.awt.Color; import java.awt.Color;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@@ -52,7 +51,6 @@ import net.runelite.api.events.ItemContainerChanged;
import net.runelite.client.Notifier; import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe; 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.HighlightedNpc;
import net.runelite.client.game.npcoverlay.NpcOverlayService; import net.runelite.client.game.npcoverlay.NpcOverlayService;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
@@ -71,15 +69,13 @@ public class RunecraftPlugin extends Plugin
private static final List<Integer> DEGRADED_POUCHES = ImmutableList.of( private static final List<Integer> DEGRADED_POUCHES = ImmutableList.of(
ItemID.MEDIUM_POUCH_5511, ItemID.MEDIUM_POUCH_5511,
ItemID.LARGE_POUCH_5513, ItemID.LARGE_POUCH_5513,
ItemID.GIANT_POUCH_5515 ItemID.GIANT_POUCH_5515,
ItemID.COLOSSAL_POUCH_26786
); );
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private final Set<DecorativeObject> abyssObjects = new HashSet<>(); private final Set<DecorativeObject> abyssObjects = new HashSet<>();
@Getter(AccessLevel.PACKAGE)
private final Set<AbyssRifts> rifts = new HashSet<>();
private boolean degradedPouchInInventory; private boolean degradedPouchInInventory;
@Inject @Inject
@@ -114,7 +110,6 @@ public class RunecraftPlugin extends Plugin
npcOverlayService.registerHighlighter(highlightDarkMage); npcOverlayService.registerHighlighter(highlightDarkMage);
overlayManager.add(abyssOverlay); overlayManager.add(abyssOverlay);
overlayManager.add(abyssMinimapOverlay); overlayManager.add(abyssMinimapOverlay);
updateRifts();
} }
@Override @Override
@@ -127,15 +122,6 @@ public class RunecraftPlugin extends Plugin
degradedPouchInInventory = false; degradedPouchInInventory = false;
} }
@Subscribe
public void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals(RunecraftConfig.GROUP))
{
updateRifts();
}
}
@Subscribe @Subscribe
public void onChatMessage(ChatMessage event) public void onChatMessage(ChatMessage event)
{ {
@@ -205,12 +191,4 @@ public class RunecraftPlugin extends Plugin
} }
return null; return null;
} }
private void updateRifts()
{
rifts.clear();
Arrays.stream(AbyssRifts.values())
.filter(r -> r.getConfigEnabled().test(config))
.forEach(rifts::add);
}
} }

View File

@@ -155,6 +155,7 @@ class XpInfoBox extends JPanel
@Override @Override
public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent) public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent)
{ {
openXpTracker.setVisible(xpTrackerConfig.wiseOldManOpenOption());
canvasItem.setText(xpTrackerPlugin.hasOverlay(skill) ? REMOVE_STATE : ADD_STATE); canvasItem.setText(xpTrackerPlugin.hasOverlay(skill) ? REMOVE_STATE : ADD_STATE);
} }

View File

@@ -39,6 +39,8 @@ import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import net.runelite.api.Actor; import net.runelite.api.Actor;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.Skill; import net.runelite.api.Skill;
@@ -112,6 +114,24 @@ class XpPanel extends PluginPanel
popupMenu.add(resetPerHour); popupMenu.add(resetPerHour);
popupMenu.add(pauseAll); popupMenu.add(pauseAll);
popupMenu.add(unpauseAll); 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); overallPanel.setComponentPopupMenu(popupMenu);
final JLabel overallIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(Skill.OVERALL))); final JLabel overallIcon = new JLabel(new ImageIcon(iconManager.getSkillImage(Skill.OVERALL)));

View File

@@ -211,4 +211,15 @@ public interface XpTrackerConfig extends Config
{ {
return false; 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;
}
} }

View File

@@ -34,7 +34,6 @@ import java.awt.image.BufferedImage;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Setter; import lombok.Setter;
@@ -123,7 +122,7 @@ public class XpTrackerPlugin extends Plugin
@VisibleForTesting @VisibleForTesting
private XpPanel xpPanel; private XpPanel xpPanel;
private XpWorldType lastWorldType; private XpWorldType lastWorldType;
private String lastUsername; private long lastAccount;
private long lastTickMillis = 0; private long lastTickMillis = 0;
private boolean fetchXp; // fetch lastXp for the online xp tracker private boolean fetchXp; // fetch lastXp for the online xp tracker
private long lastXp = 0; private long lastXp = 0;
@@ -162,6 +161,7 @@ public class XpTrackerPlugin extends Plugin
// Initialize the tracker & last xp if already logged in // Initialize the tracker & last xp if already logged in
fetchXp = true; fetchXp = true;
initializeTracker = true; initializeTracker = true;
lastAccount = -1L;
} }
@Override @Override
@@ -182,15 +182,15 @@ public class XpTrackerPlugin extends Plugin
// Check that the username changed or the world type changed. // Check that the username changed or the world type changed.
XpWorldType type = worldSetToType(client.getWorldType()); XpWorldType type = worldSetToType(client.getWorldType());
if (!Objects.equals(client.getUsername(), lastUsername) || lastWorldType != type) if (client.getAccountHash() != lastAccount || lastWorldType != type)
{ {
// Reset // Reset
log.debug("World change: {} -> {}, {} -> {}", log.debug("World change: {} -> {}, {} -> {}",
lastUsername, client.getUsername(), lastAccount, client.getAccountHash(),
firstNonNull(lastWorldType, "<unknown>"), firstNonNull(lastWorldType, "<unknown>"),
firstNonNull(type, "<unknown>")); firstNonNull(type, "<unknown>"));
lastUsername = client.getUsername(); lastAccount = client.getAccountHash();
// xp is not available until after login is finished, so fetch it on the next gametick // xp is not available until after login is finished, so fetch it on the next gametick
fetchXp = true; fetchXp = true;
lastWorldType = type; lastWorldType = type;

View File

@@ -29,7 +29,6 @@ package net.runelite.client.plugins.xpupdater;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.io.IOException; import java.io.IOException;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
@@ -74,7 +73,7 @@ public class XpUpdaterPlugin extends Plugin
@Inject @Inject
private OkHttpClient okHttpClient; private OkHttpClient okHttpClient;
private String lastUsername; private long lastAccount;
private boolean fetchXp; private boolean fetchXp;
private long lastXp; private long lastXp;
@@ -88,6 +87,7 @@ public class XpUpdaterPlugin extends Plugin
protected void startUp() protected void startUp()
{ {
fetchXp = true; fetchXp = true;
lastAccount = -1L;
} }
@Subscribe @Subscribe
@@ -96,9 +96,9 @@ public class XpUpdaterPlugin extends Plugin
GameState state = gameStateChanged.getGameState(); GameState state = gameStateChanged.getGameState();
if (state == GameState.LOGGED_IN) if (state == GameState.LOGGED_IN)
{ {
if (!Objects.equals(client.getUsername(), lastUsername)) if (lastAccount != client.getAccountHash())
{ {
lastUsername = client.getUsername(); lastAccount = client.getAccountHash();
fetchXp = true; fetchXp = true;
} }
} }
@@ -114,8 +114,8 @@ public class XpUpdaterPlugin extends Plugin
// Don't submit update unless xp threshold is reached // Don't submit update unless xp threshold is reached
if (Math.abs(totalXp - lastXp) > XP_THRESHOLD) if (Math.abs(totalXp - lastXp) > XP_THRESHOLD)
{ {
log.debug("Submitting update for {}", local.getName()); log.debug("Submitting update for {} accountHash {}", local.getName(), lastAccount);
update(local.getName()); update(lastAccount, local.getName());
lastXp = totalXp; lastXp = totalXp;
} }
} }
@@ -131,12 +131,12 @@ public class XpUpdaterPlugin extends Plugin
} }
} }
private void update(String username) private void update(long accountHash, String username)
{ {
EnumSet<WorldType> worldTypes = client.getWorldType(); EnumSet<WorldType> worldTypes = client.getWorldType();
username = username.replace(" ", "_"); username = username.replace(" ", "_");
updateCml(username, worldTypes); updateCml(username, worldTypes);
updateTempleosrs(username, worldTypes); updateTempleosrs(accountHash, username, worldTypes);
updateWom(username, worldTypes); updateWom(username, worldTypes);
} }
@@ -165,7 +165,7 @@ public class XpUpdaterPlugin extends Plugin
} }
} }
private void updateTempleosrs(String username, EnumSet<WorldType> worldTypes) private void updateTempleosrs(long accountHash, String username, EnumSet<WorldType> worldTypes)
{ {
if (config.templeosrs() if (config.templeosrs()
&& !worldTypes.contains(WorldType.SEASONAL) && !worldTypes.contains(WorldType.SEASONAL)
@@ -178,6 +178,7 @@ public class XpUpdaterPlugin extends Plugin
.addPathSegment("php") .addPathSegment("php")
.addPathSegment("add_datapoint.php") .addPathSegment("add_datapoint.php")
.addQueryParameter("player", username) .addQueryParameter("player", username)
.addQueryParameter("accountHash", Long.toString(accountHash))
.build(); .build();
Request request = new Request.Builder() Request request = new Request.Builder()

View File

@@ -60,6 +60,8 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.client.RuneLite; import net.runelite.client.RuneLite;
import net.runelite.client.RuneLiteProperties; 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.AUTO;
import static net.runelite.client.rs.ClientUpdateCheckMode.NONE; import static net.runelite.client.rs.ClientUpdateCheckMode.NONE;
import static net.runelite.client.rs.ClientUpdateCheckMode.VANILLA; import static net.runelite.client.rs.ClientUpdateCheckMode.VANILLA;
@@ -74,7 +76,7 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
@Slf4j @Slf4j
@SuppressWarnings("deprecation") @SuppressWarnings({"deprecation", "removal"})
public class ClientLoader implements Supplier<Applet> public class ClientLoader implements Supplier<Applet>
{ {
private static final String INJECTED_CLIENT_NAME = "/injected-client.oprs"; private static final String INJECTED_CLIENT_NAME = "/injected-client.oprs";
@@ -87,16 +89,18 @@ public class ClientLoader implements Supplier<Applet>
private final ClientConfigLoader clientConfigLoader; private final ClientConfigLoader clientConfigLoader;
private ClientUpdateCheckMode updateCheckMode; private ClientUpdateCheckMode updateCheckMode;
private final WorldSupplier worldSupplier; private final WorldSupplier worldSupplier;
private final RuntimeConfigLoader runtimeConfigLoader;
private final String javConfigUrl; private final String javConfigUrl;
private Object client; 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.okHttpClient = okHttpClient;
this.clientConfigLoader = new ClientConfigLoader(okHttpClient); this.clientConfigLoader = new ClientConfigLoader(okHttpClient);
this.updateCheckMode = updateCheckMode; this.updateCheckMode = updateCheckMode;
this.worldSupplier = new WorldSupplier(okHttpClient); this.worldSupplier = new WorldSupplier(okHttpClient);
this.runtimeConfigLoader = runtimeConfigLoader;
this.javConfigUrl = javConfigUrl; this.javConfigUrl = javConfigUrl;
} }
@@ -183,12 +187,19 @@ public class ClientLoader implements Supplier<Applet>
return rs; return rs;
} }
catch (OutageException e)
{
return e;
}
catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException
| VerificationException | SecurityException e) | VerificationException | SecurityException e)
{ {
log.error("Error loading RS!", 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; return e;
} }
} }
@@ -213,6 +224,10 @@ public class ClientLoader implements Supplier<Applet>
catch (IOException e) catch (IOException e)
{ {
log.info("Failed to get jav_config from host \"{}\" ({})", url.host(), e.getMessage()); log.info("Failed to get jav_config from host \"{}\" ({})", url.host(), e.getMessage());
if (checkOutages())
{
throw new OutageException(e);
}
if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig())) if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig()))
{ {
@@ -301,6 +316,11 @@ public class ClientLoader implements Supplier<Applet>
} }
vanilla.position(0); vanilla.position(0);
if (!vanillaCacheIsInvalid && "false".equals(System.getProperty("runelite.updateVanilla")))
{
return;
}
// Start downloading the vanilla client // Start downloading the vanilla client
HttpUrl url; HttpUrl url;
if (config.isFallback()) if (config.isFallback())
@@ -412,6 +432,10 @@ public class ClientLoader implements Supplier<Applet>
catch (IOException e) catch (IOException e)
{ {
log.warn("Failed to download gamepack from \"{}\"", url, 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) // With fallback config do 1 attempt (there are no additional urls to try)
if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig()) || config.isFallback() || attempt >= NUM_ATTEMPTS) if (!javConfigUrl.equals(RuneLiteProperties.getJavConfig()) || config.isFallback() || attempt >= NUM_ATTEMPTS)
@@ -438,6 +462,7 @@ public class ClientLoader implements Supplier<Applet>
SwingUtilities.invokeLater(() -> SwingUtilities.invokeLater(() ->
new FatalErrorDialog("The client-patch is missing from the classpath. If you are building " + new FatalErrorDialog("The client-patch is missing from the classpath. If you are building " +
"the client you need to re-run maven") "the client you need to re-run maven")
.addHelpButtons()
.addBuildingGuide() .addBuildingGuide()
.open()); .open());
throw new NullPointerException(); throw new NullPointerException();
@@ -605,7 +630,7 @@ public class ClientLoader implements Supplier<Applet>
Class<?> clientClass = classLoader.loadClass(initialClass); Class<?> clientClass = classLoader.loadClass(initialClass);
Applet rs = (Applet) clientClass.newInstance(); Applet rs = (Applet) clientClass.newInstance();
rs.setStub(new RSAppletStub(config)); rs.setStub(new RSAppletStub(config, runtimeConfigLoader));
if (rs instanceof Client) if (rs instanceof Client)
{ {
@@ -658,4 +683,22 @@ public class ClientLoader implements Supplier<Applet>
verifyJarEntry(je, chains); 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;
}
} }

View File

@@ -25,16 +25,28 @@
*/ */
package net.runelite.client.rs; package net.runelite.client.rs;
import java.applet.Applet;
import java.applet.AppletContext; import java.applet.AppletContext;
import java.applet.AppletStub; 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.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import javax.swing.SwingUtilities;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.runelite.client.RuntimeConfig;
import net.runelite.client.RuntimeConfigLoader;
import net.runelite.client.ui.FatalErrorDialog;
@RequiredArgsConstructor @RequiredArgsConstructor
class RSAppletStub implements AppletStub class RSAppletStub implements AppletStub
{ {
private final RSConfig config; private final RSConfig config;
private final RuntimeConfigLoader runtimeConfigLoader;
@Override @Override
public boolean isActive() public boolean isActive()
@@ -70,7 +82,89 @@ class RSAppletStub implements AppletStub
@Override @Override
public AppletContext getAppletContext() 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<Applet> 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<String> getStreamKeys()
{
return null;
}
};
} }
@Override @Override

View File

@@ -61,6 +61,7 @@ public class FatalErrorDialog extends JDialog
private final JPanel rightColumn = new JPanel(); private final JPanel rightColumn = new JPanel();
private final Font font = new Font(Font.DIALOG, Font.PLAIN, 12); private final Font font = new Font(Font.DIALOG, Font.PLAIN, 12);
private final JLabel title;
public FatalErrorDialog(String message) public FatalErrorDialog(String message)
{ {
@@ -114,7 +115,7 @@ public class FatalErrorDialog extends JDialog
leftPane.setBackground(ColorScheme.DARKER_GRAY_COLOR); leftPane.setBackground(ColorScheme.DARKER_GRAY_COLOR);
leftPane.setLayout(new BorderLayout()); 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.setForeground(Color.WHITE);
title.setFont(font.deriveFont(16.f)); title.setFont(font.deriveFont(16.f));
title.setBorder(new EmptyBorder(10, 10, 10, 10)); title.setBorder(new EmptyBorder(10, 10, 10, 10));
@@ -137,13 +138,6 @@ public class FatalErrorDialog extends JDialog
rightColumn.setBackground(ColorScheme.DARK_GRAY_COLOR); rightColumn.setBackground(ColorScheme.DARK_GRAY_COLOR);
rightColumn.setMaximumSize(new Dimension(200, Integer.MAX_VALUE)); 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); pane.add(rightColumn, BorderLayout.EAST);
} }
@@ -193,6 +187,20 @@ public class FatalErrorDialog extends JDialog
return this; 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() public FatalErrorDialog addBuildingGuide()
{ {
return addButton("Building guide", () -> LinkBrowser.browse(RuneLiteProperties.getBuildingLink())); 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 " + 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" + action + ". You may have a misbehaving antivirus, internet service provider, a proxy, or an incomplete" +
" java installation.") " java installation.")
.addHelpButtons()
.open(); .open();
return; return;
} }
@@ -213,6 +222,7 @@ public class FatalErrorDialog extends JDialog
{ {
new FatalErrorDialog("OpenOSRS is unable to connect to a required server while " + action + ". " + new FatalErrorDialog("OpenOSRS is unable to connect to a required server while " + action + ". " +
"Please check your internet connection") "Please check your internet connection")
.addHelpButtons()
.open(); .open();
return; 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 + ". " + 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 " + "Your DNS resolver may be misconfigured, pointing to an inaccurate resolver, or your internet connection may " +
"be down. ") "be down. ")
.addHelpButtons()
.addButton("Change your DNS resolver", () -> LinkBrowser.browse(RuneLiteProperties.getDNSChangeLink())) .addButton("Change your DNS resolver", () -> LinkBrowser.browse(RuneLiteProperties.getDNSChangeLink()))
.open(); .open();
return; return;
} }
new FatalErrorDialog("OpenOSRS encountered a fatal error while " + action + ".").open(); new FatalErrorDialog("RuneLite encountered a fatal error while " + action + ".")
.addHelpButtons()
.open();
} }
} }

View File

@@ -19,3 +19,4 @@ runelite.session=https://session.openosrs.dev
runelite.static.base=https://static.runelite.net runelite.static.base=https://static.runelite.net
runelite.ws=https://api.runelite.net/ws runelite.ws=https://api.runelite.net/ws
runelite.config=https://static.runelite.net/config.json runelite.config=https://static.runelite.net/config.json
runelite.osrstwitter.link=https://twitter.com/OldSchoolRS

View File

@@ -456,28 +456,28 @@ LABEL406:
sstore 17 ; message channel sstore 17 ; message channel
iload 11 iload 11
switch switch
1: LABEL409
2: LABEL409 2: LABEL409
1: LABEL409
90: LABEL433
91: LABEL433
3: LABEL457 3: LABEL457
7: LABEL457
101: LABEL482 101: LABEL482
5: LABEL503 5: LABEL503
6: LABEL539 6: LABEL539
7: LABEL457
103: LABEL564 103: LABEL564
104: LABEL564 104: LABEL564
9: LABEL606
41: LABEL677
43: LABEL1061
107: LABEL1285
44: LABEL896
109: LABEL585
110: LABEL564 110: LABEL564
46: LABEL1200 109: LABEL585
14: LABEL1255 9: LABEL606
111: LABEL635 111: LABEL635
112: LABEL656 112: LABEL656
90: LABEL433 41: LABEL677
91: LABEL433 44: LABEL896
43: LABEL1061
46: LABEL1200
14: LABEL1255
107: LABEL1285
jump LABEL1324 jump LABEL1324
LABEL409: LABEL409:
sload 21 sload 21
@@ -1481,22 +1481,22 @@ LABEL1341:
1: LABEL1346 1: LABEL1346
2: LABEL1346 2: LABEL1346
3: LABEL1346 3: LABEL1346
101: LABEL1450
6: LABEL1346 6: LABEL1346
7: LABEL1346 7: LABEL1346
103: LABEL1493
104: LABEL1493
9: LABEL1346 9: LABEL1346
41: LABEL1346
106: LABEL1346
44: LABEL1346
109: LABEL1596
110: LABEL1493
14: LABEL1536
111: LABEL1639
112: LABEL1682
90: LABEL1346 90: LABEL1346
91: 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 jump LABEL1725
LABEL1346: LABEL1346:
sconst "<col=ffffff>" sconst "<col=ffffff>"

View File

@@ -430,9 +430,9 @@ LABEL369:
iload 18 iload 18
switch switch
3: LABEL372 3: LABEL372
5: LABEL430
6: LABEL401
7: LABEL372 7: LABEL372
6: LABEL401
5: LABEL430
jump LABEL468 jump LABEL468
LABEL372: LABEL372:
iload 7 iload 7

View File

@@ -192,10 +192,10 @@ LABEL157:
1: LABEL167 1: LABEL167
2: LABEL170 2: LABEL170
3: LABEL175 3: LABEL175
4: LABEL190
5: LABEL210
8: LABEL180 8: LABEL180
9: LABEL185 9: LABEL185
4: LABEL190
5: LABEL210
jump LABEL229 jump LABEL229
LABEL167: LABEL167:
iconst 0 iconst 0

View File

@@ -14,10 +14,10 @@
istore 4 istore 4
iload 1 iload 1
switch switch
1745: LABEL169
1129: LABEL149
1130: LABEL107
1131: LABEL9 1131: LABEL9
1130: LABEL107
1129: LABEL149
1745: LABEL169
jump LABEL244 jump LABEL244
LABEL9: LABEL9:
iconst 10747996 iconst 10747996
@@ -229,8 +229,8 @@ LABEL180:
2308 2308
get_varbit 6255 get_varbit 6255
switch switch
1: LABEL197
2: LABEL189 2: LABEL189
1: LABEL197
3: LABEL205 3: LABEL205
jump LABEL213 jump LABEL213
LABEL189: LABEL189:

View File

@@ -30,24 +30,24 @@ LABEL21:
get_varc_int 5 get_varc_int 5
switch switch
1: LABEL24 1: LABEL24
2: LABEL47
3: LABEL47
4: LABEL26 4: LABEL26
5: LABEL26 5: LABEL26
2: LABEL47
3: LABEL47
6: LABEL47 6: LABEL47
7: LABEL111 7: LABEL111
19: LABEL111
8: LABEL117 8: LABEL117
9: LABEL125 9: LABEL125
10: LABEL202
11: LABEL260
12: LABEL219
13: LABEL237
15: LABEL125 15: LABEL125
16: LABEL266
18: LABEL260
19: LABEL111
20: LABEL125 20: LABEL125
21: LABEL125 21: LABEL125
10: LABEL202
12: LABEL219
13: LABEL237
11: LABEL260
18: LABEL260
16: LABEL266
jump LABEL268 jump LABEL268
LABEL24: LABEL24:
return return
@@ -346,13 +346,13 @@ LABEL268:
LABEL269: LABEL269:
get_varc_int 5 get_varc_int 5
switch switch
16: LABEL274
20: LABEL272
21: LABEL272
7: LABEL272 7: LABEL272
8: LABEL272 8: LABEL272
9: LABEL272 9: LABEL272
15: LABEL272 15: LABEL272
20: LABEL272
21: LABEL272
16: LABEL274
jump LABEL275 jump LABEL275
LABEL272: LABEL272:
return return

View File

@@ -0,0 +1 @@
3FA5FFC8DB18A42971CED41A9BC7CEA407A0EC98061D56B2822F66CD910E4BAF

View File

@@ -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

View File

@@ -166,10 +166,10 @@ LABEL141:
iload 0 iload 0
switch switch
0: LABEL144 0: LABEL144
1: LABEL234
2: LABEL144 2: LABEL144
4: LABEL189
6: LABEL144 6: LABEL144
4: LABEL189
1: LABEL234
jump LABEL278 jump LABEL278
LABEL144: LABEL144:
iconst 20 iconst 20

View File

@@ -165,11 +165,16 @@ public class ChatCommandsPluginTest
public void testTheatreOfBlood() public void testTheatreOfBlood()
{ {
ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "",
"Wave 'The Final Challenge' (Normal Mode) complete!<br>Duration: <col=ff0000>2:42.0</col><br>Theatre of Blood wave completion time: <col=ff0000>17:00.20</col> (new personal best)", null, 0); "Wave 'The Final Challenge' (Normal Mode) complete!<br>" +
"Duration: <col=ff0000>2:42.0</col><br>" +
"Theatre of Blood completion time: <col=ff0000>17:00.20</col> (new personal best)", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage); chatCommandsPlugin.onChatMessage(chatMessage);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: <col=ff0000>73</col>.", null, 0); chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Theatre of Blood total completion time: <col=ff0000>24:40.20</col>. Personal best: 20:45.00", null, 0);
chatCommandsPlugin.onChatMessage(chatMessageEvent); chatCommandsPlugin.onChatMessage(chatMessage);
chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: <col=ff0000>73</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage);
verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood", 73); verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood", 73);
verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood", 17 * 60 + .2); verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood", 17 * 60 + .2);
@@ -179,11 +184,16 @@ public class ChatCommandsPluginTest
public void testTheatreOfBloodNoPb() public void testTheatreOfBloodNoPb()
{ {
ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "",
"Wave 'The Final Challenge' (Normal Mode) complete!<br>Duration: <col=ff0000>2:42</col><br>Theatre of Blood wave completion time: <col=ff0000>17:00</col>. Personal best: 13:52.80", null, 0); "Wave 'The Final Challenge' (Normal Mode) complete!<br>" +
"Duration: <col=ff0000>2:42</col><br>" +
"Theatre of Blood completion time: <col=ff0000>17:00</col>. Personal best: 13:52.80", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage); chatCommandsPlugin.onChatMessage(chatMessage);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: <col=ff0000>73</col>.", null, 0); chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Theatre of Blood total completion time: <col=ff0000>24:40.20</col>. Personal best: 20:45.00", null, 0);
chatCommandsPlugin.onChatMessage(chatMessageEvent); chatCommandsPlugin.onChatMessage(chatMessage);
chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: <col=ff0000>73</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage);
verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood", 73); verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood", 73);
verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood", 13 * 60 + 52.8); verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood", 13 * 60 + 52.8);
@@ -193,11 +203,16 @@ public class ChatCommandsPluginTest
public void testTheatreOfBloodEntryMode() public void testTheatreOfBloodEntryMode()
{ {
ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "",
"Wave 'The Final Challenge' (Entry Mode) complete!<br>Duration: <col=ff0000>2:42</col><br>Theatre of Blood wave completion time: <col=ff0000>17:00</col> (new personal best)", null, 0); "Wave 'The Final Challenge' (Entry Mode) complete!<br>" +
"Duration: <col=ff0000>2:42</col><br>" +
"Theatre of Blood completion time: <col=ff0000>17:00</col> (new personal best)", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage); chatCommandsPlugin.onChatMessage(chatMessage);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood: Entry Mode count is: <col=ff0000>73</col>.", null, 0); chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Theatre of Blood total completion time: <col=ff0000>24:40.20</col>. Personal best: 20:45.00", null, 0);
chatCommandsPlugin.onChatMessage(chatMessageEvent); chatCommandsPlugin.onChatMessage(chatMessage);
chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood: Entry Mode count is: <col=ff0000>73</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage);
verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood entry mode", 73); verify(configManager).setRSProfileConfiguration("killcount", "theatre of blood entry mode", 73);
verify(configManager).setRSProfileConfiguration("personalbest", "theatre of blood entry mode", 17 * 60.); 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); 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: <col=ff0000>2</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage);
chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Lap duration: <col=ff0000>1:01</col> (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: <col=ff0000>1:01.20</col> (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: <col=ff0000>2</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage);
chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Lap duration: <col=ff0000>1:01</col> (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: <col=ff0000>1:01.20</col> (new personal best).", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage);
verify(configManager).setRSProfileConfiguration("personalbest", "shayzien basic agility course", 61.2);
}
@Test @Test
public void testZukNewPb() public void testZukNewPb()
{ {
@@ -1074,4 +1129,13 @@ public class ChatCommandsPluginTest
verify(configManager).setRSProfileConfiguration("personalbest", "tempoross", 234.0); verify(configManager).setRSProfileConfiguration("personalbest", "tempoross", 234.0);
verify(configManager).setRSProfileConfiguration("personalbest", "chambers of xeric", 1360.0); // the lowest time 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: <col=ff0000>7</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage);
verify(configManager).setRSProfileConfiguration("killcount", "guardians of the rift", 7);
}
} }

View File

@@ -34,8 +34,10 @@ import net.runelite.api.GameState;
import net.runelite.api.KeyCode; import net.runelite.api.KeyCode;
import net.runelite.api.MenuAction; import net.runelite.api.MenuAction;
import net.runelite.api.MenuEntry; import net.runelite.api.MenuEntry;
import net.runelite.api.ObjectComposition;
import net.runelite.api.events.ClientTick; import net.runelite.api.events.ClientTick;
import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuEntryAdded;
import net.runelite.client.chat.ChatMessageManager;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.menus.TestMenuEntry; import net.runelite.client.menus.TestMenuEntry;
@@ -45,10 +47,12 @@ import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import org.mockito.Mock; import org.mockito.Mock;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -70,6 +74,10 @@ public class MenuEntrySwapperPluginTest
@Bind @Bind
ItemManager itemManager; ItemManager itemManager;
@Mock
@Bind
ChatMessageManager chatMessageManager;
@Mock @Mock
@Bind @Bind
MenuEntrySwapperConfig config; MenuEntrySwapperConfig config;
@@ -85,6 +93,7 @@ public class MenuEntrySwapperPluginTest
Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this);
when(client.getGameState()).thenReturn(GameState.LOGGED_IN); when(client.getGameState()).thenReturn(GameState.LOGGED_IN);
when(client.getObjectDefinition(anyInt())).thenReturn(mock(ObjectComposition.class));
when(client.getMenuEntries()).thenAnswer((Answer<MenuEntry[]>) invocationOnMock -> when(client.getMenuEntries()).thenAnswer((Answer<MenuEntry[]>) invocationOnMock ->
{ {

View File

@@ -95,7 +95,7 @@ public class FarmingTrackerTest
@Test(expected = IllegalStateException.class) @Test(expected = IllegalStateException.class)
public void testEmptyNotification() 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); PatchPrediction patchPrediction = new PatchPrediction(Produce.EMPTY_COMPOST_BIN, CropState.EMPTY, 0L, 0, 0);
FarmingRegion region = new FarmingRegion("Ardougne", 10548, false, FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,
@@ -113,7 +113,7 @@ public class FarmingTrackerTest
@Test @Test
public void testHarvestableNotification() 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); PatchPrediction patchPrediction = new PatchPrediction(Produce.RANARR, CropState.HARVESTABLE, 0L, 0, 0);
FarmingRegion region = new FarmingRegion("Ardougne", 10548, false, FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,