Merge remote-tracking branch 'runelite/master'

This commit is contained in:
Owain van Brakel
2022-03-30 12:03:22 +02:00
34 changed files with 1071 additions and 92 deletions

View File

@@ -27,9 +27,12 @@ package net.runelite.client;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.jagex.oldscape.pub.OAuthApi;
import com.jagex.oldscape.pub.OtlTokenResponse;
import com.openosrs.client.OpenOSRS;
import com.openosrs.client.game.PlayerManager;
import com.openosrs.client.ui.OpenOSRSSplashScreen;
@@ -50,6 +53,7 @@ import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.Optional;
import java.util.stream.Stream;
@@ -92,6 +96,8 @@ import net.runelite.http.api.RuneLiteAPI;
import net.runelite.http.api.worlds.World;
import net.runelite.http.api.worlds.WorldResult;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
@@ -120,6 +126,9 @@ public class RuneLite
@Inject
private net.runelite.client.plugins.PluginManager pluginManager;
@Inject
private OkHttpClient okHttpClient;
@Inject
private ExternalPluginManager externalPluginManager;
@@ -290,8 +299,8 @@ public class RuneLite
try
{
final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), (String) options.valueOf("jav_config"));
final RuntimeConfigLoader runtimeConfigLoader = new RuntimeConfigLoader(okHttpClient);
final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), runtimeConfigLoader, (String) options.valueOf("jav_config"));
new Thread(() ->
{
@@ -330,6 +339,7 @@ public class RuneLite
log.error("Failure during startup", e);
SwingUtilities.invokeLater(() ->
new FatalErrorDialog("OpenOSRS has encountered an unexpected error during startup.")
.addHelpButtons()
.open());
}
finally
@@ -371,6 +381,11 @@ public class RuneLite
}
applet.start();
if (applet instanceof OAuthApi)
{
setupJxAuth((OAuthApi) applet);
}
}
SplashScreen.stage(.57, null, "Loading configuration");
@@ -661,4 +676,83 @@ public class RuneLite
System.setProperty(key, value);
}
}
private void setupJxAuth(OAuthApi oAuthApi)
{
String accessToken = System.getenv("JX_ACCESS_TOKEN");
if (Strings.isNullOrEmpty(accessToken))
{
return;
}
try
{
log.info("Initializing OTL token requester with access token");
oAuthApi.setOtlTokenRequester(url ->
{
CompletableFuture<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 API_BASE = "runelite.api.base";
private static final String RUNELITE_CONFIG = "runelite.config";
private static final String OSRS_TWITTER_LINK = "runelite.osrstwitter.link";
@Getter(AccessLevel.PACKAGE)
private static final Properties properties = new Properties();
@@ -148,4 +149,9 @@ public class RuneLiteProperties
{
return properties.getProperty(RUNELITE_CONFIG);
}
public static String getOSRSTwitterLink()
{
return properties.getProperty(OSRS_TWITTER_LINK);
}
}

View File

@@ -24,13 +24,46 @@
*/
package net.runelite.client;
import com.google.common.base.Strings;
import java.util.Collections;
import java.util.Map;
import javax.swing.SwingUtilities;
import lombok.Data;
import net.runelite.client.ui.FatalErrorDialog;
import net.runelite.client.util.LinkBrowser;
@Data
public class RuntimeConfig
{
private Map<String, ?> props = 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.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import net.runelite.http.api.RuneLiteAPI;
import okhttp3.Call;
@@ -41,7 +44,7 @@ import okhttp3.Request;
import okhttp3.Response;
@Slf4j
class RuntimeConfigLoader implements Supplier<RuntimeConfig>
public class RuntimeConfigLoader implements Supplier<RuntimeConfig>
{
private final OkHttpClient okHttpClient;
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()
{
CompletableFuture<RuntimeConfig> future = new CompletableFuture<>();

View File

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

View File

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

View File

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

View File

@@ -110,10 +110,8 @@ public class ChatCommandsPlugin extends Plugin
private static final String TEAM_SIZES = "(?:\\d+(?:\\+|-\\d+)? players|Solo)";
private static final Pattern RAIDS_PB_PATTERN = Pattern.compile("<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 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 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 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 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 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 DUEL_ARENA_WINS_PATTERN = Pattern.compile("You (were defeated|won)! You have(?: now)? won ([\\d,]+|one) duels?");
private static final Pattern DUEL_ARENA_LOSSES_PATTERN = Pattern.compile("You have(?: now)? lost ([\\d,]+|one) duels?");
private static final Pattern ADVENTURE_LOG_TITLE_PATTERN = Pattern.compile("The Exploits of (.+)");
@@ -123,6 +121,7 @@ public class ChatCommandsPlugin extends Plugin
private static final Pattern HS_KC_FLOOR_PATTERN = Pattern.compile("You have completed Floor (\\d) of the Hallowed Sepulchre! Total completions: <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 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 PRICE_COMMAND_STRING = "!price";
@@ -469,18 +468,6 @@ public class ChatCommandsPlugin extends Plugin
matchPb(matcher);
}
matcher = TOB_WAVE_PB_PATTERN.matcher(message);
if (matcher.find())
{
matchPb(matcher);
}
matcher = TOB_WAVE_DURATION_PATTERN.matcher(message);
if (matcher.find())
{
matchPb(matcher);
}
matcher = HS_PB_PATTERN.matcher(message);
if (matcher.find())
{
@@ -538,6 +525,13 @@ public class ChatCommandsPlugin extends Plugin
}
}
}
matcher = GUARDIANS_OF_THE_RIFT_PATTERN.matcher(message);
if (matcher.find())
{
int kc = Integer.parseInt(matcher.group(1));
setKc("Guardians of the Rift", kc);
}
}
@VisibleForTesting
@@ -1970,6 +1964,7 @@ public class ChatCommandsPlugin extends Plugin
case "tob hm":
case "tob hard mode":
case "tob hard":
case "hmt":
return "Theatre of Blood Hard Mode";
// agility course
@@ -2152,6 +2147,11 @@ public class ChatCommandsPlugin extends Plugin
case "jad 6":
return "TzHaar-Ket-Rak's Sixth Challenge";
// Guardians of the Rift
case "gotr":
case "runetodt":
return "Guardians of the Rift";
default:
return WordUtils.capitalize(boss);
}

View File

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

View File

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

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 int TEMPOROSS_REGION = 12588;
// Guardians of the Rift
private static final String GUARDIANS_OF_THE_RIFT_EVENT = "Guardians of the Rift";
private static final String INTRICATE_POUCH_EVENT = "Intricate pouch";
private static final String GUARDIANS_OF_THE_RIFT_LOOT_STRING = "You found some loot: ";
private static final int GUARDIANS_OF_THE_RIFT_REGION = 14484;
// Mahogany Homes
private static final String MAHOGANY_CRATE_EVENT = "Supply crate (Mahogany Homes)";
@@ -480,6 +486,13 @@ public class LootTrackerPlugin extends Plugin
clientThread.invokeLater(() ->
{
// convertToLootTrackerRecord requires item compositions to be available to get the item name,
// so it can't be run while the client is starting
if (client.getGameState().getState() < GameState.LOGIN_SCREEN.getState())
{
return false;
}
// convertToLootTrackerRecord must be called on client thread
List<LootTrackerRecord> records = loots.stream()
.map(this::convertToLootTrackerRecord)
@@ -489,6 +502,8 @@ public class LootTrackerPlugin extends Plugin
panel.clearRecords();
panel.addRecords(records);
});
return true;
});
});
}
@@ -887,6 +902,12 @@ public class LootTrackerPlugin extends Plugin
return;
}
if (regionID == GUARDIANS_OF_THE_RIFT_REGION && message.startsWith(GUARDIANS_OF_THE_RIFT_LOOT_STRING))
{
onInvChange(collectInvItems(LootRecordType.EVENT, GUARDIANS_OF_THE_RIFT_EVENT, client.getBoostedSkillLevel(Skill.RUNECRAFT)));
return;
}
if (message.equals(IMPLING_CATCH_MESSAGE))
{
onInvChange(collectInvItems(LootRecordType.EVENT, client.getLocalPlayer().getInteracting().getName()));
@@ -977,6 +998,9 @@ public class LootTrackerPlugin extends Plugin
case ItemID.CASKET_25590:
onInvChange(collectInvAndGroundItems(LootRecordType.EVENT, TEMPOROSS_CASKET_EVENT));
break;
case ItemID.INTRICATE_POUCH:
onInvChange(collectInvAndGroundItems(LootRecordType.EVENT, INTRICATE_POUCH_EVENT));
break;
case ItemID.SIMPLE_LOCKBOX_25647:
case ItemID.ELABORATE_LOCKBOX_25649:
case ItemID.ORNATE_LOCKBOX_25651:

View File

@@ -826,4 +826,34 @@ public interface MenuEntrySwapperConfig extends Config
{
return false;
}
@ConfigItem(
keyName = "swapDepositPool",
name = "Deposit Pool - Only Runes",
description = "Swap Deposit with Deposit Runes on the Deposit Pool in Guardians of the Rift.",
section = objectSection
)
default boolean swapDepositPool()
{
return false;
}
enum UnchargedCellsMode
{
TAKE,
TAKE_1,
TAKE_5,
TAKE_10
}
@ConfigItem(
keyName = "swapUnchargedCells",
name = "Uncharged Cells",
description = "Swap the take option for Uncharged Cells in Guardians of the Rift.",
section = objectSection
)
default UnchargedCellsMode swapUnchargedCells()
{
return UnchargedCellsMode.TAKE;
}
}

