From 58d2b2e0e17c156d50b3b6b154fbbc63dd60c24e Mon Sep 17 00:00:00 2001 From: Adam Keenan Date: Sun, 30 Jan 2022 19:57:35 -0500 Subject: [PATCH 01/12] chat commands: fix cmb to use correct endpoint --- .../plugins/chatcommands/ChatCommandsPlugin.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index f1e72c9303..84b87bcb88 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -1407,21 +1407,11 @@ public class ChatCommandsPlugin extends Plugin return; } - ChatMessageType type = chatMessage.getType(); - - String player; - if (type == ChatMessageType.PRIVATECHATOUT) - { - player = client.getLocalPlayer().getName(); - } - else - { - player = Text.sanitize(chatMessage.getName()); - } + final HiscoreLookup lookup = getCorrectLookupFor(chatMessage); try { - HiscoreResult playerStats = hiscoreClient.lookup(player); + HiscoreResult playerStats = hiscoreClient.lookup(lookup.getName(), lookup.getEndpoint()); if (playerStats == null) { From 302b57de8ea3fb74c7e1428c4c135ce2cf45f363 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 30 Jan 2022 19:12:44 -0500 Subject: [PATCH 02/12] xptracker: use seasonal wom version for seasonal worlds --- .../net/runelite/client/plugins/xptracker/XpInfoBox.java | 2 +- .../net/runelite/client/plugins/xptracker/XpPanel.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java index dbcfd6ddbd..8f6f9d6dbc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpInfoBox.java @@ -124,7 +124,7 @@ class XpInfoBox extends JPanel // Create open xp tracker menu final JMenuItem openXpTracker = new JMenuItem("Open Wise Old Man"); openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl( - client.getLocalPlayer(), skill))); + client.getWorldType(), client.getLocalPlayer(), skill))); // Create reset menu final JMenuItem reset = new JMenuItem("Reset"); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java index ed72128ef4..0ecce8290d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpPanel.java @@ -29,6 +29,7 @@ import java.awt.BorderLayout; import java.awt.GridLayout; import java.util.HashMap; import java.util.Map; +import java.util.Set; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JComponent; @@ -41,6 +42,7 @@ import javax.swing.border.EmptyBorder; import net.runelite.api.Actor; import net.runelite.api.Client; import net.runelite.api.Skill; +import net.runelite.api.WorldType; import net.runelite.client.game.SkillIconManager; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; @@ -83,7 +85,7 @@ class XpPanel extends PluginPanel // Create open xp tracker menu final JMenuItem openXpTracker = new JMenuItem("Open Wise Old Man"); openXpTracker.addActionListener(e -> LinkBrowser.browse(XpPanel.buildXpTrackerUrl( - client.getLocalPlayer(), Skill.OVERALL))); + client.getWorldType(), client.getLocalPlayer(), Skill.OVERALL))); // Create reset all menu final JMenuItem reset = new JMenuItem("Reset All"); @@ -146,7 +148,7 @@ class XpPanel extends PluginPanel add(errorPanel); } - static String buildXpTrackerUrl(final Actor player, final Skill skill) + static String buildXpTrackerUrl(final Set worldTypes, final Actor player, final Skill skill) { if (player == null) { @@ -155,7 +157,7 @@ class XpPanel extends PluginPanel return new HttpUrl.Builder() .scheme("https") - .host("wiseoldman.net") + .host(worldTypes.contains(WorldType.SEASONAL) ? "seasonal.wiseoldman.net" : "wiseoldman.net") .addPathSegment("players") .addPathSegment(player.getName()) .addPathSegment("gained") From f283871f507d8bd40793294f9d248aba7c258290 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 31 Jan 2022 20:20:01 -0500 Subject: [PATCH 03/12] interact highlight: support menu option mouseover Co-authored-by: superiorser9 --- .../InteractHighlightOverlay.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java index 0a14053e36..e401f39d9b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/interacthighlight/InteractHighlightOverlay.java @@ -33,6 +33,7 @@ import net.runelite.api.Client; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; import net.runelite.api.NPC; +import net.runelite.api.Point; import net.runelite.api.TileObject; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayLayer; @@ -78,8 +79,8 @@ class InteractHighlightOverlay extends Overlay return; } - MenuEntry top = menuEntries[menuEntries.length - 1]; - MenuAction menuAction = top.getType(); + MenuEntry entry = client.isMenuOpen() ? hoveredMenuEntry(menuEntries) : menuEntries[menuEntries.length - 1]; + MenuAction menuAction = entry.getType(); switch (menuAction) { @@ -90,10 +91,11 @@ class InteractHighlightOverlay extends Overlay case GAME_OBJECT_THIRD_OPTION: case GAME_OBJECT_FOURTH_OPTION: case GAME_OBJECT_FIFTH_OPTION: + case EXAMINE_OBJECT: { - int x = top.getParam0(); - int y = top.getParam1(); - int id = top.getIdentifier(); + int x = entry.getParam0(); + int y = entry.getParam1(); + int id = entry.getIdentifier(); TileObject tileObject = plugin.findTileObject(x, y, id); if (tileObject != null && config.objectShowHover() && (tileObject != plugin.getInteractedObject() || !config.objectShowInteract())) { @@ -108,8 +110,9 @@ class InteractHighlightOverlay extends Overlay case NPC_THIRD_OPTION: case NPC_FOURTH_OPTION: case NPC_FIFTH_OPTION: + case EXAMINE_NPC: { - int id = top.getIdentifier(); + int id = entry.getIdentifier(); NPC npc = plugin.findNpc(id); if (npc != null && config.npcShowHover() && (npc != plugin.getInteractedTarget() || !config.npcShowInteract())) { @@ -155,4 +158,29 @@ class InteractHighlightOverlay extends Overlay } return end; } + + private MenuEntry hoveredMenuEntry(final MenuEntry[] menuEntries) + { + final int menuX = client.getMenuX(); + final int menuY = client.getMenuY(); + final int menuWidth = client.getMenuWidth(); + final Point mousePosition = client.getMouseCanvasPosition(); + + int dy = mousePosition.getY() - menuY; + dy -= 19; // Height of Choose Option + if (dy < 0) + { + return menuEntries[menuEntries.length - 1]; + } + + int idx = dy / 15; // Height of each menu option + idx = menuEntries.length - 1 - idx; + + if (mousePosition.getX() > menuX && mousePosition.getX() < menuX + menuWidth + && idx >= 0 && idx < menuEntries.length) + { + return menuEntries[idx]; + } + return menuEntries[menuEntries.length - 1]; + } } From 03f0df17b6e27a8a91f9f158f986db8d095f3259 Mon Sep 17 00:00:00 2001 From: Daniel Bolink Date: Tue, 1 Feb 2022 17:20:17 -0800 Subject: [PATCH 04/12] woodcutting: add Isle Of Souls trees --- .../client/plugins/woodcutting/Tree.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/Tree.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/Tree.java index f87bcaeef3..28f90cfc6f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/Tree.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/Tree.java @@ -34,18 +34,23 @@ import static net.runelite.api.ObjectID.MAGIC_TREE_10834; import static net.runelite.api.NullObjectID.NULL_10835; import static net.runelite.api.ObjectID.MAHOGANY; import static net.runelite.api.ObjectID.MAHOGANY_36688; +import static net.runelite.api.ObjectID.MAHOGANY_40760; import static net.runelite.api.ObjectID.MAPLE_TREE_10832; import static net.runelite.api.ObjectID.MAPLE_TREE_36681; +import static net.runelite.api.ObjectID.MAPLE_TREE_40754; import static net.runelite.api.ObjectID.OAK_10820; import static net.runelite.api.ObjectID.OAK_TREE_4540; import static net.runelite.api.ObjectID.REDWOOD_29670; import static net.runelite.api.ObjectID.TEAK; import static net.runelite.api.ObjectID.TEAK_36686; +import static net.runelite.api.ObjectID.TEAK_40758; import static net.runelite.api.ObjectID.TREE; import static net.runelite.api.ObjectID.TREE_1277; import static net.runelite.api.ObjectID.TREE_1278; import static net.runelite.api.ObjectID.TREE_1279; import static net.runelite.api.ObjectID.TREE_1280; +import static net.runelite.api.ObjectID.TREE_40750; +import static net.runelite.api.ObjectID.TREE_40752; import static net.runelite.api.ObjectID.WILLOW; import static net.runelite.api.ObjectID.WILLOW_10829; import static net.runelite.api.ObjectID.WILLOW_10831; @@ -53,15 +58,16 @@ import static net.runelite.api.ObjectID.WILLOW_10833; import static net.runelite.api.ObjectID.YEW; import static net.runelite.api.NullObjectID.NULL_10823; import static net.runelite.api.ObjectID.YEW_36683; +import static net.runelite.api.ObjectID.YEW_40756; import static net.runelite.client.util.RSTimeUnit.GAME_TICKS; @Getter enum Tree { - REGULAR_TREE(null, TREE, TREE_1277, TREE_1278, TREE_1279, TREE_1280), + REGULAR_TREE(null, TREE, TREE_1277, TREE_1278, TREE_1279, TREE_1280, TREE_40750, TREE_40752), OAK_TREE(Duration.of(14, GAME_TICKS), OAK_TREE_4540, OAK_10820), WILLOW_TREE(Duration.of(14, GAME_TICKS), WILLOW, WILLOW_10829, WILLOW_10831, WILLOW_10833), - MAPLE_TREE(Duration.of(59, GAME_TICKS), MAPLE_TREE_10832, MAPLE_TREE_36681) + MAPLE_TREE(Duration.of(59, GAME_TICKS), MAPLE_TREE_10832, MAPLE_TREE_36681, MAPLE_TREE_40754) { @Override Duration getRespawnTime(int region) @@ -69,9 +75,9 @@ enum Tree return region == MISCELLANIA_REGION ? Duration.of(14, GAME_TICKS) : super.respawnTime; } }, - TEAK_TREE(Duration.of(15, GAME_TICKS), TEAK, TEAK_36686), - MAHOGANY_TREE(Duration.of(14, GAME_TICKS), MAHOGANY, MAHOGANY_36688), - YEW_TREE(Duration.of(99, GAME_TICKS), YEW, NULL_10823, YEW_36683), + TEAK_TREE(Duration.of(15, GAME_TICKS), TEAK, TEAK_36686, TEAK_40758), + MAHOGANY_TREE(Duration.of(14, GAME_TICKS), MAHOGANY, MAHOGANY_36688, MAHOGANY_40760), + YEW_TREE(Duration.of(99, GAME_TICKS), YEW, NULL_10823, YEW_36683, YEW_40756), MAGIC_TREE(Duration.of(199, GAME_TICKS), MAGIC_TREE_10834, NULL_10835), REDWOOD(Duration.of(199, GAME_TICKS), ObjectID.REDWOOD, REDWOOD_29670); From b37d46e488ff2a3ee131ac149b51f6951b93840a Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Feb 2022 16:53:51 -0500 Subject: [PATCH 05/12] client: add runtime config A form of this was added in 1.7.11.2 in e8ea616fc24691177cb2de18698f13ba17448a11, but did not bind the config to the RuneLite guice module. --- .../java/net/runelite/client/RuneLite.java | 2 + .../net/runelite/client/RuneLiteModule.java | 39 ++++++ .../runelite/client/RuneLiteProperties.java | 6 + .../net/runelite/client/RuntimeConfig.java | 35 +++++ .../runelite/client/RuntimeConfigLoader.java | 123 ++++++++++++++++++ .../net/runelite/client/runelite.properties | 3 +- .../client/plugins/PluginManagerTest.java | 2 +- 7 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLite.java b/runelite-client/src/main/java/net/runelite/client/RuneLite.java index fad5c22eb3..6abb3b2208 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -215,6 +215,7 @@ public class RuneLite try { final ClientLoader clientLoader = new ClientLoader(okHttpClient, options.valueOf(updateMode), (String) options.valueOf("jav_config")); + final RuntimeConfigLoader runtimeConfigLoader = new RuntimeConfigLoader(okHttpClient); new Thread(() -> { @@ -249,6 +250,7 @@ public class RuneLite injector = Guice.createInjector(new RuneLiteModule( okHttpClient, clientLoader, + runtimeConfigLoader, developerMode, options.has("safe-mode"), options.valueOf(sessionfile), diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java index 569f05e20d..04744534c8 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java @@ -25,12 +25,15 @@ package net.runelite.client; import com.google.common.base.Strings; +import com.google.common.math.DoubleMath; import com.google.gson.Gson; import com.google.inject.AbstractModule; import com.google.inject.Provides; +import com.google.inject.binder.ConstantBindingBuilder; import com.google.inject.name.Names; import java.applet.Applet; import java.io.File; +import java.util.Map; import java.util.Properties; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -63,6 +66,7 @@ public class RuneLiteModule extends AbstractModule { private final OkHttpClient okHttpClient; private final Supplier clientLoader; + private final Supplier configSupplier; private final boolean developerMode; private final boolean safeMode; private final File sessionfile; @@ -71,12 +75,40 @@ public class RuneLiteModule extends AbstractModule @Override protected void configure() { + // bind properties Properties properties = RuneLiteProperties.getProperties(); for (String key : properties.stringPropertyNames()) { String value = properties.getProperty(key); bindConstant().annotatedWith(Names.named(key)).to(value); } + + // bind runtime config + RuntimeConfig runtimeConfig = configSupplier.get(); + if (runtimeConfig != null && runtimeConfig.getProps() != null) + { + for (Map.Entry entry : runtimeConfig.getProps().entrySet()) + { + if (entry.getValue() instanceof String) + { + ConstantBindingBuilder binder = bindConstant().annotatedWith(Names.named(entry.getKey())); + binder.to((String) entry.getValue()); + } + else if (entry.getValue() instanceof Double) + { + ConstantBindingBuilder binder = bindConstant().annotatedWith(Names.named(entry.getKey())); + if (DoubleMath.isMathematicalInteger((double) entry.getValue())) + { + binder.to((int) (double) entry.getValue()); + } + else + { + binder.to((double) entry.getValue()); + } + } + } + } + bindConstant().annotatedWith(Names.named("developerMode")).to(developerMode); bindConstant().annotatedWith(Names.named("safeMode")).to(safeMode); bind(File.class).annotatedWith(Names.named("sessionfile")).toInstance(sessionfile); @@ -116,6 +148,13 @@ public class RuneLiteModule extends AbstractModule return applet instanceof Client ? (Client) applet : null; } + @Provides + @Singleton + RuntimeConfig provideRuntimeConfig() + { + return configSupplier.get(); + } + @Provides @Singleton RuneLiteConfig provideConfig(ConfigManager configManager) diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java index e598439c2d..1bead1101c 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteProperties.java @@ -48,6 +48,7 @@ public class RuneLiteProperties private static final String PLUGINHUB_BASE = "runelite.pluginhub.url"; 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"; @Getter(AccessLevel.PACKAGE) private static final Properties properties = new Properties(); @@ -130,4 +131,9 @@ public class RuneLiteProperties { return properties.getProperty(API_BASE); } + + public static String getRuneLiteConfig() + { + return properties.getProperty(RUNELITE_CONFIG); + } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java b/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java new file mode 100644 index 0000000000..5d3fb80d94 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/RuntimeConfig.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2022, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client; + +import java.util.Collections; +import java.util.Map; +import lombok.Data; + +@Data +public class RuntimeConfig +{ + private Map props = Collections.emptyMap(); +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java b/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java new file mode 100644 index 0000000000..aa82fe44dc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/RuntimeConfigLoader.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client; + +import com.google.common.base.Strings; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; +import lombok.extern.slf4j.Slf4j; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +@Slf4j +class RuntimeConfigLoader implements Supplier +{ + private final OkHttpClient okHttpClient; + private final CompletableFuture configFuture; + + public RuntimeConfigLoader(OkHttpClient okHttpClient) + { + this.okHttpClient = okHttpClient; + configFuture = fetch(); + } + + @Override + public RuntimeConfig get() + { + try + { + return configFuture.get(); + } + catch (InterruptedException | ExecutionException e) + { + log.error("error fetching runtime config", e); + return null; + } + } + + private CompletableFuture fetch() + { + CompletableFuture future = new CompletableFuture<>(); + + String prop = System.getProperty("runelite.rtconf"); + if (!Strings.isNullOrEmpty(prop)) + { + try + { + log.info("Using local runtime config"); + + String strConf = new String(Files.readAllBytes(Paths.get(prop)), StandardCharsets.UTF_8); + RuntimeConfig conf = RuneLiteAPI.GSON.fromJson(strConf, RuntimeConfig.class); + future.complete(conf); + return future; + } + catch (IOException e) + { + throw new RuntimeException("failed to load override runtime config", e); + } + } + + Request request = new Request.Builder() + .url(RuneLiteProperties.getRuneLiteConfig()) + .build(); + + okHttpClient.newCall(request).enqueue(new Callback() + { + @Override + public void onFailure(Call call, IOException e) + { + future.completeExceptionally(e); + } + + @Override + public void onResponse(Call call, Response response) + { + try // NOPMD: UseTryWithResources + { + RuntimeConfig config = RuneLiteAPI.GSON.fromJson(response.body().charStream(), RuntimeConfig.class); + future.complete(config); + } + catch (Throwable ex) + { + future.completeExceptionally(ex); + } + finally + { + response.close(); + } + } + }); + return future; + } +} \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/runelite.properties b/runelite-client/src/main/resources/net/runelite/client/runelite.properties index 8952e01593..049a6443fb 100644 --- a/runelite-client/src/main/resources/net/runelite/client/runelite.properties +++ b/runelite-client/src/main/resources/net/runelite/client/runelite.properties @@ -18,4 +18,5 @@ runelite.imgur.client.id=30d71e5f6860809 runelite.api.base=https://api.runelite.net/runelite-${project.version} runelite.session=https://api.runelite.net/session runelite.static.base=https://static.runelite.net -runelite.ws=https://api.runelite.net/ws \ No newline at end of file +runelite.ws=https://api.runelite.net/ws +runelite.config=https://static.runelite.net/config.json \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java index 50692a915a..983bf613f2 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java @@ -89,7 +89,7 @@ public class PluginManagerTest .thenThrow(new RuntimeException("in plugin manager test")); Injector injector = Guice.createInjector(Modules - .override(new RuneLiteModule(okHttpClient, () -> null, true, false, + .override(new RuneLiteModule(okHttpClient, () -> null, () -> null, true, false, RuneLite.DEFAULT_SESSION_FILE, RuneLite.DEFAULT_CONFIG_FILE)) .with(BoundFieldModule.of(this))); From aaf4adaea0a5c957f5f98aec3dedd80e859ca532 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Feb 2022 16:55:21 -0500 Subject: [PATCH 06/12] itemmanager: sanity check wiki prices Try to avoid excessive item price manipulation by requiring the guide price to be within a configured threshold of the official price. --- runelite-client/pmd-ruleset.xml | 7 +++- .../net/runelite/client/game/ItemManager.java | 34 +++++++++++++++++-- .../chatcommands/ChatCommandsPlugin.java | 2 +- .../GrandExchangeSearchPanel.java | 2 +- 4 files changed, 40 insertions(+), 5 deletions(-) diff --git a/runelite-client/pmd-ruleset.xml b/runelite-client/pmd-ruleset.xml index 3cdece031e..ebab6e2dcf 100644 --- a/runelite-client/pmd-ruleset.xml +++ b/runelite-client/pmd-ruleset.xml @@ -67,7 +67,12 @@ - + + + + + diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java index e7af66d994..2b4f9ec7f1 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java @@ -28,6 +28,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; +import com.google.inject.Inject; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.IOException; @@ -41,7 +42,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import javax.inject.Inject; +import javax.inject.Named; import javax.inject.Singleton; import lombok.Value; import lombok.extern.slf4j.Slf4j; @@ -84,6 +85,14 @@ public class ItemManager private final ItemClient itemClient; private final RuneLiteConfig runeLiteConfig; + @Inject(optional = true) + @Named("activePriceThreshold") + private double activePriceThreshold = 5; + + @Inject(optional = true) + @Named("lowPriceThreshold") + private int lowPriceThreshold = 1000; + private Map itemPrices = Collections.emptyMap(); private Map itemStats = Collections.emptyMap(); private final LoadingCache itemImages; @@ -288,7 +297,7 @@ public class ItemManager if (ip != null) { - price = useWikiPrice && ip.getWikiPrice() > 0 ? ip.getWikiPrice() : ip.getPrice(); + price = useWikiPrice ? getWikiPrice(ip) : ip.getPrice(); } } else @@ -302,6 +311,27 @@ public class ItemManager return price; } + /** + * Get the wiki price for an item, with checks to try and avoid excessive price manipulation + * @param itemPrice + * @return + */ + public int getWikiPrice(ItemPrice itemPrice) + { + final int wikiPrice = itemPrice.getWikiPrice(); + final int jagPrice = itemPrice.getPrice(); + if (wikiPrice <= 0) + { + return jagPrice; + } + if (wikiPrice <= lowPriceThreshold) + { + return wikiPrice; + } + int d = jagPrice - (int) (jagPrice * activePriceThreshold); + return wikiPrice >= jagPrice - d && wikiPrice <= jagPrice + d ? wikiPrice : jagPrice; + } + /** * Look up an item's stats * @param itemId item id diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 84b87bcb88..3d86d52ad9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -1287,7 +1287,7 @@ public class ChatCommandsPlugin extends Plugin ItemPrice item = retrieveFromList(results, search); int itemId = item.getId(); - int itemPrice = runeLiteConfig.useWikiItemPrices() && item.getWikiPrice() > 0 ? item.getWikiPrice() : item.getPrice(); + int itemPrice = runeLiteConfig.useWikiItemPrices() ? itemManager.getWikiPrice(item) : item.getPrice(); final ChatMessageBuilder builder = new ChatMessageBuilder() .append(ChatColorType.NORMAL) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java index ae22917b37..9cbfaf667b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java @@ -215,7 +215,7 @@ class GrandExchangeSearchPanel extends JPanel ItemComposition itemComp = itemManager.getItemComposition(itemId); ItemStats itemStats = itemManager.getItemStats(itemId, false); - int itemPrice = useActivelyTradedPrice && item.getWikiPrice() > 0 ? item.getWikiPrice() : item.getPrice(); + int itemPrice = useActivelyTradedPrice ? itemManager.getWikiPrice(item) : item.getPrice(); int itemLimit = itemStats != null ? itemStats.getGeLimit() : 0; final int haPrice = itemComp.getHaPrice(); AsyncBufferedImage itemImage = itemManager.getImage(itemId); From 3f3f3dc97be8b3c718c56bcd32e8b21bf9aa08a2 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 3 Feb 2022 18:57:02 -0500 Subject: [PATCH 07/12] plugins: prevent plugins from overriding equals and hashCode This has no legitimate use and several parts of the core assume that they are not overridden --- .../java/net/runelite/client/plugins/Plugin.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java index a36a1f40e6..f1b1c95ea3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java @@ -32,6 +32,18 @@ public abstract class Plugin implements Module { protected Injector injector; + @Override + public final int hashCode() + { + return super.hashCode(); + } + + @Override + public final boolean equals(Object obj) + { + return super.equals(obj); + } + @Override public void configure(Binder binder) { From e9b138447ece24ca2125453024b8e8311a7f921c Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 3 Feb 2022 17:08:43 -0700 Subject: [PATCH 08/12] config: implement toString/equals/hashCode for Config proxies --- .../client/config/ConfigInvocationHandler.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java index 1553bc904a..217afe6b07 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigInvocationHandler.java @@ -63,6 +63,21 @@ class ConfigInvocationHandler implements InvocationHandler Class iface = proxy.getClass().getInterfaces()[0]; + if ("toString".equals(method.getName()) && args == null) + { + return iface.getSimpleName(); + } + + if ("hashCode".equals(method.getName()) && args == null) + { + return System.identityHashCode(proxy); + } + + if ("equals".equals(method.getName()) && args != null && args.length == 1) + { + return proxy == args[0]; + } + ConfigGroup group = iface.getAnnotation(ConfigGroup.class); ConfigItem item = method.getAnnotation(ConfigItem.class); From 43e8a57cadc232c151c002cbfe5a7e4dd95e8b53 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 4 Feb 2022 18:01:38 -0500 Subject: [PATCH 09/12] ground items: make hotkey configurable Co-authored-by: David Goldstein --- .../grounditems/GroundItemHotkeyListener.java | 77 +++++++++++++++++++ ...tener.java => GroundItemMouseAdapter.java} | 56 +------------- .../grounditems/GroundItemsConfig.java | 14 +++- .../grounditems/GroundItemsPlugin.java | 13 ++-- .../runelite/client/util/HotkeyListener.java | 5 ++ 5 files changed, 104 insertions(+), 61 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemHotkeyListener.java rename runelite-client/src/main/java/net/runelite/client/plugins/grounditems/{GroundItemInputListener.java => GroundItemMouseAdapter.java} (71%) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemHotkeyListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemHotkeyListener.java new file mode 100644 index 0000000000..a83210b0a7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemHotkeyListener.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018, Seth + * 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.grounditems; + +import java.time.Duration; +import java.time.Instant; +import javax.inject.Inject; +import net.runelite.client.util.HotkeyListener; + +class GroundItemHotkeyListener extends HotkeyListener +{ + private final GroundItemsPlugin plugin; + private final GroundItemsConfig config; + + private Instant lastPress; + + @Inject + private GroundItemHotkeyListener(GroundItemsPlugin plugin, GroundItemsConfig config) + { + super(config::hotkey); + + this.plugin = plugin; + this.config = config; + } + + @Override + public void hotkeyPressed() + { + if (plugin.isHideAll()) + { + plugin.setHideAll(false); + plugin.setHotKeyPressed(true); + lastPress = null; + } + else if (lastPress != null && !plugin.isHotKeyPressed() && config.doubleTapDelay() > 0 && Duration.between(lastPress, Instant.now()).compareTo(Duration.ofMillis(config.doubleTapDelay())) < 0) + { + plugin.setHideAll(true); + lastPress = null; + } + else + { + plugin.setHotKeyPressed(true); + lastPress = Instant.now(); + } + } + + @Override + public void hotkeyReleased() + { + plugin.setHotKeyPressed(false); + plugin.setTextBoxBounds(null); + plugin.setHiddenBoxBounds(null); + plugin.setHighlightBoxBounds(null); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemInputListener.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemMouseAdapter.java similarity index 71% rename from runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemInputListener.java rename to runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemMouseAdapter.java index da6c3a80c5..a61be015db 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemInputListener.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemMouseAdapter.java @@ -25,69 +25,16 @@ package net.runelite.client.plugins.grounditems; import java.awt.Point; -import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; -import java.time.Duration; -import java.time.Instant; import javax.inject.Inject; import javax.swing.SwingUtilities; -import net.runelite.client.input.KeyListener; import net.runelite.client.input.MouseAdapter; -public class GroundItemInputListener extends MouseAdapter implements KeyListener +class GroundItemMouseAdapter extends MouseAdapter { - private static final int HOTKEY = KeyEvent.VK_ALT; - - private Instant lastPress; - @Inject private GroundItemsPlugin plugin; - @Inject - private GroundItemsConfig config; - - @Override - public void keyTyped(KeyEvent e) - { - - } - - @Override - public void keyPressed(KeyEvent e) - { - if (e.getKeyCode() == HOTKEY) - { - if (plugin.isHideAll()) - { - plugin.setHideAll(false); - plugin.setHotKeyPressed(true); - lastPress = null; - } - else if (lastPress != null && !plugin.isHotKeyPressed() && config.doubleTapDelay() > 0 && Duration.between(lastPress, Instant.now()).compareTo(Duration.ofMillis(config.doubleTapDelay())) < 0) - { - plugin.setHideAll(true); - lastPress = null; - } - else - { - plugin.setHotKeyPressed(true); - lastPress = Instant.now(); - } - } - } - - @Override - public void keyReleased(KeyEvent e) - { - if (e.getKeyCode() == HOTKEY) - { - plugin.setHotKeyPressed(false); - plugin.setTextBoxBounds(null); - plugin.setHiddenBoxBounds(null); - plugin.setHighlightBoxBounds(null); - } - } - @Override public MouseEvent mousePressed(MouseEvent e) { @@ -134,4 +81,3 @@ public class GroundItemInputListener extends MouseAdapter implements KeyListener return e; } } - diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsConfig.java index 5869c07208..1031765645 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsConfig.java @@ -30,8 +30,9 @@ import net.runelite.client.config.Alpha; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; -import net.runelite.client.config.Units; import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Keybind; +import net.runelite.client.config.Units; import net.runelite.client.plugins.grounditems.config.DespawnTimerMode; import net.runelite.client.plugins.grounditems.config.HighlightTier; import net.runelite.client.plugins.grounditems.config.ItemHighlightMode; @@ -447,4 +448,15 @@ public interface GroundItemsConfig extends Config { return Lootbeam.Style.MODERN; } + + @ConfigItem( + keyName = "hotkey", + name = "Hotkey", + description = "Configures the hotkey used by the Ground Items plugin", + position = 33 + ) + default Keybind hotkey() + { + return Keybind.ALT; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java index af5282db14..c4524bf353 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java @@ -140,7 +140,10 @@ public class GroundItemsPlugin extends Plugin private List highlightedItemsList = new CopyOnWriteArrayList<>(); @Inject - private GroundItemInputListener inputListener; + private GroundItemHotkeyListener hotkeyListener; + + @Inject + private GroundItemMouseAdapter mouseAdapter; @Inject private MouseManager mouseManager; @@ -191,8 +194,8 @@ public class GroundItemsPlugin extends Plugin protected void startUp() { overlayManager.add(overlay); - mouseManager.registerMouseListener(inputListener); - keyManager.registerKeyListener(inputListener); + mouseManager.registerMouseListener(mouseAdapter); + keyManager.registerKeyListener(hotkeyListener); executor.execute(this::reset); lastUsedItem = -1; } @@ -201,8 +204,8 @@ public class GroundItemsPlugin extends Plugin protected void shutDown() { overlayManager.remove(overlay); - mouseManager.unregisterMouseListener(inputListener); - keyManager.unregisterKeyListener(inputListener); + mouseManager.unregisterMouseListener(mouseAdapter); + keyManager.unregisterKeyListener(hotkeyListener); highlightedItems.invalidateAll(); highlightedItems = null; hiddenItems.invalidateAll(); diff --git a/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java b/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java index 2099564846..8451ca134b 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java +++ b/runelite-client/src/main/java/net/runelite/client/util/HotkeyListener.java @@ -85,10 +85,15 @@ public abstract class HotkeyListener implements KeyListener { isPressed = false; isConsumingTyped = false; + hotkeyReleased(); } } public void hotkeyPressed() { } + + public void hotkeyReleased() + { + } } From 5d99ec56f21746ee1f4ac07280e9784586f2c4e3 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 5 Feb 2022 21:56:50 -0500 Subject: [PATCH 10/12] defaultworld: always changeWorld on client thread --- .../defaultworld/DefaultWorldPlugin.java | 86 ++++++++----------- 1 file changed, 37 insertions(+), 49 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/defaultworld/DefaultWorldPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/defaultworld/DefaultWorldPlugin.java index 9010f9a8d0..1932045138 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/defaultworld/DefaultWorldPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/defaultworld/DefaultWorldPlugin.java @@ -29,7 +29,8 @@ import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.GameState; -import net.runelite.api.events.GameStateChanged; +import net.runelite.api.events.WorldChanged; +import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.SessionOpen; @@ -51,27 +52,33 @@ public class DefaultWorldPlugin extends Plugin @Inject private Client client; + @Inject + private ClientThread clientThread; + @Inject private DefaultWorldConfig config; @Inject private WorldService worldService; - private int worldCache; - private boolean worldChangeRequired; - @Override - protected void startUp() throws Exception + protected void startUp() { - worldChangeRequired = true; - applyWorld(); + clientThread.invokeLater(() -> + { + if (client.getGameState().getState() < GameState.LOGIN_SCREEN.getState()) + { + return false; + } + + applyWorld(); + return true; + }); } @Override - protected void shutDown() throws Exception + protected void shutDown() { - worldChangeRequired = true; - changeWorld(worldCache); } @Provides @@ -83,29 +90,25 @@ public class DefaultWorldPlugin extends Plugin @Subscribe public void onSessionOpen(SessionOpen event) { - worldChangeRequired = true; - applyWorld(); + clientThread.invokeLater(this::applyWorld); } @Subscribe - public void onGameStateChanged(GameStateChanged event) + public void onWorldChanged(WorldChanged worldChanged) { - if (event.getGameState() == GameState.LOGGED_IN) - { - config.lastWorld(client.getWorld()); - } - - applyWorld(); + int world = client.getWorld(); + config.lastWorld(world); + log.debug("Saving last world {}", world); } - private void changeWorld(int newWorld) + private void applyWorld() { - if (!worldChangeRequired || client.getGameState() != GameState.LOGIN_SCREEN) + if (client.getGameState() != GameState.LOGIN_SCREEN) { return; } - worldChangeRequired = false; + final int newWorld = config.useLastWorld() ? config.lastWorld() : config.getWorld(); int correctedWorld = newWorld < 300 ? newWorld + 300 : newWorld; // Old School RuneScape worlds start on 301 so don't even bother trying to find lower id ones @@ -116,7 +119,6 @@ public class DefaultWorldPlugin extends Plugin } final WorldResult worldResult = worldService.getWorlds(); - if (worldResult == null) { log.warn("Failed to lookup worlds."); @@ -124,35 +126,21 @@ public class DefaultWorldPlugin extends Plugin } final World world = worldResult.findWorld(correctedWorld); - - if (world != null) - { - final net.runelite.api.World rsWorld = client.createWorld(); - rsWorld.setActivity(world.getActivity()); - rsWorld.setAddress(world.getAddress()); - rsWorld.setId(world.getId()); - rsWorld.setPlayerCount(world.getPlayers()); - rsWorld.setLocation(world.getLocation()); - rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes())); - - client.changeWorld(rsWorld); - log.debug("Applied new world {}", correctedWorld); - } - else + if (world == null) { log.warn("World {} not found.", correctedWorld); - } - } - - private void applyWorld() - { - if (worldCache == 0) - { - worldCache = client.getWorld(); - log.debug("Stored old world {}", worldCache); + return; } - final int newWorld = !config.useLastWorld() ? config.getWorld() : config.lastWorld(); - changeWorld(newWorld); + final net.runelite.api.World rsWorld = client.createWorld(); + rsWorld.setActivity(world.getActivity()); + rsWorld.setAddress(world.getAddress()); + rsWorld.setId(world.getId()); + rsWorld.setPlayerCount(world.getPlayers()); + rsWorld.setLocation(world.getLocation()); + rsWorld.setTypes(WorldUtil.toWorldTypes(world.getTypes())); + + client.changeWorld(rsWorld); + log.debug("Applied new world {}", correctedWorld); } } From e17baf1aee342282ea73cbad40844f47568eefc8 Mon Sep 17 00:00:00 2001 From: LootBagger <91767458+LootBagger@users.noreply.github.com> Date: Sat, 5 Feb 2022 10:38:30 -0800 Subject: [PATCH 11/12] plugin manager: fix plugins with multiple dependencies If one plugin has multiple dependencies this would throw a concurrent modification exception due to iterating the successors after removing the edge Fill out javadoc for topologicalSort Co-authored-by: Adam --- .../client/plugins/PluginManager.java | 13 +++++++---- .../client/plugins/PluginManagerTest.java | 23 +++++++++++++++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index c9c88b669a..ccbf5e61c7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -631,11 +631,14 @@ public class PluginManager /** * Topologically sort a graph. Uses Kahn's algorithm. * - * @param graph - * @param - * @return + * @param graph - A directed graph + * @param - The type of the item contained in the nodes of the graph + * @return - A topologically sorted list corresponding to graph. + *

+ * Multiple invocations with the same arguments may return lists that are not equal. */ - private List topologicalSort(Graph graph) + @VisibleForTesting + static List topologicalSort(Graph graph) { MutableGraph graphCopy = Graphs.copyOf(graph); List l = new ArrayList<>(); @@ -650,7 +653,7 @@ public class PluginManager l.add(n); - for (T m : graphCopy.successors(n)) + for (T m : new HashSet<>(graphCopy.successors(n))) { graphCopy.removeEdge(n, m); if (graphCopy.inDegree(m) == 0) diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java index 983bf613f2..3166552b6a 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/PluginManagerTest.java @@ -24,6 +24,8 @@ */ package net.runelite.client.plugins; +import com.google.common.graph.GraphBuilder; +import com.google.common.graph.MutableGraph; import com.google.common.reflect.ClassPath; import com.google.common.reflect.ClassPath.ClassInfo; import com.google.inject.Guice; @@ -40,6 +42,7 @@ import java.io.PrintWriter; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; import net.runelite.api.Client; @@ -51,6 +54,7 @@ import net.runelite.client.eventbus.EventBus; import okhttp3.OkHttpClient; import okhttp3.Request; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -208,4 +212,23 @@ public class PluginManagerTest } } + @Test + public void testTopologicalSort() + { + MutableGraph graph = GraphBuilder + .directed() + .build(); + + graph.addNode(1); + graph.addNode(2); + graph.addNode(3); + + graph.putEdge(1, 2); + graph.putEdge(1, 3); + + List sorted = PluginManager.topologicalSort(graph); + + assertTrue(sorted.indexOf(1) < sorted.indexOf(2)); + assertTrue(sorted.indexOf(1) < sorted.indexOf(3)); + } } From 57622f3badf1f7c88a4a2e59d75540dac4647bab Mon Sep 17 00:00:00 2001 From: LootBagger <91767458+LootBagger@users.noreply.github.com> Date: Sat, 5 Feb 2022 11:24:18 -0800 Subject: [PATCH 12/12] plugin manager: optimize plugin dependency sorting Previously, PluginManager.loadPlugins would make a graph of reverse dependencies, topologically sort them, then reverse the list. This commit changes loadPlugins to build a graph of dependencies so no reversal is needed. Co-authored-by: Adam --- .../java/net/runelite/client/plugins/PluginManager.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index ccbf5e61c7..55f8405b39 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -26,7 +26,6 @@ package net.runelite.client.plugins; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; -import com.google.common.collect.Lists; import com.google.common.graph.Graph; import com.google.common.graph.GraphBuilder; import com.google.common.graph.Graphs; @@ -320,8 +319,7 @@ public class PluginManager continue; } - Class pluginClass = (Class) clazz; - graph.addNode(pluginClass); + graph.addNode((Class) clazz); } // Build plugin graph @@ -333,7 +331,7 @@ public class PluginManager { if (graph.nodes().contains(pluginDependency.value())) { - graph.putEdge(pluginClazz, pluginDependency.value()); + graph.putEdge(pluginDependency.value(), pluginClazz); } } } @@ -344,7 +342,6 @@ public class PluginManager } List> sortedPlugins = topologicalSort(graph); - sortedPlugins = Lists.reverse(sortedPlugins); int loaded = 0; List newPlugins = new ArrayList<>();