From 116036d542b4e8382a52f168adcd2b40c13c896c Mon Sep 17 00:00:00 2001 From: Cyborger1 <45152844+Cyborger1@users.noreply.github.com> Date: Thu, 24 Feb 2022 23:32:16 -0500 Subject: [PATCH 01/32] clues: fix Guardian mummy capitalization --- .../runelite/client/plugins/cluescrolls/clues/CrypticClue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java index 19bbf55731..785ffce336 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CrypticClue.java @@ -153,7 +153,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc new CrypticClue("Search the crates in the Port Sarim Fishing shop.", CRATE_9534, new WorldPoint(3012, 3222, 0), "Search the crates, by the door, in Gerrant's Fishy Business in Port Sarim."), new CrypticClue("Speak to The Lady of the Lake.", "The Lady of the Lake", new WorldPoint(2924, 3405, 0), "Talk to The Lady of the Lake in Taverley."), new CrypticClue("Rotting next to a ditch. Dig next to the fish.", new WorldPoint(3547, 3183, 0), "Dig next to a fishing spot on the south-east side of Burgh de Rott."), - new CrypticClue("The King's magic won't be wasted by me.", "Guardian Mummy", new WorldPoint(1934, 4427, 0), "Talk to the Guardian mummy inside the Pyramid Plunder minigame in Sophanem."), + new CrypticClue("The King's magic won't be wasted by me.", "Guardian mummy", new WorldPoint(1934, 4427, 0), "Talk to the Guardian mummy inside the Pyramid Plunder minigame in Sophanem."), new CrypticClue("Dig where the forces of Zamorak and Saradomin collide.", new WorldPoint(3049, 4839, 0), "Dig next to the law rift in the Abyss."), new CrypticClue("Search the boxes in the goblin house near Lumbridge.", BOXES, new WorldPoint(3245, 3245, 0), "Goblin house on the eastern side of the river outside of Lumbridge."), new CrypticClue("W marks the spot.", new WorldPoint(2867, 3546, 0), "Dig in the middle of the Warriors' Guild entrance hall."), From d94abb884d7aa607746aa81f361ac894dbfa5b73 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Thu, 24 Feb 2022 21:02:24 -0800 Subject: [PATCH 02/32] slayer: Fix name matching The Slayer plugin highlights target monsters based on their name rather than NPC ID, as many common monsters (skeletons, zombies, etc.) have nearly endless variations for different models and combat levels. Previously, this name matching was done via a simple `String#contains()`, which led to some incorrect matches such as pirates being highlighted while on rat tasks and Jonny the beard being highlighted while on bear tasks. This commit changes matching to use regex to match string boundaries or whitespace at either end of the task string, ensuring these substring matches can only happen when word breaks occur. The only known existing case where this would apply is for baby dragons and brutal dragons, which are valid alternatives for their respective chromatic dragon tasks. --- .../client/plugins/slayer/SlayerPlugin.java | 32 +++++++++++-------- .../plugins/slayer/SlayerPluginTest.java | 28 ++++++++++++++++ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java index b535ec643a..6c7b95ebd9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java @@ -203,7 +203,7 @@ public class SlayerPlugin extends Plugin private int cachedXp = -1; private Instant infoTimer; private boolean loginFlag; - private final List targetNames = new ArrayList<>(); + private final List targetNames = new ArrayList<>(); public final Function isTarget = (n) -> { @@ -664,7 +664,8 @@ public class SlayerPlugin extends Plugin SlayerUnlock.GROTESQUE_GUARDIAN_DOUBLE_COUNT.isEnabled(client); } - private boolean isTarget(NPC npc) + @VisibleForTesting + boolean isTarget(NPC npc) { if (targetNames.isEmpty()) { @@ -681,16 +682,15 @@ public class SlayerPlugin extends Plugin .replace('\u00A0', ' ') .toLowerCase(); - for (String target : targetNames) + for (Pattern target : targetNames) { - if (name.contains(target)) - { - if (ArrayUtils.contains(composition.getActions(), "Attack") + final Matcher targetMatcher = target.matcher(name); + if (targetMatcher.find() + && (ArrayUtils.contains(composition.getActions(), "Attack") // Pick action is for zygomite-fungi - || ArrayUtils.contains(composition.getActions(), "Pick")) - { - return true; - } + || ArrayUtils.contains(composition.getActions(), "Pick"))) + { + return true; } } return false; @@ -703,13 +703,18 @@ public class SlayerPlugin extends Plugin if (task != null) { Arrays.stream(task.getTargetNames()) - .map(String::toLowerCase) + .map(SlayerPlugin::targetNamePattern) .forEach(targetNames::add); - targetNames.add(taskName.toLowerCase().replaceAll("s$", "")); + targetNames.add(targetNamePattern(taskName.replaceAll("s$", ""))); } } + private static Pattern targetNamePattern(final String targetName) + { + return Pattern.compile("(?:\\s|^)" + targetName + "(?:\\s|$)", Pattern.CASE_INSENSITIVE); + } + private void rebuildTargetList() { targets.clear(); @@ -723,7 +728,8 @@ public class SlayerPlugin extends Plugin } } - private void setTask(String name, int amt, int initAmt) + @VisibleForTesting + void setTask(String name, int amt, int initAmt) { setTask(name, amt, initAmt, null); } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java index 3fc570c233..6ce2d86783 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java @@ -60,6 +60,8 @@ import net.runelite.client.game.npcoverlay.NpcOverlayService; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -926,4 +928,30 @@ public class SlayerPluginTest assertEquals("Suqahs", slayerPlugin.getTaskName()); assertEquals(229, slayerPlugin.getAmount()); // 2 kills } + + @Test + public void npcMatching() + { + assertTrue(matches("Abyssal demon", Task.ABYSSAL_DEMONS)); + assertTrue(matches("Baby blue dragon", Task.BLUE_DRAGONS)); + assertTrue(matches("Duck", Task.BIRDS)); + assertTrue(matches("Donny the Lad", Task.BANDITS)); + + assertFalse(matches("Rat", Task.PIRATES)); + assertFalse(matches("Wolf", Task.WEREWOLVES)); + assertFalse(matches("Scorpia's offspring", Task.SCORPIA)); + assertFalse(matches("Jonny the beard", Task.BEARS)); + } + + private boolean matches(final String npcName, final Task task) + { + final NPC npc = mock(NPC.class); + final NPCComposition comp = mock(NPCComposition.class); + when(npc.getTransformedComposition()).thenReturn(comp); + when(comp.getName()).thenReturn(npcName); + when(comp.getActions()).thenReturn(new String[] { "Attack" }); + + slayerPlugin.setTask(task.getName(), 0, 0); + return slayerPlugin.isTarget(npc); + } } From d8c67fa9b8c8e95d57cb43c9a731fcca108341f8 Mon Sep 17 00:00:00 2001 From: Cyborger1 <45152844+Cyborger1@users.noreply.github.com> Date: Fri, 25 Feb 2022 13:04:13 -0500 Subject: [PATCH 03/32] clientui: run PluginPanel#onDeactivate when switching panels --- .../src/main/java/net/runelite/client/ui/ClientUI.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java index f08e97807a..20a2f378d1 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java @@ -983,6 +983,13 @@ public class ClientUI int width = panel.getWrappedPanel().getPreferredSize().width; int expandBy = pluginPanel != null ? pluginPanel.getWrappedPanel().getPreferredSize().width - width : width; + + // Deactivate previously active panel + if (pluginPanel != null) + { + pluginPanel.onDeactivate(); + } + pluginPanel = panel; // Expand sidebar From 15a393fe86a5c6eab4af8729634fcf53f08a3e2f Mon Sep 17 00:00:00 2001 From: Tony Wang <74754989+hwang-pku@users.noreply.github.com> Date: Sat, 26 Feb 2022 03:12:21 +0800 Subject: [PATCH 04/32] cache: script: use linkedhashmap for switch map The switch maps are iterated in the assembler and disassembler and the generated code depends on the iteration order --- .../java/net/runelite/cache/script/assembler/ScriptWriter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java b/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java index 2797d4a4c6..429971d237 100644 --- a/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java +++ b/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java @@ -26,6 +26,7 @@ package net.runelite.cache.script.assembler; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -267,7 +268,7 @@ public class ScriptWriter extends rs2asmBaseListener continue; } - Map map = maps[index++] = new HashMap<>(); + Map map = maps[index++] = new LinkedHashMap<>(); for (LookupCase scase : lswitch.getCases()) { From ffc5380f28b8be9d35dd235187c51df49b6695a3 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 25 Feb 2022 14:30:06 -0500 Subject: [PATCH 05/32] cache: script: remove unused import --- .../java/net/runelite/cache/script/assembler/ScriptWriter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java b/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java index 429971d237..e20076099c 100644 --- a/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java +++ b/cache/src/main/java/net/runelite/cache/script/assembler/ScriptWriter.java @@ -25,7 +25,6 @@ package net.runelite.cache.script.assembler; import java.util.ArrayList; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; From 5d7edacd2125f9c66cc78220eda21e6272932718 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 27 Feb 2022 12:32:41 -0500 Subject: [PATCH 06/32] item manager: fix active price threshold calculation This was meant to keep items within 5x of their Jagex price, but the logic was only correct if activePriceThreshold <= 1.0. --- .../net/runelite/client/game/ItemManager.java | 3 +- .../runelite/client/game/ItemManagerTest.java | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/game/ItemManagerTest.java 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 2b4f9ec7f1..b2847da18f 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 @@ -328,8 +328,7 @@ public class ItemManager { return wikiPrice; } - int d = jagPrice - (int) (jagPrice * activePriceThreshold); - return wikiPrice >= jagPrice - d && wikiPrice <= jagPrice + d ? wikiPrice : jagPrice; + return wikiPrice < jagPrice * activePriceThreshold ? wikiPrice : jagPrice; } /** diff --git a/runelite-client/src/test/java/net/runelite/client/game/ItemManagerTest.java b/runelite-client/src/test/java/net/runelite/client/game/ItemManagerTest.java new file mode 100644 index 0000000000..dfcc12242b --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/game/ItemManagerTest.java @@ -0,0 +1,98 @@ +/* + * 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.game; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import java.util.concurrent.ScheduledExecutorService; +import javax.inject.Named; +import net.runelite.api.Client; +import net.runelite.api.ItemID; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.RuneLiteConfig; +import net.runelite.http.api.item.ItemPrice; +import static org.junit.Assert.assertEquals; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class ItemManagerTest +{ + @Inject + private ItemManager itemManager; + + @Mock + @Bind + private Client client; + + @Mock + @Bind + private ScheduledExecutorService scheduledExecutorService; + + @Mock + @Bind + private ClientThread clientThread; + + @Mock + @Bind + private ItemClient itemClient; + + @Mock + @Bind + private RuneLiteConfig runeLiteConfig; + + @Bind + @Named("activePriceThreshold") + private double activePriceThreshold = 5; + + @Bind + @Named("lowPriceThreshold") + private int lowPriceThreshold = 1000; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testGetWikiPrice() + { + ItemPrice itemPrice = new ItemPrice(); + itemPrice.setId(ItemID.YEW_SEED); + itemPrice.setName("Yew seed"); + itemPrice.setPrice(47_975); + itemPrice.setWikiPrice(50_754); + assertEquals(itemPrice.getWikiPrice(), itemManager.getWikiPrice(itemPrice)); + + itemPrice.setWikiPrice(300_000); // outside of 5x range + assertEquals(itemPrice.getPrice(), itemManager.getWikiPrice(itemPrice)); + } +} \ No newline at end of file From cda29d36b8ed19de9b68d02b3f2fa736cfe3634d Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 27 Feb 2022 22:57:00 -0500 Subject: [PATCH 07/32] idle notifier: disable by default --- .../client/plugins/idlenotifier/IdleNotifierPlugin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index 987ecedabb..5cae67799b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -60,7 +60,8 @@ import net.runelite.client.plugins.PluginDescriptor; @PluginDescriptor( name = "Idle Notifier", description = "Send a notification when going idle, or when HP/Prayer reaches a threshold", - tags = {"health", "hitpoints", "notifications", "prayer"} + tags = {"health", "hitpoints", "notifications", "prayer"}, + enabledByDefault = false ) public class IdleNotifierPlugin extends Plugin { From e839dc15c3b41308de1619b120dd013da7ffb980 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Mar 2022 09:54:26 -0500 Subject: [PATCH 08/32] api: expand item api for inventory model replacement --- .../main/java/net/runelite/api/Client.java | 14 +++ .../net/runelite/api/ItemComposition.java | 115 ++++++++++++++++-- 2 files changed, 116 insertions(+), 13 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index e7616e4ea0..928ee839e4 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -397,6 +397,20 @@ public interface Client extends GameEngine @Nullable SpritePixels createItemSprite(int itemId, int quantity, int border, int shadowColor, @MagicConstant(valuesFromClass = ItemQuantityMode.class) int stackable, boolean noted, int scale); + /** + * Get the item model cache. These models are used for drawing widgets of type {@link net.runelite.api.widgets.WidgetType#MODEL} + * and inventory item icons + * @return + */ + NodeCache getItemModelCache(); + + /** + * Get the item sprite cache. These are 2d SpritePixels which are used to raster item images on the inventory and + * on widgets of type {@link net.runelite.api.widgets.WidgetType#GRAPHIC} + * @return + */ + NodeCache getItemSpriteCache(); + /** * Loads and creates the sprite images of the passed archive and file IDs. * diff --git a/runelite-api/src/main/java/net/runelite/api/ItemComposition.java b/runelite-api/src/main/java/net/runelite/api/ItemComposition.java index 37407724cb..7dca8a5669 100644 --- a/runelite-api/src/main/java/net/runelite/api/ItemComposition.java +++ b/runelite-api/src/main/java/net/runelite/api/ItemComposition.java @@ -32,12 +32,18 @@ import javax.annotation.Nullable; public interface ItemComposition extends ParamHolder { /** - * Gets the items name. + * Gets the item's name. * * @return the name of the item */ String getName(); + /** + * Sets the item's name. + * @param name the new name + */ + void setName(String name); + /** * Gets the items ID. * @@ -144,12 +150,6 @@ public interface ItemComposition extends ParamHolder */ void setShiftClickActionIndex(int shiftClickActionIndex); - /** - * Resets the menu action index of the shift-click action to its - * default value. - */ - void resetShiftClickActionIndex(); - /** * Gets the model ID of the inventory item. * @@ -158,20 +158,109 @@ public interface ItemComposition extends ParamHolder int getInventoryModel(); /** - * Since the client reuses item models, it stores colors that can be replaced. - * This returns what colors the item model will be replaced with. - * + * Set the model ID of the inventory item. You will also need to flush the item model cache and the item + * sprite cache to have the changes fully propagated after changing this value. + * @see Client#getItemModelCache() + * @see Client#getItemSpriteCache() + */ + void setInventoryModel(int model); + + /** + * Get the colors to be replaced on this item's model for this item. + * @see JagexColor + * @see ItemComposition#getColorToReplaceWith() + * @return the colors to be replaced + */ + @Nullable + short[] getColorToReplace(); + + /** + * Set the colors to be replaced on this item's model for this item. + * @see JagexColor + * @see ItemComposition#setColorToReplaceWith(short[]) + */ + void setColorToReplace(short[] colorsToReplace); + + /** + * Get the colors applied to this item's model for this item. + * @see JagexColor + * @see ItemComposition#getColorToReplace() * @return the colors to replace with */ @Nullable short[] getColorToReplaceWith(); /** - * Since the client reuses item models, it stores textures that can be replaced. - * This returns what textures the item model will be replaced with. - * + * Set the colors applied to this item's model for this item. + * @see JagexColor + * @see ItemComposition#setColorToReplace(short[]) + */ + void setColorToReplaceWith(short[] colorToReplaceWith); + + /** + * Get the textures to be replaced on this item's model for this item. + * @see ItemComposition#getTextureToReplaceWith() + * @return the textures to be replaced + */ + @Nullable + short[] getTextureToReplace(); + + /** + * Set the textures to be replaced on this item's model for this item. + * @see ItemComposition#setTextureToReplaceWith(short[]) + */ + void setTextureToReplace(short[] textureToFind); + + /** + * Get the textures applied to this item's model for this item. + * @see ItemComposition#getTextureToReplace() * @return the textures to replace with */ @Nullable short[] getTextureToReplaceWith(); + + /** + * Set the textures applied to this item's model for this item. + * @see ItemComposition#setTextureToReplace(short[]) + */ + void setTextureToReplaceWith(short[] textureToReplaceWith); + + /** + * Get the x angle for 2d item sprites used in the inventory. + * @see net.runelite.api.coords.Angle + * @return + */ + int getXan2d(); + + /** + * Get the y angle for 2d item sprites used in the inventory. + * @see net.runelite.api.coords.Angle + * @return + */ + int getYan2d(); + + /** + * Get the z angle for 2d item sprites used in the inventory. + * @see net.runelite.api.coords.Angle + * @return + */ + int getZan2d(); + + /** + * Set the x angle for 2d item sprites used in the inventory. + * @see net.runelite.api.coords.Angle + */ + void setXan2d(int angle); + + /** + * Set the y angle for 2d item sprites used in the inventory. + * @see net.runelite.api.coords.Angle + */ + void setYan2d(int angle); + + /** + * Set the z angle for 2d item sprites used in the inventory. + * @see net.runelite.api.coords.Angle + */ + void setZan2d(int angle); } From 5a4faef3508c58b4977d874d23cc02b9d9f4fdee Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 28 Feb 2022 21:04:37 -0500 Subject: [PATCH 09/32] client: add plugin sideloading This supports sideloading plugins out of the sideloaded-plugins directory when run in developer mode --- .../java/net/runelite/client/RuneLite.java | 1 + .../client/plugins/PluginClassLoader.java | 57 +++++++++++++++++++ .../client/plugins/PluginManager.java | 41 +++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/PluginClassLoader.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 6abb3b2208..96acb797ef 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -323,6 +323,7 @@ public class RuneLite // Load the plugins, but does not start them yet. // This will initialize configuration pluginManager.loadCorePlugins(); + pluginManager.loadSideLoadPlugins(); externalPluginManager.loadExternalPlugins(); SplashScreen.stage(.70, null, "Finalizing configuration"); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginClassLoader.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginClassLoader.java new file mode 100644 index 0000000000..3be3a4ed91 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginClassLoader.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016-2017, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; + +class PluginClassLoader extends URLClassLoader +{ + private final ClassLoader parent; + + PluginClassLoader(File plugin, ClassLoader parent) throws MalformedURLException + { + // null parent classloader, or else class path scanning includes everything from the main class loader + super(new URL[]{plugin.toURI().toURL()}, null); + + this.parent = parent; + } + + @Override + public Class loadClass(String name) throws ClassNotFoundException + { + try + { + return super.loadClass(name); + } + catch (ClassNotFoundException ex) + { + // fall back to main class loader + return parent.loadClass(name); + } + } +} \ No newline at end of file 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 55f8405b39..b2092e8d6c 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 @@ -37,6 +37,7 @@ import com.google.inject.CreationException; import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; +import java.io.File; import java.io.IOException; import java.lang.invoke.CallSite; import java.lang.invoke.LambdaMetafactory; @@ -87,6 +88,7 @@ public class PluginManager * Base package where the core plugins are */ private static final String PLUGIN_PACKAGE = "net.runelite.client.plugins"; + private static final File SIDELOADED_PLUGINS = new File(RuneLite.RUNELITE_DIR, "sideloaded-plugins"); private final boolean developerMode; private final boolean safeMode; @@ -275,6 +277,45 @@ public class PluginManager SplashScreen.stage(.60, .70, null, "Loading Plugins", loaded, total, false)); } + public void loadSideLoadPlugins() + { + if (!developerMode) + { + return; + } + + File[] files = SIDELOADED_PLUGINS.listFiles(); + if (files == null) + { + return; + } + + for (File f : files) + { + if (f.getName().endsWith(".jar")) + { + log.info("Side-loading plugin {}", f); + + try + { + ClassLoader classLoader = new PluginClassLoader(f, getClass().getClassLoader()); + + List> plugins = ClassPath.from(classLoader) + .getAllClasses() + .stream() + .map(ClassInfo::load) + .collect(Collectors.toList()); + + loadPlugins(plugins, null); + } + catch (PluginInstantiationException | IOException ex) + { + log.error("error sideloading plugin", ex); + } + } + } + } + public List loadPlugins(List> plugins, BiConsumer onPluginLoaded) throws PluginInstantiationException { MutableGraph> graph = GraphBuilder From 3ad8452d41ac422094f40b83e50e6ffbfe092cb4 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Mar 2022 22:02:16 -0500 Subject: [PATCH 10/32] cache: rename texture animation direction and speed --- .../net/runelite/cache/definitions/TextureDefinition.java | 4 ++-- .../runelite/cache/definitions/loaders/TextureLoader.java | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/cache/src/main/java/net/runelite/cache/definitions/TextureDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/TextureDefinition.java index d34bc5678a..820d73b6bd 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/TextureDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/TextureDefinition.java @@ -38,8 +38,8 @@ public class TextureDefinition public int[] field1780; public int[] field1781; public int[] field1786; - public int field1782; - public int field1783; + public int animationSpeed; + public int animationDirection; public transient int[] pixels; diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/TextureLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/TextureLoader.java index 3e9ff09e89..626f620d0d 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/TextureLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/TextureLoader.java @@ -26,13 +26,9 @@ package net.runelite.cache.definitions.loaders; import net.runelite.cache.definitions.TextureDefinition; import net.runelite.cache.io.InputStream; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class TextureLoader { - private static final Logger logger = LoggerFactory.getLogger(TextureLoader.class); - public TextureDefinition load(int id, byte[] b) { TextureDefinition def = new TextureDefinition(); @@ -77,8 +73,8 @@ public class TextureLoader def.field1786[var3] = is.readInt(); } - def.field1783 = is.readUnsignedByte(); - def.field1782 = is.readUnsignedByte(); + def.animationDirection = is.readUnsignedByte(); + def.animationSpeed = is.readUnsignedByte(); return def; } From 2520da4dcaea009142926376665d18bbe502c789 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 1 Mar 2022 22:02:44 -0500 Subject: [PATCH 11/32] gpu: move texture animation to gpu --- .../client/plugins/gpu/GpuPlugin.java | 38 ++++---- .../client/plugins/gpu/TextureManager.java | 91 +++++++------------ .../net/runelite/client/plugins/gpu/frag.glsl | 5 +- .../net/runelite/client/plugins/gpu/vert.glsl | 19 +++- 4 files changed, 66 insertions(+), 87 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 96c992f2da..23e2f4fe46 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -223,7 +223,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private int textureArrayId; private final GLBuffer uniformBuffer = new GLBuffer(); - private final float[] textureOffsets = new float[256]; private GpuIntBuffer vertexBuffer; private GpuFloatBuffer uvBuffer; @@ -291,12 +290,13 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private int uniTexTargetDimensions; private int uniUiAlphaOverlay; private int uniTextures; - private int uniTextureOffsets; + private int uniTextureAnimations; private int uniBlockSmall; private int uniBlockLarge; private int uniBlockMain; private int uniSmoothBanding; private int uniTextureLightMode; + private int uniTick; private int needsReset; @@ -665,6 +665,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks uniDrawDistance = gl.glGetUniformLocation(glProgram, "drawDistance"); uniColorBlindMode = gl.glGetUniformLocation(glProgram, "colorBlindMode"); uniTextureLightMode = gl.glGetUniformLocation(glProgram, "textureLightMode"); + uniTick = gl.glGetUniformLocation(glProgram, "tick"); uniTex = gl.glGetUniformLocation(glUiProgram, "tex"); uniTexSamplingMode = gl.glGetUniformLocation(glUiProgram, "samplingMode"); @@ -673,7 +674,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks uniUiColorBlindMode = gl.glGetUniformLocation(glUiProgram, "colorBlindMode"); uniUiAlphaOverlay = gl.glGetUniformLocation(glUiProgram, "alphaOverlay"); uniTextures = gl.glGetUniformLocation(glProgram, "textures"); - uniTextureOffsets = gl.glGetUniformLocation(glProgram, "textureOffsets"); + uniTextureAnimations = gl.glGetUniformLocation(glProgram, "textureAnimations"); uniBlockSmall = gl.glGetUniformBlockIndex(glSmallComputeProgram, "uniforms"); uniBlockLarge = gl.glGetUniformBlockIndex(glComputeProgram, "uniforms"); @@ -1222,18 +1223,25 @@ public class GpuPlugin extends Plugin implements DrawCallbacks gl.glClear(gl.GL_COLOR_BUFFER_BIT); // Draw 3d scene - final TextureProvider textureProvider = client.getTextureProvider(); final GameState gameState = client.getGameState(); - if (textureProvider != null && gameState.getState() >= GameState.LOADING.getState()) + if (gameState.getState() >= GameState.LOADING.getState()) { + final TextureProvider textureProvider = client.getTextureProvider(); if (textureArrayId == -1) { // lazy init textures as they may not be loaded at plugin start. // this will return -1 and retry if not all textures are loaded yet, too. textureArrayId = textureManager.initTextureArray(textureProvider, gl); + if (textureArrayId > -1) + { + // if texture upload is successful, compute and set texture animations + float[] texAnims = textureManager.computeTextureAnimations(textureProvider); + gl.glUseProgram(glProgram); + gl.glUniform2fv(uniTextureAnimations, texAnims.length, texAnims, 0); + gl.glUseProgram(0); + } } - final Texture[] textures = textureProvider.getTextures(); int renderWidthOff = viewportOffsetX; int renderHeightOff = viewportOffsetY; int renderCanvasHeight = canvasHeight; @@ -1285,6 +1293,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks gl.glUniform1f(uniSmoothBanding, config.smoothBanding() ? 0f : 1f); gl.glUniform1i(uniColorBlindMode, config.colorBlindMode().ordinal()); gl.glUniform1f(uniTextureLightMode, config.brightTextures() ? 1f : 0f); + gl.glUniform1i(uniTick, client.getGameCycle()); // Calculate projection matrix Matrix4 projectionMatrix = new Matrix4(); @@ -1295,24 +1304,9 @@ public class GpuPlugin extends Plugin implements DrawCallbacks projectionMatrix.translate(-client.getCameraX2(), -client.getCameraY2(), -client.getCameraZ2()); gl.glUniformMatrix4fv(uniProjectionMatrix, 1, false, projectionMatrix.getMatrix(), 0); - for (int id = 0; id < textures.length; ++id) - { - Texture texture = textures[id]; - if (texture == null) - { - continue; - } - - textureProvider.load(id); // trips the texture load flag which lets textures animate - - textureOffsets[id * 2] = texture.getU(); - textureOffsets[id * 2 + 1] = texture.getV(); - } - // Bind uniforms gl.glUniformBlockBinding(glProgram, uniBlockMain, 0); gl.glUniform1i(uniTextures, 1); // texture sampler array is bound to texture1 - gl.glUniform2fv(uniTextureOffsets, textureOffsets.length, textureOffsets, 0); // We just allow the GL to do face culling. Note this requires the priority renderer // to have logic to disregard culled faces in the priority depth testing. @@ -1518,7 +1512,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks @Override public void animate(Texture texture, int diff) { - textureManager.animate(texture, diff); + // texture animation happens on gpu } @Subscribe diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java index 351eba2823..00f984f778 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java @@ -35,9 +35,6 @@ import net.runelite.api.TextureProvider; @Slf4j class TextureManager { - private static final float PERC_64 = 1f / 64f; - private static final float PERC_128 = 1f / 128f; - private static final int TEXTURE_SIZE = 128; int initTextureArray(TextureProvider textureProvider, GL4 gl) @@ -207,64 +204,40 @@ class TextureManager return pixels; } - /** - * Animate the given texture - * - * @param texture - * @param diff Number of elapsed client ticks since last animation - */ - void animate(Texture texture, int diff) + float[] computeTextureAnimations(TextureProvider textureProvider) { - final int[] pixels = texture.getPixels(); - if (pixels == null) + Texture[] textures = textureProvider.getTextures(); + float[] anims = new float[TEXTURE_SIZE * 2]; + int idx = 0; + for (Texture texture : textures) { - return; + if (texture != null) + { + float u = 0f, v = 0f; + switch (texture.getAnimationDirection()) + { + case 1: + v = -1f; + break; + case 3: + v = 1f; + break; + case 2: + u = -1f; + break; + case 4: + u = 1f; + break; + } + + int speed = texture.getAnimationSpeed(); + u *= speed; + v *= speed; + + anims[idx++] = u; + anims[idx++] = v; + } } - - final int animationSpeed = texture.getAnimationSpeed(); - final float uvdiff = pixels.length == 4096 ? PERC_64 : PERC_128; - - float u = texture.getU(); - float v = texture.getV(); - - int offset = animationSpeed * diff; - float d = (float) offset * uvdiff; - - switch (texture.getAnimationDirection()) - { - case 1: - v -= d; - if (v < 0f) - { - v += 1f; - } - break; - case 3: - v += d; - if (v > 1f) - { - v -= 1f; - } - break; - case 2: - u -= d; - if (u < 0f) - { - u += 1f; - } - break; - case 4: - u += d; - if (u > 1f) - { - u -= 1f; - } - break; - default: - return; - } - - texture.setU(u); - texture.setV(v); + return anims; } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl index d028c4ce40..8c4b53dec1 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/frag.glsl @@ -25,7 +25,6 @@ #version 330 uniform sampler2DArray textures; -uniform vec2 textureOffsets[128]; uniform float brightness; uniform float smoothBanding; uniform vec4 fogColor; @@ -49,9 +48,7 @@ void main() { if (textureId > 0) { int textureIdx = textureId - 1; - vec2 animatedUv = fUv + textureOffsets[textureIdx]; - - vec4 textureColor = texture(textures, vec3(animatedUv, float(textureIdx))); + vec4 textureColor = texture(textures, vec3(fUv, float(textureIdx))); vec4 textureColorBrightness = pow(textureColor, vec4(brightness, brightness, brightness, 1.0f)); // textured triangles hsl is a 7 bit lightness 2-126 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl index 0b161a1149..6e19adfdb9 100644 --- a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/vert.glsl @@ -27,6 +27,10 @@ #define TILE_SIZE 128 +// smallest unit of the texture which can be moved per tick. textures are all +// 128x128px - so this is equivalent to +1px +#define TEXTURE_ANIM_UNIT (1.0f / 128.0f) + #define FOG_SCENE_EDGE_MIN TILE_SIZE #define FOG_SCENE_EDGE_MAX (103 * TILE_SIZE) #define FOG_CORNER_ROUNDING 1.5 @@ -52,6 +56,8 @@ uniform int useFog; uniform int fogDepth; uniform int drawDistance; uniform mat4 projectionMatrix; +uniform vec2 textureAnimations[128]; +uniform int tick; out vec4 Color; noperspective centroid out float fHsl; @@ -77,8 +83,17 @@ void main() gl_Position = projectionMatrix * vec4(vertex, 1.f); Color = vec4(rgb, 1.f - a); fHsl = float(hsl); - textureId = int(uv.x); - fUv = uv.yz; + + int textureIdx = int(uv.x); // the texture id + 1 + vec2 textureUv = uv.yz; + + vec2 textureAnim = vec2(0); + if (textureIdx > 0) { + textureAnim = textureAnimations[textureIdx - 1]; + } + + textureId = textureIdx; + fUv = textureUv + tick * textureAnim * TEXTURE_ANIM_UNIT; int fogWest = max(FOG_SCENE_EDGE_MIN, cameraX - drawDistance); int fogEast = min(FOG_SCENE_EDGE_MAX, cameraX + drawDistance - TILE_SIZE); From 5006e959b024c0fa564ebff0d8923c83a01cda82 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 2 Mar 2022 14:20:03 -0500 Subject: [PATCH 12/32] gpu: fix anim array indexes with sparse texture array Texture 54 doesn't exist and was causing everything after it to have its index off by 1 --- .../client/plugins/gpu/TextureManager.java | 54 ++++++++++--------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java index 00f984f778..994fc64d26 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/TextureManager.java @@ -208,35 +208,37 @@ class TextureManager { Texture[] textures = textureProvider.getTextures(); float[] anims = new float[TEXTURE_SIZE * 2]; - int idx = 0; - for (Texture texture : textures) + for (int i = 0; i < textures.length; ++i) { - if (texture != null) + Texture texture = textures[i]; + if (texture == null) { - float u = 0f, v = 0f; - switch (texture.getAnimationDirection()) - { - case 1: - v = -1f; - break; - case 3: - v = 1f; - break; - case 2: - u = -1f; - break; - case 4: - u = 1f; - break; - } - - int speed = texture.getAnimationSpeed(); - u *= speed; - v *= speed; - - anims[idx++] = u; - anims[idx++] = v; + continue; } + + float u = 0f, v = 0f; + switch (texture.getAnimationDirection()) + { + case 1: + v = -1f; + break; + case 3: + v = 1f; + break; + case 2: + u = -1f; + break; + case 4: + u = 1f; + break; + } + + int speed = texture.getAnimationSpeed(); + u *= speed; + v *= speed; + + anims[i * 2] = u; + anims[i * 2 + 1] = v; } return anims; } From 7864a10135dbdbaa6f87431186b46a4a13a8d4f5 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 5 Mar 2022 17:31:44 -0500 Subject: [PATCH 13/32] loottracker: split panel construction into methods This moves the overall and actions panel creation into separate methods --- .../plugins/loottracker/LootTrackerPanel.java | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java index 654d7db9b1..bde380c210 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java @@ -95,17 +95,18 @@ class LootTrackerPanel extends PluginPanel // When there is no loot, display this private final PluginErrorPanel errorPanel = new PluginErrorPanel(); + // When there is loot, display this. This contains the actions, overall, and log panel. + private final JPanel layoutPanel = new JPanel(); + // Handle loot boxes private final JPanel logsContainer = new JPanel(); // Handle overall session data - private final JPanel overallPanel = new JPanel(); private final JLabel overallKillsLabel = new JLabel(); private final JLabel overallGpLabel = new JLabel(); private final JLabel overallIcon = new JLabel(); // Details and navigation - private final JPanel actionsContainer = new JPanel(); private final JLabel detailsTitle = new JLabel(); private final JButton backBtn = new JButton(); private final JToggleButton viewHiddenBtn = new JToggleButton(); @@ -171,15 +172,36 @@ class LootTrackerPanel extends PluginPanel setLayout(new BorderLayout()); // Create layout panel for wrapping - final JPanel layoutPanel = new JPanel(); layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS)); + layoutPanel.setVisible(false); add(layoutPanel, BorderLayout.NORTH); + final JPanel actionsPanel = buildActionsPanel(); + final JPanel overallPanel = buildOverallPanel(); + + // Create loot boxes wrapper + logsContainer.setLayout(new BoxLayout(logsContainer, BoxLayout.Y_AXIS)); + layoutPanel.add(actionsPanel); + layoutPanel.add(overallPanel); + layoutPanel.add(logsContainer); + + // Add error pane + errorPanel.setContent("Loot tracker", "You have not received any loot yet."); + add(errorPanel); + } + + /** + * The actions panel includes the back/title label for the current view, + * as well as the view controls panel which includes hidden, single/grouped, and + * collapse buttons. + */ + private JPanel buildActionsPanel() + { + final JPanel actionsContainer = new JPanel(); actionsContainer.setLayout(new BorderLayout()); actionsContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); actionsContainer.setPreferredSize(new Dimension(0, 30)); actionsContainer.setBorder(new EmptyBorder(5, 5, 5, 10)); - actionsContainer.setVisible(false); final JPanel viewControls = new JPanel(new GridLayout(1, 3, 10, 0)); viewControls.setBackground(ColorScheme.DARKER_GRAY_COLOR); @@ -252,14 +274,19 @@ class LootTrackerPanel extends PluginPanel actionsContainer.add(viewControls, BorderLayout.EAST); actionsContainer.add(leftTitleContainer, BorderLayout.WEST); + return actionsContainer; + } + + private JPanel buildOverallPanel() + { // Create panel that will contain overall data + final JPanel overallPanel = new JPanel(); overallPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(5, 0, 0, 0, ColorScheme.DARK_GRAY_COLOR), BorderFactory.createEmptyBorder(8, 10, 8, 10) )); overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); overallPanel.setLayout(new BorderLayout()); - overallPanel.setVisible(false); // Add icon and contents final JPanel overallInfo = new JPanel(); @@ -310,15 +337,7 @@ class LootTrackerPanel extends PluginPanel popupMenu.add(reset); overallPanel.setComponentPopupMenu(popupMenu); - // Create loot boxes wrapper - logsContainer.setLayout(new BoxLayout(logsContainer, BoxLayout.Y_AXIS)); - layoutPanel.add(actionsContainer); - layoutPanel.add(overallPanel); - layoutPanel.add(logsContainer); - - // Add error pane - errorPanel.setContent("Loot tracker", "You have not received any loot yet."); - add(errorPanel); + return overallPanel; } void updateCollapseText() @@ -511,8 +530,7 @@ class LootTrackerPanel extends PluginPanel // Show main view remove(errorPanel); - actionsContainer.setVisible(true); - overallPanel.setVisible(true); + layoutPanel.setVisible(true); // Create box final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getType(), record.getSubTitle(), @@ -555,7 +573,7 @@ class LootTrackerPanel extends PluginPanel { final LootTrackerClient client = plugin.getLootTrackerClient(); final boolean syncLoot = client.getUuid() != null && config.syncPanel(); - final int result = JOptionPane.showOptionDialog(overallPanel, + final int result = JOptionPane.showOptionDialog(box, syncLoot ? SYNC_RESET_ALL_WARNING_TEXT : NO_SYNC_RESET_ALL_WARNING_TEXT, "Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[]{"Yes", "No"}, "No"); From 17f6890b693fbe130f4877bc906ac1a8022b9d28 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 5 Mar 2022 18:58:29 -0500 Subject: [PATCH 14/32] loottracker: replace getTotalPrice with stream --- .../plugins/loottracker/LootTrackerPlugin.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index b8c9f1a7b2..9c43c44545 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -1131,26 +1131,18 @@ public class LootTrackerPlugin extends Plugin return false; } - private long getTotalPrice(Collection items) - { - long totalPrice = 0; - - for (final ItemStack itemStack : items) - { - totalPrice += (long) itemManager.getItemPrice(itemStack.getId()) * itemStack.getQuantity(); - } - - return totalPrice; - } - private void lootReceivedChatMessage(final Collection items, final String name) { + long totalPrice = items.stream() + .mapToLong(is -> (long) itemManager.getItemPrice(is.getId()) * is.getQuantity()) + .sum(); + final String message = new ChatMessageBuilder() .append(ChatColorType.HIGHLIGHT) .append("You've killed ") .append(name) .append(" for ") - .append(QuantityFormatter.quantityToStackSize(getTotalPrice(items))) + .append(QuantityFormatter.quantityToStackSize(totalPrice)) .append(" loot.") .build(); From d842bacd2c71bc5f1ad24be06fc326d636f23a3d Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 5 Mar 2022 19:06:38 -0500 Subject: [PATCH 15/32] loottracker: hoist box emptyborder from loop --- .../runelite/client/plugins/loottracker/LootTrackerBox.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java index 16da3c9460..97dd4504df 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java @@ -316,6 +316,7 @@ class LootTrackerBox extends JPanel itemContainer.removeAll(); itemContainer.setLayout(new GridLayout(rowSize, ITEMS_PER_ROW, 1, 1)); + final EmptyBorder emptyBorder = new EmptyBorder(5, 5, 5, 5); for (int i = 0; i < rowSize * ITEMS_PER_ROW; i++) { final JPanel slotContainer = new JPanel(); @@ -350,7 +351,7 @@ class LootTrackerBox extends JPanel // Create popup menu final JPopupMenu popupMenu = new JPopupMenu(); - popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + popupMenu.setBorder(emptyBorder); slotContainer.setComponentPopupMenu(popupMenu); final JMenuItem toggle = new JMenuItem("Toggle item"); From f2b43743c3c33b39276acfc53bb6affc6ca033f2 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 5 Mar 2022 21:39:58 -0500 Subject: [PATCH 16/32] config manager: post RuneScapeProfileChanged when new profiles are created --- .../src/main/java/net/runelite/client/config/ConfigManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index fd6e492d39..add85d3d58 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -564,6 +564,8 @@ public class ConfigManager RuneScapeProfile prof = findRSProfile(getRSProfiles(), username, RuneScapeProfileType.getCurrent(client), displayName, true); rsProfileKey = prof.getKey(); this.rsProfileKey = rsProfileKey; + + eventBus.post(new RuneScapeProfileChanged()); } setConfiguration(groupName, rsProfileKey, key, value); } From 64abf450d9f72778b5ff1555f3ad04485964143c Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 6 Mar 2022 15:12:33 -0500 Subject: [PATCH 17/32] loot tracker: store loot in config Since loot is now aggregated, the data is little enough to store in config. This allows loot to persist between sessions even when not logged in. --- .../runelite/client/config/ConfigManager.java | 23 +- .../plugins/loottracker/ConfigLoot.java | 73 ++++ .../plugins/loottracker/LootTrackerBox.java | 1 + .../loottracker/LootTrackerConfig.java | 26 +- .../plugins/loottracker/LootTrackerPanel.java | 92 +++- .../loottracker/LootTrackerPlugin.java | 405 +++++++++++++++--- .../plugins/loottracker/import_icon.png | Bin 0 -> 420 bytes .../loottracker/LootTrackerPluginTest.java | 5 + 8 files changed, 527 insertions(+), 98 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/loottracker/ConfigLoot.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/import_icon.png diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index add85d3d58..70278e10c5 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -58,6 +58,7 @@ import java.time.Duration; import java.time.Instant; import java.util.Arrays; import java.util.Base64; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -410,7 +411,27 @@ public class ConfigManager public List getConfigurationKeys(String prefix) { - return properties.keySet().stream().filter(v -> ((String) v).startsWith(prefix)).map(String.class::cast).collect(Collectors.toList()); + return properties.keySet().stream() + .map(String.class::cast) + .filter(k -> k.startsWith(prefix)) + .collect(Collectors.toList()); + } + + public List getRSProfileConfigurationKeys(String group, String profile, String keyPrefix) + { + if (profile == null) + { + return Collections.emptyList(); + } + + assert profile.startsWith(RSPROFILE_GROUP); + + String prefix = group + "." + profile + "." + keyPrefix; + return properties.keySet().stream() + .map(String.class::cast) + .filter(k -> k.startsWith(prefix)) + .map(k -> splitKey(k)[KEY_SPLITTER_KEY]) + .collect(Collectors.toList()); } public static String getWholeKey(String groupName, String profile, String key) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/ConfigLoot.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/ConfigLoot.java new file mode 100644 index 0000000000..0a26254926 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/ConfigLoot.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.loottracker; + +import java.time.Instant; +import java.util.Arrays; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import net.runelite.http.api.loottracker.LootRecordType; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(of = {"type", "name"}) +class ConfigLoot +{ + LootRecordType type; + String name; + int kills; + Instant first = Instant.now(); + Instant last; + int[] drops; + + ConfigLoot(LootRecordType type, String name) + { + this.type = type; + this.name = name; + this.drops = new int[0]; + } + + void add(int id, int qty) + { + for (int i = 0; i < drops.length; i += 2) + { + if (drops[i] == id) + { + drops[i + 1] += qty; + return; + } + } + + drops = Arrays.copyOf(drops, drops.length + 2); + drops[drops.length - 2] = id; + drops[drops.length - 1] = qty; + } + + int numDrops() + { + return drops.length / 2; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java index 97dd4504df..36d784c182 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java @@ -72,6 +72,7 @@ class LootTrackerBox extends JPanel private final ItemManager itemManager; @Getter(AccessLevel.PACKAGE) private final String id; + @Getter(AccessLevel.PACKAGE) private final LootRecordType lootRecordType; private final LootTrackerPriceType priceType; private final boolean showPriceType; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java index 983cfb45a9..6e1b9f27ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerConfig.java @@ -30,9 +30,11 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; -@ConfigGroup("loottracker") +@ConfigGroup(LootTrackerConfig.GROUP) public interface LootTrackerConfig extends Config { + String GROUP = "loottracker"; + @ConfigSection( name = "Ignored Entries", description = "The Ignore items and Ignore groups options", @@ -79,28 +81,6 @@ public interface LootTrackerConfig extends Config return false; } - @ConfigItem( - keyName = "saveLoot", - name = "Submit loot tracker data", - description = "Submit loot tracker data" - ) - default boolean saveLoot() - { - return true; - } - - @ConfigItem( - keyName = "syncPanel", - name = "Synchronize panel contents", - description = "Synchronize your local loot tracker with your server data (requires being signed in).
" + - " This means the panel is filled with portions of your remote data on startup
" + - " and deleting data in the panel also deletes it on the server." - ) - default boolean syncPanel() - { - return true; - } - @ConfigItem( keyName = "ignoredEvents", name = "Ignored Loot Sources", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java index bde380c210..11dfb21abf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java @@ -52,6 +52,7 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButton; import javax.swing.JToggleButton; +import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicToggleButtonUI; @@ -84,29 +85,31 @@ class LootTrackerPanel extends PluginPanel private static final ImageIcon INVISIBLE_ICON_HOVER; private static final ImageIcon COLLAPSE_ICON; private static final ImageIcon EXPAND_ICON; + private static final ImageIcon IMPORT_ICON; private static final String HTML_LABEL_TEMPLATE = "%s%s"; - private static final String SYNC_RESET_ALL_WARNING_TEXT = - "This will permanently delete the current loot from both the client and the RuneLite website."; - private static final String NO_SYNC_RESET_ALL_WARNING_TEXT = - "This will permanently delete the current loot from the client."; + private static final String RESET_ALL_WARNING_TEXT = + "This will permanently delete all loot."; + private static final String RESET_CURRENT_WARNING_TEXT = + "This will permanently delete \"%s\" loot."; + private static final String RESET_ONE_WARNING_TEXT = + "This will delete one kill."; // When there is no loot, display this private final PluginErrorPanel errorPanel = new PluginErrorPanel(); - // When there is loot, display this. This contains the actions, overall, and log panel. - private final JPanel layoutPanel = new JPanel(); - // Handle loot boxes private final JPanel logsContainer = new JPanel(); // Handle overall session data + private final JPanel overallPanel; private final JLabel overallKillsLabel = new JLabel(); private final JLabel overallGpLabel = new JLabel(); private final JLabel overallIcon = new JLabel(); // Details and navigation + private final JPanel actionsPanel; private final JLabel detailsTitle = new JLabel(); private final JButton backBtn = new JButton(); private final JToggleButton viewHiddenBtn = new JToggleButton(); @@ -114,6 +117,8 @@ class LootTrackerPanel extends PluginPanel private final JRadioButton groupedLootBtn = new JRadioButton(); private final JButton collapseBtn = new JButton(); + private final JPanel importNoticePanel; + // Aggregate of all kills private final List aggregateRecords = new ArrayList<>(); // Individual records for the individual kills this session @@ -158,6 +163,8 @@ class LootTrackerPanel extends PluginPanel COLLAPSE_ICON = new ImageIcon(collapseImg); EXPAND_ICON = new ImageIcon(expandedImg); + + IMPORT_ICON = new ImageIcon(ImageUtil.loadImageResource(LootTrackerPlugin.class, "import_icon.png")); } LootTrackerPanel(final LootTrackerPlugin plugin, final ItemManager itemManager, final LootTrackerConfig config) @@ -172,16 +179,18 @@ class LootTrackerPanel extends PluginPanel setLayout(new BorderLayout()); // Create layout panel for wrapping + final JPanel layoutPanel = new JPanel(); layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS)); - layoutPanel.setVisible(false); add(layoutPanel, BorderLayout.NORTH); - final JPanel actionsPanel = buildActionsPanel(); - final JPanel overallPanel = buildOverallPanel(); + actionsPanel = buildActionsPanel(); + overallPanel = buildOverallPanel(); + importNoticePanel = createImportNoticePanel(); // Create loot boxes wrapper logsContainer.setLayout(new BoxLayout(logsContainer, BoxLayout.Y_AXIS)); layoutPanel.add(actionsPanel); + layoutPanel.add(importNoticePanel); layoutPanel.add(overallPanel); layoutPanel.add(logsContainer); @@ -202,6 +211,7 @@ class LootTrackerPanel extends PluginPanel actionsContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); actionsContainer.setPreferredSize(new Dimension(0, 30)); actionsContainer.setBorder(new EmptyBorder(5, 5, 5, 10)); + actionsContainer.setVisible(false); final JPanel viewControls = new JPanel(new GridLayout(1, 3, 10, 0)); viewControls.setBackground(ColorScheme.DARKER_GRAY_COLOR); @@ -287,6 +297,7 @@ class LootTrackerPanel extends PluginPanel )); overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); overallPanel.setLayout(new BorderLayout()); + overallPanel.setVisible(false); // Add icon and contents final JPanel overallInfo = new JPanel(); @@ -304,10 +315,8 @@ class LootTrackerPanel extends PluginPanel final JMenuItem reset = new JMenuItem("Reset All"); reset.addActionListener(e -> { - final LootTrackerClient client = plugin.getLootTrackerClient(); - final boolean syncLoot = client.getUuid() != null && config.syncPanel(); final int result = JOptionPane.showOptionDialog(overallPanel, - syncLoot ? SYNC_RESET_ALL_WARNING_TEXT : NO_SYNC_RESET_ALL_WARNING_TEXT, + currentView == null ? RESET_ALL_WARNING_TEXT : String.format(RESET_CURRENT_WARNING_TEXT, currentView), "Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[]{"Yes", "No"}, "No"); @@ -325,9 +334,14 @@ class LootTrackerPanel extends PluginPanel logsContainer.repaint(); // Delete all loot, or loot matching the current view - if (syncLoot) + if (currentView != null) { - client.delete(currentView); + assert currentType != null; + plugin.removeLootConfig(currentType, currentView); + } + else + { + plugin.removeAllLoot(); } }); @@ -340,6 +354,30 @@ class LootTrackerPanel extends PluginPanel return overallPanel; } + private JPanel createImportNoticePanel() + { + JPanel panel = new JPanel(); + panel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + panel.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createMatteBorder(5, 0, 0, 0, ColorScheme.DARK_GRAY_COLOR), + BorderFactory.createEmptyBorder(8, 10, 8, 10) + )); + panel.setLayout(new BorderLayout()); + + final JLabel importLabel = new JLabel("Missing saved loot? Click the
import button to import it."); + importLabel.setForeground(Color.YELLOW); + panel.add(importLabel, BorderLayout.WEST); + + JButton importButton = new JButton(); + SwingUtil.removeButtonDecorations(importButton); + importButton.setIcon(IMPORT_ICON); + importButton.setToolTipText("Import old loot tracker data to current profile"); + importButton.addActionListener(l -> plugin.importLoot()); + panel.add(importButton, BorderLayout.EAST); + + return panel; + } + void updateCollapseText() { collapseBtn.setSelected(isAllCollapsed()); @@ -389,6 +427,14 @@ class LootTrackerPanel extends PluginPanel } } + /** + * Clear all records in the panel + */ + void clearRecords() + { + aggregateRecords.clear(); + } + /** * Adds a Collection of records to the panel */ @@ -530,7 +576,8 @@ class LootTrackerPanel extends PluginPanel // Show main view remove(errorPanel); - layoutPanel.setVisible(true); + actionsPanel.setVisible(true); + overallPanel.setVisible(true); // Create box final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getType(), record.getSubTitle(), @@ -571,10 +618,8 @@ class LootTrackerPanel extends PluginPanel final JMenuItem reset = new JMenuItem("Reset"); reset.addActionListener(e -> { - final LootTrackerClient client = plugin.getLootTrackerClient(); - final boolean syncLoot = client.getUuid() != null && config.syncPanel(); final int result = JOptionPane.showOptionDialog(box, - syncLoot ? SYNC_RESET_ALL_WARNING_TEXT : NO_SYNC_RESET_ALL_WARNING_TEXT, + groupLoot ? String.format(RESET_CURRENT_WARNING_TEXT, box.getId()) : RESET_ONE_WARNING_TEXT, "Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, new String[]{"Yes", "No"}, "No"); @@ -596,9 +641,9 @@ class LootTrackerPanel extends PluginPanel logsContainer.repaint(); // Without loot being grouped we have no way to identify single kills to be deleted - if (client.getUuid() != null && groupLoot && config.syncPanel()) + if (groupLoot) { - client.delete(box.getId()); + plugin.removeLootConfig(box.getLootRecordType(), box.getId()); } }); @@ -691,4 +736,9 @@ class LootTrackerPanel extends PluginPanel final String valueStr = QuantityFormatter.quantityToStackSize(value); return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); } + + void toggleImportNotice(boolean on) + { + SwingUtilities.invokeLater(() -> importNoticePanel.setVisible(on)); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 9c43c44545..39d2b6bf81 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -26,6 +26,7 @@ package net.runelite.client.plugins.loottracker; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Strings; import com.google.common.collect.HashMultiset; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMultimap; @@ -33,9 +34,12 @@ import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.Multiset; import com.google.common.collect.Multisets; +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; import com.google.inject.Provides; import java.awt.image.BufferedImage; import java.io.IOException; +import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; @@ -43,6 +47,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -55,6 +60,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.inject.Inject; +import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import lombok.AccessLevel; import lombok.Getter; @@ -96,6 +102,7 @@ import net.runelite.client.events.ClientShutdown; import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.NpcLootReceived; import net.runelite.client.events.PlayerLootReceived; +import net.runelite.client.events.RuneScapeProfileChanged; import net.runelite.client.events.SessionClose; import net.runelite.client.events.SessionOpen; import net.runelite.client.game.ItemManager; @@ -119,12 +126,14 @@ import org.apache.commons.text.WordUtils; @PluginDescriptor( name = "Loot Tracker", description = "Tracks loot from monsters and minigames", - tags = {"drops"}, - enabledByDefault = false + tags = {"drops"} ) @Slf4j public class LootTrackerPlugin extends Plugin { + private static final int MAX_DROPS = 1024; + private static final Duration MAX_AGE = Duration.ofDays(365L); + // Activity/Event loot handling private static final Pattern CLUE_SCROLL_PATTERN = Pattern.compile("You have completed [0-9]+ ([a-z]+) Treasure Trails?\\."); private static final int THEATRE_OF_BLOOD_REGION = 12867; @@ -291,6 +300,12 @@ public class LootTrackerPlugin extends Plugin @Inject private LootManager lootManager; + @Inject + private ConfigManager configManager; + + @Inject + private Gson gson; + private LootTrackerPanel panel; private NavigationButton navButton; @VisibleForTesting @@ -310,6 +325,8 @@ public class LootTrackerPlugin extends Plugin @Inject private LootTrackerClient lootTrackerClient; private final List queuedLoots = new ArrayList<>(); + private String profileKey; + private Instant lastLootImport = Instant.now().minus(1, ChronoUnit.MINUTES); private static Collection stack(Collection items) { @@ -367,20 +384,120 @@ public class LootTrackerPlugin extends Plugin lootTrackerClient.setUuid(null); } + @Subscribe + public void onRuneScapeProfileChanged(RuneScapeProfileChanged e) + { + final String profileKey = configManager.getRSProfileKey(); + if (profileKey == null) + { + return; + } + + if (profileKey.equals(this.profileKey)) + { + return; + } + + log.debug("Profile changed to {}", profileKey); + switchProfile(profileKey); + } + + private void switchProfile(String profileKey) + { + executor.execute(() -> + { + // Current queued loot is for the previous profile, so save it first with the current profile key + submitLoot(); + + this.profileKey = profileKey; + + log.debug("Switched to profile {}", profileKey); + + int drops = 0; + List loots = new ArrayList<>(); + Instant old = Instant.now().minus(MAX_AGE); + for (String key : configManager.getRSProfileConfigurationKeys(LootTrackerConfig.GROUP, profileKey, "drops_")) + { + String json = configManager.getConfiguration(LootTrackerConfig.GROUP, profileKey, key); + ConfigLoot configLoot; + + try + { + configLoot = gson.fromJson(json, ConfigLoot.class); + } + catch (JsonSyntaxException ex) + { + log.warn("Removing loot with malformed json: {}", json, ex); + configManager.unsetConfiguration(LootTrackerConfig.GROUP, profileKey, key); + continue; + } + + if (configLoot.last.isBefore(old)) + { + log.debug("Removing old loot for {} {}", configLoot.type, configLoot.name); + configManager.unsetConfiguration(LootTrackerConfig.GROUP, profileKey, key); + continue; + } + + if (drops >= MAX_DROPS && !loots.isEmpty() && loots.get(0).last.isAfter(configLoot.last)) + { + // fast drop + continue; + } + + sortedInsert(loots, configLoot, Comparator.comparing(ConfigLoot::getLast)); + drops += configLoot.numDrops(); + + if (drops >= MAX_DROPS) + { + ConfigLoot top = loots.remove(0); + drops -= top.numDrops(); + } + } + + log.debug("Loaded {} records", loots.size()); + + clientThread.invokeLater(() -> + { + // convertToLootTrackerRecord must be called on client thread + List records = loots.stream() + .map(this::convertToLootTrackerRecord) + .collect(Collectors.toList()); + SwingUtilities.invokeLater(() -> + { + panel.clearRecords(); + panel.addRecords(records); + }); + }); + + panel.toggleImportNotice(!hasImported()); + }); + } + + private static void sortedInsert(List list, T value, Comparator c) + { + int idx = Collections.binarySearch(list, value, c); + list.add(idx < 0 ? -idx - 1 : idx, value); + } + @Subscribe public void onConfigChanged(ConfigChanged event) { - if (event.getGroup().equals("loottracker")) + if (event.getGroup().equals(LootTrackerConfig.GROUP)) { - ignoredItems = Text.fromCSV(config.getIgnoredItems()); - ignoredEvents = Text.fromCSV(config.getIgnoredEvents()); - SwingUtilities.invokeLater(panel::updateIgnoredRecords); + if ("ignoredItems".equals(event.getKey()) || "ignoredEvents".equals(event.getKey())) + { + ignoredItems = Text.fromCSV(config.getIgnoredItems()); + ignoredEvents = Text.fromCSV(config.getIgnoredEvents()); + SwingUtilities.invokeLater(panel::updateIgnoredRecords); + } } } @Override protected void startUp() throws Exception { + profileKey = null; ignoredItems = Text.fromCSV(config.getIgnoredItems()); ignoredEvents = Text.fromCSV(config.getIgnoredEvents()); panel = new LootTrackerPanel(this, itemManager, config); @@ -401,44 +518,12 @@ public class LootTrackerPlugin extends Plugin if (accountSession != null) { lootTrackerClient.setUuid(accountSession.getUuid()); + } - clientThread.invokeLater(() -> - { - switch (client.getGameState()) - { - case STARTING: - case UNKNOWN: - return false; - } - - executor.submit(() -> - { - if (!config.syncPanel()) - { - return; - } - - Collection lootRecords; - try - { - lootRecords = lootTrackerClient.get(); - } - catch (IOException e) - { - log.debug("Unable to look up loot", e); - return; - } - - log.debug("Loaded {} data entries", lootRecords.size()); - - clientThread.invokeLater(() -> - { - Collection records = convertToLootTrackerRecord(lootRecords); - SwingUtilities.invokeLater(() -> panel.addRecords(records)); - }); - }); - return true; - }); + String profileKey = configManager.getRSProfileKey(); + if (profileKey != null) + { + switchProfile(profileKey); } } @@ -475,13 +560,10 @@ public class LootTrackerPlugin extends Plugin final LootTrackerItem[] entries = buildEntries(stack(items)); SwingUtilities.invokeLater(() -> panel.add(name, type, combatLevel, entries)); - if (config.saveLoot()) + LootRecord lootRecord = new LootRecord(name, type, metadata, toGameItems(items), Instant.now(), getLootWorldId()); + synchronized (queuedLoots) { - LootRecord lootRecord = new LootRecord(name, type, metadata, toGameItems(items), Instant.now(), getLootWorldId()); - synchronized (queuedLoots) - { - queuedLoots.add(lootRecord); - } + queuedLoots.add(lootRecord); } eventBus.post(new LootReceived(name, combatLevel, type, items)); @@ -932,16 +1014,53 @@ public class LootTrackerPlugin extends Plugin queuedLoots.clear(); } - if (!config.saveLoot()) - { - return null; - } + saveLoot(copy); log.debug("Submitting {} loot records", copy.size()); return lootTrackerClient.submit(copy); } + private Collection combine(List records) + { + Map map = new HashMap<>(); + for (LootRecord record : records) + { + ConfigLoot key = new ConfigLoot(record.getType(), record.getEventId()); + ConfigLoot loot = map.computeIfAbsent(key, k -> key); + loot.kills++; + for (GameItem item : record.getDrops()) + { + loot.add(item.getId(), item.getQty()); + } + } + return map.values(); + } + + private void saveLoot(List records) + { + Instant now = Instant.now(); + Collection combinedRecords = combine(records); + for (ConfigLoot record : combinedRecords) + { + ConfigLoot lootConfig = getLootConfig(record.type, record.name); + if (lootConfig == null) + { + lootConfig = record; + } + else + { + lootConfig.kills += record.kills; + for (int i = 0; i < record.drops.length; i += 2) + { + lootConfig.add(record.drops[i], record.drops[i + 1]); + } + } + lootConfig.last = now; + setLootConfig(lootConfig.type, lootConfig.name, lootConfig); + } + } + private void setEvent(LootRecordType lootRecordType, String eventType, Object metadata) { this.lootRecordType = lootRecordType; @@ -1113,6 +1232,18 @@ public class LootTrackerPlugin extends Plugin .collect(Collectors.toCollection(ArrayList::new)); } + private LootTrackerRecord convertToLootTrackerRecord(final ConfigLoot configLoot) + { + LootTrackerItem[] items = new LootTrackerItem[configLoot.drops.length / 2]; + for (int i = 0; i < configLoot.drops.length; i += 2) + { + int id = configLoot.drops[i]; + int qty = configLoot.drops[i + 1]; + items[i >> 1] = buildLootTrackerItem(id, qty); + } + return new LootTrackerRecord(configLoot.name, "", configLoot.type, items, configLoot.kills); + } + /** * Is player currently within the provided map regions */ @@ -1152,4 +1283,172 @@ public class LootTrackerPlugin extends Plugin .runeLiteFormattedMessage(message) .build()); } + + ConfigLoot getLootConfig(LootRecordType type, String name) + { + String profile = profileKey; + if (Strings.isNullOrEmpty(profile)) + { + log.debug("Trying to get loot with no profile!"); + return null; + } + + String json = configManager.getConfiguration(LootTrackerConfig.GROUP, profile, "drops_" + type + "_" + name); + if (json == null) + { + return null; + } + + return gson.fromJson(json, ConfigLoot.class); + } + + void setLootConfig(LootRecordType type, String name, ConfigLoot loot) + { + String profile = profileKey; + if (Strings.isNullOrEmpty(profile)) + { + log.debug("Trying to set loot with no profile!"); + return; + } + + String json = gson.toJson(loot); + configManager.setConfiguration(LootTrackerConfig.GROUP, profile, "drops_" + type + "_" + name, json); + } + + void removeLootConfig(LootRecordType type, String name) + { + String profile = profileKey; + if (Strings.isNullOrEmpty(profile)) + { + log.debug("Trying to remove loot with no profile!"); + return; + } + + configManager.unsetConfiguration(LootTrackerConfig.GROUP, profile, "drops_" + type + "_" + name); + } + + void removeAllLoot() + { + String profile = profileKey; + if (Strings.isNullOrEmpty(profile)) + { + log.debug("Trying to clear loot with no profile!"); + return; + } + + for (String key : configManager.getRSProfileConfigurationKeys(LootTrackerConfig.GROUP, profile, "drops_")) + { + configManager.unsetConfiguration(LootTrackerConfig.GROUP, profile, key); + } + + clearImported(); + panel.toggleImportNotice(true); + } + + void importLoot() + { + if (configManager.getRSProfileKey() == null) + { + JOptionPane.showMessageDialog(panel, "You do not have an active profile to import loot into; log in to the game first."); + return; + } + + if (lootTrackerClient.getUuid() == null) + { + JOptionPane.showMessageDialog(panel, "You are not logged into RuneLite, so loot can not be imported from your account. Log in first."); + return; + } + + if (lastLootImport.isAfter(Instant.now().minus(1, ChronoUnit.MINUTES))) + { + JOptionPane.showMessageDialog(panel, "You imported too recently. Wait a minute and try again."); + return; + } + + lastLootImport = Instant.now(); + + executor.execute(() -> + { + if (hasImported()) + { + SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(panel, "You already have imported loot.")); + return; + } + + Collection lootRecords; + try + { + lootRecords = lootTrackerClient.get(); + } + catch (IOException e) + { + log.debug("Unable to look up loot", e); + return; + } + + log.debug("Loaded {} data entries", lootRecords.size()); + + for (LootAggregate record : lootRecords) + { + ConfigLoot lootConfig = getLootConfig(record.getType(), record.getEventId()); + if (lootConfig == null) + { + lootConfig = new ConfigLoot(record.getType(), record.getEventId()); + } + lootConfig.first = record.getFirst_time(); + lootConfig.last = record.getLast_time(); + lootConfig.kills += record.getAmount(); + for (GameItem gameItem : record.getDrops()) + { + lootConfig.add(gameItem.getId(), gameItem.getQty()); + } + setLootConfig(record.getType(), record.getEventId(), lootConfig); + } + + clientThread.invokeLater(() -> + { + Collection records = convertToLootTrackerRecord(lootRecords); + SwingUtilities.invokeLater(() -> panel.addRecords(records)); + }); + + SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(panel, "Imported " + lootRecords.size() + " loot entries.")); + + setHasImported(); + panel.toggleImportNotice(false); + }); + } + + void setHasImported() + { + String profile = profileKey; + if (Strings.isNullOrEmpty(profile)) + { + return; + } + + configManager.setConfiguration(LootTrackerConfig.GROUP, profile, "imported", 1); + } + + boolean hasImported() + { + String profile = profileKey; + if (Strings.isNullOrEmpty(profile)) + { + return false; + } + + Integer i = configManager.getConfiguration(LootTrackerConfig.GROUP, profile, "imported", Integer.class); + return i != null && i == 1; + } + + void clearImported() + { + String profile = profileKey; + if (Strings.isNullOrEmpty(profile)) + { + return; + } + + configManager.unsetConfiguration(LootTrackerConfig.GROUP, profile, "imported"); + } } diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/import_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/loottracker/import_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5402cd593e9b7c02a90b5551d91bb9c19f3428a5 GIT binary patch literal 420 zcmV;V0bBlwP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGf6951U69E94oEQKA0Xa!TK~y+TwbQ#w zLqQP6@!gn!pxDI9Mr>_tZF~wveFDMOMlA#p1hGh?l~~$oZ6gvdq!6@HlS1$b6l}Ec zcKm;v899*1vCt2GFgv?v&V?lWd$KHZ70lrn4|u{A<`I$36~+z@;2g>=cCn478l(>0 zuz08)+QS Date: Sun, 6 Mar 2022 19:39:46 -0500 Subject: [PATCH 18/32] runelite: disable option parser abbreviations --- runelite-client/src/main/java/net/runelite/client/RuneLite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 96acb797ef..5c3cd50611 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -149,7 +149,7 @@ public class RuneLite { Locale.setDefault(Locale.ENGLISH); - final OptionParser parser = new OptionParser(); + final OptionParser parser = new OptionParser(false); parser.accepts("developer-mode", "Enable developer tools"); parser.accepts("debug", "Show extra debugging output"); parser.accepts("safe-mode", "Disables external plugins and the GPU plugin"); From 473a3042fbdd891a42a00082ccf58ee1bf8af57b Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 6 Mar 2022 22:09:56 -0500 Subject: [PATCH 19/32] api: add methods to get selected item --- .../src/main/java/net/runelite/api/Client.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index 928ee839e4..2a47aaeee3 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1883,6 +1883,18 @@ public interface Client extends GameEngine */ void setSpellSelected(boolean selected); + /** + * Get if an item is selected with "Use" + * @return 1 if selected, else 0 + */ + int getSelectedItem(); + + /** + * If an item is selected, this is the item index in the inventory. + * @return + */ + int getSelectedItemIndex(); + /** * Returns client item composition cache */ From d2fd37e543de9fe36922451dfd81d6cb357e602e Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 7 Mar 2022 22:48:29 -0500 Subject: [PATCH 20/32] config manager: run shutdown hook after plugins --- .../main/java/net/runelite/client/config/ConfigManager.java | 5 ++++- .../main/java/net/runelite/client/eventbus/Subscribe.java | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index 70278e10c5..8f349d6308 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -910,7 +910,10 @@ public class ConfigManager return object == null ? null : object.toString(); } - @Subscribe(priority = 100) + @Subscribe( + // run after plugins, in the event they save config on shutdown + priority = -100 + ) private void onClientShutdown(ClientShutdown e) { Future f = sendConfig(); diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/Subscribe.java b/runelite-client/src/main/java/net/runelite/client/eventbus/Subscribe.java index d3a7c0514b..dae4eb4597 100644 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/Subscribe.java +++ b/runelite-client/src/main/java/net/runelite/client/eventbus/Subscribe.java @@ -38,5 +38,9 @@ import java.lang.annotation.Target; @Documented public @interface Subscribe { + /** + * Priority relative to other event subscribers. Higher priorities run first. + * @return + */ float priority() default 0; } From 93d483db7b4968d04ff43989be8c05acfc378dbe Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 8 Mar 2022 16:11:23 -0500 Subject: [PATCH 21/32] gpu: don't animate textures when loading --- .../java/net/runelite/client/plugins/gpu/GpuPlugin.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 23e2f4fe46..fdb07d1e6e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -1293,7 +1293,11 @@ public class GpuPlugin extends Plugin implements DrawCallbacks gl.glUniform1f(uniSmoothBanding, config.smoothBanding() ? 0f : 1f); gl.glUniform1i(uniColorBlindMode, config.colorBlindMode().ordinal()); gl.glUniform1f(uniTextureLightMode, config.brightTextures() ? 1f : 0f); - gl.glUniform1i(uniTick, client.getGameCycle()); + if (gameState == GameState.LOGGED_IN) + { + // avoid textures animating during loading + gl.glUniform1i(uniTick, client.getGameCycle()); + } // Calculate projection matrix Matrix4 projectionMatrix = new Matrix4(); From 6aba1e3894f4e037a4834123db81cf31a4d25c5f Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 8 Mar 2022 19:13:18 -0500 Subject: [PATCH 22/32] api: add isFollower to NPCComposition --- .../src/main/java/net/runelite/api/NPCComposition.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/NPCComposition.java b/runelite-api/src/main/java/net/runelite/api/NPCComposition.java index faff1da7db..4e4dff2c71 100644 --- a/runelite-api/src/main/java/net/runelite/api/NPCComposition.java +++ b/runelite-api/src/main/java/net/runelite/api/NPCComposition.java @@ -96,4 +96,11 @@ public interface NPCComposition extends ParamHolder * Gets the displayed overhead icon of the NPC. */ HeadIcon getOverheadIcon(); + + /** + * If the npc is a follower, such as a pet. Is affected by the + * "Move follower options lower down" setting. + * @return + */ + boolean isFollower(); } From eabe39df6ef0c6351f9b7d3a7bd858dc4c2f6546 Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 9 Mar 2022 02:34:15 +0100 Subject: [PATCH 23/32] configmanager: Whoops forgot to add this --- .../runelite/client/config/ConfigManager.java | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index 8459e7bcd5..77bacede3f 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -374,7 +374,27 @@ public class ConfigManager public List getConfigurationKeys(String prefix) { - return properties.keySet().stream().filter(v -> ((String) v).startsWith(prefix)).map(String.class::cast).collect(Collectors.toList()); + return properties.keySet().stream() + .map(String.class::cast) + .filter(k -> k.startsWith(prefix)) + .collect(Collectors.toList()); + } + + public List getRSProfileConfigurationKeys(String group, String profile, String keyPrefix) + { + if (profile == null) + { + return Collections.emptyList(); + } + + assert profile.startsWith(RSPROFILE_GROUP); + + String prefix = group + "." + profile + "." + keyPrefix; + return properties.keySet().stream() + .map(String.class::cast) + .filter(k -> k.startsWith(prefix)) + .map(k -> splitKey(k)[KEY_SPLITTER_KEY]) + .collect(Collectors.toList()); } public static String getWholeKey(String groupName, String profile, String key) From 7ebe91e176e6510baf3671309c2d502a32d2c947 Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 9 Mar 2022 02:34:35 +0100 Subject: [PATCH 24/32] mixins: Add mixins for selected items --- .../src/main/java/net/runelite/rs/api/RSClient.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java index 1b408e036a..b4eac54666 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSClient.java @@ -1160,6 +1160,10 @@ public interface RSClient extends RSGameEngine, Client @Override void setSelectedItemSlot(int index); + @Import("selectedItemSlot") + @Override + int getSelectedItemIndex(); + @Import("selectedItemWidget") @Override int getSelectedItemWidget(); @@ -1308,6 +1312,10 @@ public interface RSClient extends RSGameEngine, Client @Import("isItemSelected") int isItemSelected(); + @Override + @Import("isItemSelected") + int getSelectedItem(); + @Override @Import("selectedItemName") String getSelectedItemName(); From 790f18128ffe99175f02108473f9bf8d7799d7c5 Mon Sep 17 00:00:00 2001 From: David Luong Date: Tue, 8 Mar 2022 23:03:58 -0500 Subject: [PATCH 25/32] item identification: add goblin and magic ess potions --- .../client/plugins/itemidentification/ItemIdentification.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java index 70b018c286..0ca4ed6edd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java @@ -317,6 +317,9 @@ enum ItemIdentification FISHING(Type.POTION, "Fishing", "Fi", ItemID.FISHING_POTION4, ItemID.FISHING_POTION3, ItemID.FISHING_POTION2, ItemID.FISHING_POTION1), HUNTER(Type.POTION, "Hunter", "Hu", ItemID.HUNTER_POTION4, ItemID.HUNTER_POTION3, ItemID.HUNTER_POTION2, ItemID.HUNTER_POTION1), + GOBLIN(Type.POTION, "Goblin", "G", ItemID.GOBLIN_POTION4, ItemID.GOBLIN_POTION3, ItemID.GOBLIN_POTION2, ItemID.GOBLIN_POTION1), + MAGIC_ESS(Type.POTION, "MagEss", "M.E", ItemID.MAGIC_ESSENCE4, ItemID.MAGIC_ESSENCE3, ItemID.MAGIC_ESSENCE2, ItemID.MAGIC_ESSENCE1), + // Unfinished Potions GUAM_POTION(Type.POTION, "Guam", "G", ItemID.GUAM_POTION_UNF), MARRENTILL_POTION(Type.POTION, "Marren", "M", ItemID.MARRENTILL_POTION_UNF), From 65e3670a4614dc40bc6513afb262e2963d4aa183 Mon Sep 17 00:00:00 2001 From: Faycal Date: Tue, 8 Mar 2022 14:36:49 +0100 Subject: [PATCH 26/32] item identification: add rejuvenation potion --- .../client/plugins/itemidentification/ItemIdentification.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java index 0ca4ed6edd..dd25f32fc7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemidentification/ItemIdentification.java @@ -319,6 +319,7 @@ enum ItemIdentification GOBLIN(Type.POTION, "Goblin", "G", ItemID.GOBLIN_POTION4, ItemID.GOBLIN_POTION3, ItemID.GOBLIN_POTION2, ItemID.GOBLIN_POTION1), MAGIC_ESS(Type.POTION, "MagEss", "M.E", ItemID.MAGIC_ESSENCE4, ItemID.MAGIC_ESSENCE3, ItemID.MAGIC_ESSENCE2, ItemID.MAGIC_ESSENCE1), + REJUVENATION(Type.POTION, "Rejuv", "Rj", ItemID.REJUVENATION_POTION_4, ItemID.REJUVENATION_POTION_3, ItemID.REJUVENATION_POTION_2, ItemID.REJUVENATION_POTION_1), // Unfinished Potions GUAM_POTION(Type.POTION, "Guam", "G", ItemID.GUAM_POTION_UNF), From de2edb2033a3e7cdfa24bcfd758dc01cf2c7a60a Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 9 Mar 2022 10:41:31 -0500 Subject: [PATCH 27/32] Add barbarian rod anim to fishing and idle notifier --- runelite-api/src/main/java/net/runelite/api/AnimationID.java | 1 + .../net/runelite/client/plugins/fishing/FishingOverlay.java | 3 ++- .../client/plugins/idlenotifier/IdleNotifierPlugin.java | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/runelite-api/src/main/java/net/runelite/api/AnimationID.java b/runelite-api/src/main/java/net/runelite/api/AnimationID.java index ab4928ccb6..6c94279c10 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -133,6 +133,7 @@ public final class AnimationID public static final int FISHING_PEARL_FLY_ROD_2 = 8192; public static final int FISHING_PEARL_BARBARIAN_ROD_2 = 8193; public static final int FISHING_PEARL_OILY_ROD = 6932; + public static final int FISHING_BARBARIAN_ROD = 9350; public static final int MINING_BRONZE_PICKAXE = 625; public static final int MINING_IRON_PICKAXE = 626; public static final int MINING_STEEL_PICKAXE = 627; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingOverlay.java index 6d2f06d473..848632af92 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingOverlay.java @@ -78,7 +78,8 @@ class FishingOverlay extends OverlayPanel AnimationID.FISHING_PEARL_ROD_2, AnimationID.FISHING_PEARL_FLY_ROD_2, AnimationID.FISHING_PEARL_BARBARIAN_ROD_2, - AnimationID.FISHING_PEARL_OILY_ROD); + AnimationID.FISHING_PEARL_OILY_ROD, + AnimationID.FISHING_BARBARIAN_ROD); private final Client client; private final FishingPlugin plugin; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index 5cae67799b..ddcdd5fa92 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -224,6 +224,7 @@ public class IdleNotifierPlugin extends Plugin case FISHING_PEARL_FLY_ROD_2: case FISHING_PEARL_BARBARIAN_ROD_2: case FISHING_PEARL_OILY_ROD: + case FISHING_BARBARIAN_ROD: /* Mining(Normal) */ case MINING_BRONZE_PICKAXE: case MINING_IRON_PICKAXE: From c121e652093a9633b7fe09b53c222aa5e9ebc947 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Wed, 9 Mar 2022 16:28:54 +0000 Subject: [PATCH 28/32] Release 1.8.13 --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-jshell/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 7246369f27..25e7779933 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13-SNAPSHOT + 1.8.13 cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 57c3667f93..d95041a672 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.8.13-SNAPSHOT + 1.8.13 Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 84a81a4512..cccaffc872 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13-SNAPSHOT + 1.8.13 cache diff --git a/pom.xml b/pom.xml index fa8dfc977d..0c528efd83 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.8.13-SNAPSHOT + 1.8.13 pom RuneLite @@ -63,7 +63,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - HEAD + runelite-parent-1.8.13 diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 15c2888624..28fc008a5d 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13-SNAPSHOT + 1.8.13 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index b24ff0f30c..f6057c22cb 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13-SNAPSHOT + 1.8.13 client diff --git a/runelite-jshell/pom.xml b/runelite-jshell/pom.xml index 78198cd8d5..aed99e99cb 100644 --- a/runelite-jshell/pom.xml +++ b/runelite-jshell/pom.xml @@ -30,7 +30,7 @@ net.runelite runelite-parent - 1.8.13-SNAPSHOT + 1.8.13 jshell diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 2c26602602..6ad73c1246 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13-SNAPSHOT + 1.8.13 script-assembler-plugin From 4eb0fb56e3c518e5a0c05069c7a1b20ef661b978 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Wed, 9 Mar 2022 16:29:02 +0000 Subject: [PATCH 29/32] Bump for 1.8.14-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-jshell/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 25e7779933..6e03e322f4 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13 + 1.8.14-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index d95041a672..d516abdee4 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.8.13 + 1.8.14-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index cccaffc872..ec85e354bb 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13 + 1.8.14-SNAPSHOT cache diff --git a/pom.xml b/pom.xml index 0c528efd83..7d2aecbe20 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.8.13 + 1.8.14-SNAPSHOT pom RuneLite @@ -63,7 +63,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - runelite-parent-1.8.13 + HEAD diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 28fc008a5d..82dd597f29 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13 + 1.8.14-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index f6057c22cb..3467eb7ac8 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13 + 1.8.14-SNAPSHOT client diff --git a/runelite-jshell/pom.xml b/runelite-jshell/pom.xml index aed99e99cb..c480798e4d 100644 --- a/runelite-jshell/pom.xml +++ b/runelite-jshell/pom.xml @@ -30,7 +30,7 @@ net.runelite runelite-parent - 1.8.13 + 1.8.14-SNAPSHOT jshell diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 6ad73c1246..7965daed5e 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.8.13 + 1.8.14-SNAPSHOT script-assembler-plugin From 40e9acfd09152144e6e9ce322e6bafd8035dc21b Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 9 Mar 2022 22:44:05 +0100 Subject: [PATCH 30/32] project: Bump RuneLite version --- buildSrc/src/main/kotlin/Dependencies.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 37eeb877dc..3c4b2f3f57 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -25,7 +25,7 @@ object ProjectVersions { const val launcherVersion = "2.2.0" - const val rlVersion = "1.8.12" + const val rlVersion = "1.8.13" const val openosrsVersion = "4.20.2" From eefd891cb7356f49a189fb2191fc827be2299aa1 Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 9 Mar 2022 22:44:29 +0100 Subject: [PATCH 31/32] project: Bump OpenOSRS version --- buildSrc/src/main/kotlin/Dependencies.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 3c4b2f3f57..05cef0b42b 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -27,7 +27,7 @@ object ProjectVersions { const val launcherVersion = "2.2.0" const val rlVersion = "1.8.13" - const val openosrsVersion = "4.20.2" + const val openosrsVersion = "4.20.3" const val rsversion = 203 const val cacheversion = 165 From 2fe8585035b127aed4d142b5641656d84a65a060 Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Wed, 9 Mar 2022 22:57:43 +0100 Subject: [PATCH 32/32] loottracker: Remove import loot panel --- .../plugins/loottracker/LootTrackerPanel.java | 37 ------ .../loottracker/LootTrackerPlugin.java | 114 ------------------ 2 files changed, 151 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java index 11dfb21abf..d08157ce99 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java @@ -52,7 +52,6 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JRadioButton; import javax.swing.JToggleButton; -import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import javax.swing.plaf.basic.BasicButtonUI; import javax.swing.plaf.basic.BasicToggleButtonUI; @@ -85,7 +84,6 @@ class LootTrackerPanel extends PluginPanel private static final ImageIcon INVISIBLE_ICON_HOVER; private static final ImageIcon COLLAPSE_ICON; private static final ImageIcon EXPAND_ICON; - private static final ImageIcon IMPORT_ICON; private static final String HTML_LABEL_TEMPLATE = "%s%s"; @@ -117,8 +115,6 @@ class LootTrackerPanel extends PluginPanel private final JRadioButton groupedLootBtn = new JRadioButton(); private final JButton collapseBtn = new JButton(); - private final JPanel importNoticePanel; - // Aggregate of all kills private final List aggregateRecords = new ArrayList<>(); // Individual records for the individual kills this session @@ -163,8 +159,6 @@ class LootTrackerPanel extends PluginPanel COLLAPSE_ICON = new ImageIcon(collapseImg); EXPAND_ICON = new ImageIcon(expandedImg); - - IMPORT_ICON = new ImageIcon(ImageUtil.loadImageResource(LootTrackerPlugin.class, "import_icon.png")); } LootTrackerPanel(final LootTrackerPlugin plugin, final ItemManager itemManager, final LootTrackerConfig config) @@ -185,12 +179,10 @@ class LootTrackerPanel extends PluginPanel actionsPanel = buildActionsPanel(); overallPanel = buildOverallPanel(); - importNoticePanel = createImportNoticePanel(); // Create loot boxes wrapper logsContainer.setLayout(new BoxLayout(logsContainer, BoxLayout.Y_AXIS)); layoutPanel.add(actionsPanel); - layoutPanel.add(importNoticePanel); layoutPanel.add(overallPanel); layoutPanel.add(logsContainer); @@ -354,30 +346,6 @@ class LootTrackerPanel extends PluginPanel return overallPanel; } - private JPanel createImportNoticePanel() - { - JPanel panel = new JPanel(); - panel.setBackground(ColorScheme.DARKER_GRAY_COLOR); - panel.setBorder(BorderFactory.createCompoundBorder( - BorderFactory.createMatteBorder(5, 0, 0, 0, ColorScheme.DARK_GRAY_COLOR), - BorderFactory.createEmptyBorder(8, 10, 8, 10) - )); - panel.setLayout(new BorderLayout()); - - final JLabel importLabel = new JLabel("Missing saved loot? Click the
import button to import it."); - importLabel.setForeground(Color.YELLOW); - panel.add(importLabel, BorderLayout.WEST); - - JButton importButton = new JButton(); - SwingUtil.removeButtonDecorations(importButton); - importButton.setIcon(IMPORT_ICON); - importButton.setToolTipText("Import old loot tracker data to current profile"); - importButton.addActionListener(l -> plugin.importLoot()); - panel.add(importButton, BorderLayout.EAST); - - return panel; - } - void updateCollapseText() { collapseBtn.setSelected(isAllCollapsed()); @@ -736,9 +704,4 @@ class LootTrackerPanel extends PluginPanel final String valueStr = QuantityFormatter.quantityToStackSize(value); return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); } - - void toggleImportNotice(boolean on) - { - SwingUtilities.invokeLater(() -> importNoticePanel.setVisible(on)); - } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java index 39d2b6bf81..726e74d7ac 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPlugin.java @@ -38,7 +38,6 @@ import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import com.google.inject.Provides; import java.awt.image.BufferedImage; -import java.io.IOException; import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; @@ -60,7 +59,6 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.inject.Inject; -import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import lombok.AccessLevel; import lombok.Getter; @@ -469,8 +467,6 @@ public class LootTrackerPlugin extends Plugin panel.addRecords(records); }); }); - - panel.toggleImportNotice(!hasImported()); }); } @@ -1340,115 +1336,5 @@ public class LootTrackerPlugin extends Plugin { configManager.unsetConfiguration(LootTrackerConfig.GROUP, profile, key); } - - clearImported(); - panel.toggleImportNotice(true); - } - - void importLoot() - { - if (configManager.getRSProfileKey() == null) - { - JOptionPane.showMessageDialog(panel, "You do not have an active profile to import loot into; log in to the game first."); - return; - } - - if (lootTrackerClient.getUuid() == null) - { - JOptionPane.showMessageDialog(panel, "You are not logged into RuneLite, so loot can not be imported from your account. Log in first."); - return; - } - - if (lastLootImport.isAfter(Instant.now().minus(1, ChronoUnit.MINUTES))) - { - JOptionPane.showMessageDialog(panel, "You imported too recently. Wait a minute and try again."); - return; - } - - lastLootImport = Instant.now(); - - executor.execute(() -> - { - if (hasImported()) - { - SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(panel, "You already have imported loot.")); - return; - } - - Collection lootRecords; - try - { - lootRecords = lootTrackerClient.get(); - } - catch (IOException e) - { - log.debug("Unable to look up loot", e); - return; - } - - log.debug("Loaded {} data entries", lootRecords.size()); - - for (LootAggregate record : lootRecords) - { - ConfigLoot lootConfig = getLootConfig(record.getType(), record.getEventId()); - if (lootConfig == null) - { - lootConfig = new ConfigLoot(record.getType(), record.getEventId()); - } - lootConfig.first = record.getFirst_time(); - lootConfig.last = record.getLast_time(); - lootConfig.kills += record.getAmount(); - for (GameItem gameItem : record.getDrops()) - { - lootConfig.add(gameItem.getId(), gameItem.getQty()); - } - setLootConfig(record.getType(), record.getEventId(), lootConfig); - } - - clientThread.invokeLater(() -> - { - Collection records = convertToLootTrackerRecord(lootRecords); - SwingUtilities.invokeLater(() -> panel.addRecords(records)); - }); - - SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(panel, "Imported " + lootRecords.size() + " loot entries.")); - - setHasImported(); - panel.toggleImportNotice(false); - }); - } - - void setHasImported() - { - String profile = profileKey; - if (Strings.isNullOrEmpty(profile)) - { - return; - } - - configManager.setConfiguration(LootTrackerConfig.GROUP, profile, "imported", 1); - } - - boolean hasImported() - { - String profile = profileKey; - if (Strings.isNullOrEmpty(profile)) - { - return false; - } - - Integer i = configManager.getConfiguration(LootTrackerConfig.GROUP, profile, "imported", Integer.class); - return i != null && i == 1; - } - - void clearImported() - { - String profile = profileKey; - if (Strings.isNullOrEmpty(profile)) - { - return; - } - - configManager.unsetConfiguration(LootTrackerConfig.GROUP, profile, "imported"); } }