View File

@@ -71,6 +71,7 @@ import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfi
import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfig.KaramjaGlovesMode;
import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfig.MorytaniaLegsMode;
import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfig.RadasBlessingMode;
import static net.runelite.client.plugins.menuentryswapper.MenuEntrySwapperConfig.UnchargedCellsMode;
import net.runelite.client.util.Text;
@PluginDescriptor(
@@ -419,6 +420,12 @@ public class MenuEntrySwapperPlugin extends Plugin
swap("climb", "climb-up", () -> (shiftModifier() ? config.swapStairsShiftClick() : config.swapStairsLeftClick()) == MenuEntrySwapperConfig.StairsMode.CLIMB_UP);
swap("climb", "climb-down", () -> (shiftModifier() ? config.swapStairsShiftClick() : config.swapStairsLeftClick()) == MenuEntrySwapperConfig.StairsMode.CLIMB_DOWN);
swap("deposit", "deposit-runes", config::swapDepositPool);
swap("take", "uncharged cells", "take-1", () -> config.swapUnchargedCells() == UnchargedCellsMode.TAKE_1);
swap("take", "uncharged cells", "take-5", () -> config.swapUnchargedCells() == UnchargedCellsMode.TAKE_5);
swap("take", "uncharged cells", "take-10", () -> config.swapUnchargedCells() == UnchargedCellsMode.TAKE_10);
}
public Swap swap(String option, String swappedOption, Supplier<Boolean> enabled)

View File

@@ -42,7 +42,7 @@ class NpcAggroAreaNotWorkingOverlay extends OverlayPanel
this.plugin = plugin;
panelComponent.getChildren().add(LineComponent.builder()
.left("Unaggressive NPC timers will start working when you teleport far away or enter a dungeon.")
.left("Unaggressive NPC timers require calibration. Teleport far away or enter a dungeon, then run until this overlay disappears.")
.build());
setPriority(OverlayPriority.LOW);
@@ -53,7 +53,7 @@ class NpcAggroAreaNotWorkingOverlay extends OverlayPanel
@Override
public Dimension render(Graphics2D graphics)
{
if (!plugin.isActive() || plugin.getSafeCenters()[1] != null)
if (plugin.getSafeCenters()[1] != null)
{
return null;
}

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

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

View File

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

View File

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

View File

@@ -61,6 +61,7 @@ public class FatalErrorDialog extends JDialog
private final JPanel rightColumn = new JPanel();
private final Font font = new Font(Font.DIALOG, Font.PLAIN, 12);
private final JLabel title;
public FatalErrorDialog(String message)
{
@@ -114,7 +115,7 @@ public class FatalErrorDialog extends JDialog
leftPane.setBackground(ColorScheme.DARKER_GRAY_COLOR);
leftPane.setLayout(new BorderLayout());
JLabel title = new JLabel("There was a fatal error starting OpenOSRS");
title = new JLabel("There was a fatal error starting OpenOSRS");
title.setForeground(Color.WHITE);
title.setFont(font.deriveFont(16.f));
title.setBorder(new EmptyBorder(10, 10, 10, 10));
@@ -137,13 +138,6 @@ public class FatalErrorDialog extends JDialog
rightColumn.setBackground(ColorScheme.DARK_GRAY_COLOR);
rightColumn.setMaximumSize(new Dimension(200, Integer.MAX_VALUE));
addButton("Open logs folder", () ->
{
LinkBrowser.open(RuneLite.LOGS_DIR.toString());
});
addButton("Get help on Discord", () -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite()));
addButton("Troubleshooting steps", () -> LinkBrowser.browse(RuneLiteProperties.getTroubleshootingLink()));
pane.add(rightColumn, BorderLayout.EAST);
}
@@ -193,6 +187,20 @@ public class FatalErrorDialog extends JDialog
return this;
}
public FatalErrorDialog setTitle(String windowTitle, String header)
{
super.setTitle(windowTitle);
title.setText(header);
return this;
}
public FatalErrorDialog addHelpButtons()
{
return addButton("Open logs folder", () -> LinkBrowser.open(RuneLite.LOGS_DIR.toString()))
.addButton("Get help on Discord", () -> LinkBrowser.browse(RuneLiteProperties.getDiscordInvite()))
.addButton("Troubleshooting steps", () -> LinkBrowser.browse(RuneLiteProperties.getTroubleshootingLink()));
}
public FatalErrorDialog addBuildingGuide()
{
return addButton("Building guide", () -> LinkBrowser.browse(RuneLiteProperties.getBuildingLink()));
@@ -205,6 +213,7 @@ public class FatalErrorDialog extends JDialog
new FatalErrorDialog("OpenOSRS was unable to verify the security of its connection to the internet while " +
action + ". You may have a misbehaving antivirus, internet service provider, a proxy, or an incomplete" +
" java installation.")
.addHelpButtons()
.open();
return;
}
@@ -213,6 +222,7 @@ public class FatalErrorDialog extends JDialog
{
new FatalErrorDialog("OpenOSRS is unable to connect to a required server while " + action + ". " +
"Please check your internet connection")
.addHelpButtons()
.open();
return;
}
@@ -222,11 +232,14 @@ public class FatalErrorDialog extends JDialog
new FatalErrorDialog("OpenOSRS is unable to resolve the address of a required server while " + action + ". " +
"Your DNS resolver may be misconfigured, pointing to an inaccurate resolver, or your internet connection may " +
"be down. ")
.addHelpButtons()
.addButton("Change your DNS resolver", () -> LinkBrowser.browse(RuneLiteProperties.getDNSChangeLink()))
.open();
return;
}
new FatalErrorDialog("OpenOSRS encountered a fatal error while " + action + ".").open();
new FatalErrorDialog("RuneLite encountered a fatal error while " + action + ".")
.addHelpButtons()
.open();
}
}

View File

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

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

@@ -165,11 +165,16 @@ public class ChatCommandsPluginTest
public void testTheatreOfBlood()
{
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);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: <col=ff0000>73</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessageEvent);
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(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("personalbest", "theatre of blood", 17 * 60 + .2);
@@ -179,11 +184,16 @@ public class ChatCommandsPluginTest
public void testTheatreOfBloodNoPb()
{
ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "",
"Wave 'The Final Challenge' (Normal Mode) complete!<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);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood count is: <col=ff0000>73</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessageEvent);
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(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("personalbest", "theatre of blood", 13 * 60 + 52.8);
@@ -193,11 +203,16 @@ public class ChatCommandsPluginTest
public void testTheatreOfBloodEntryMode()
{
ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "",
"Wave 'The Final Challenge' (Entry Mode) complete!<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);
ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Theatre of Blood: Entry Mode count is: <col=ff0000>73</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessageEvent);
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(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("personalbest", "theatre of blood entry mode", 17 * 60.);
@@ -1074,4 +1089,13 @@ public class ChatCommandsPluginTest
verify(configManager).setRSProfileConfiguration("personalbest", "tempoross", 234.0);
verify(configManager).setRSProfileConfiguration("personalbest", "chambers of xeric", 1360.0); // the lowest time
}
@Test
public void testGuardiansOfTheRift()
{
ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Amount of Rifts you have closed: <col=ff0000>7</col>.", null, 0);
chatCommandsPlugin.onChatMessage(chatMessage);
verify(configManager).setRSProfileConfiguration("killcount", "guardians of the rift", 7);
}
}

View File

@@ -95,7 +95,7 @@ public class FarmingTrackerTest
@Test(expected = IllegalStateException.class)
public void testEmptyNotification()
{
RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null);
RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, -1, null);
PatchPrediction patchPrediction = new PatchPrediction(Produce.EMPTY_COMPOST_BIN, CropState.EMPTY, 0L, 0, 0);
FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,
@@ -113,7 +113,7 @@ public class FarmingTrackerTest
@Test
public void testHarvestableNotification()
{
RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null);
RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, -1, null);
PatchPrediction patchPrediction = new PatchPrediction(Produce.RANARR, CropState.HARVESTABLE, 0L, 0, 0);
FarmingRegion region = new FarmingRegion("Ardougne", 10548, false,