From 1af1601b3e9253d0a6cd8725ac05708a0accd86e Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 28 Dec 2018 17:03:53 -0500 Subject: [PATCH 1/3] loot tracker service: remove type argument from delete The client does not track type and eventId so it cannot use it --- .../service/loottracker/LootTrackerController.java | 5 ++--- .../http/service/loottracker/LootTrackerService.java | 12 +++++------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java index 945dffb68b..36e0a2333d 100644 --- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java +++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java @@ -31,7 +31,6 @@ import java.util.Collection; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import net.runelite.http.api.loottracker.LootRecord; -import net.runelite.http.api.loottracker.LootRecordType; import net.runelite.http.service.account.AuthFilter; import net.runelite.http.service.account.beans.SessionEntry; import org.springframework.beans.factory.annotation.Autowired; @@ -81,7 +80,7 @@ public class LootTrackerController @DeleteMapping public void deleteLoot(HttpServletRequest request, HttpServletResponse response, - @RequestParam(required = false) LootRecordType type, @RequestParam(required = false) String eventId) throws IOException + @RequestParam(required = false) String eventId) throws IOException { SessionEntry e = auth.handle(request, response); if (e == null) @@ -90,6 +89,6 @@ public class LootTrackerController return; } - service.delete(e.getUser(), type, eventId); + service.delete(e.getUser(), eventId); } } \ No newline at end of file diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java index c5f396add2..694bda2d74 100644 --- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java +++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java @@ -30,7 +30,6 @@ import java.util.Collection; import java.util.List; import net.runelite.http.api.loottracker.GameItem; import net.runelite.http.api.loottracker.LootRecord; -import net.runelite.http.api.loottracker.LootRecordType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.scheduling.annotation.Scheduled; @@ -70,7 +69,7 @@ public class LootTrackerService private static final String SELECT_LOOT_QUERY = "SELECT killId,time,type,eventId,itemId,itemQuantity FROM kills JOIN drops ON drops.killId = kills.id WHERE accountId = :accountId ORDER BY TIME DESC LIMIT :limit"; private static final String DELETE_LOOT_ACCOUNT = "DELETE FROM kills WHERE accountId = :accountId"; - private static final String DELETE_LOOT_ACCOUNT_TYPE = "DELETE FROM kills WHERE accountId = :accountId AND type = :type AND eventId = :eventId"; + private static final String DELETE_LOOT_ACCOUNT_EVENTID = "DELETE FROM kills WHERE accountId = :accountId AND eventId = :eventId"; private final Sql2o sql2o; @@ -164,21 +163,20 @@ public class LootTrackerService return lootRecords; } - public void delete(int accountId, LootRecordType type, String eventId) + public void delete(int accountId, String eventId) { try (Connection con = sql2o.open()) { - if (eventId == null && type == null) + if (eventId == null) { con.createQuery(DELETE_LOOT_ACCOUNT) .addParameter("accountId", accountId) .executeUpdate(); } - else if (eventId != null && type != null) + else { - con.createQuery(DELETE_LOOT_ACCOUNT_TYPE) + con.createQuery(DELETE_LOOT_ACCOUNT_EVENTID) .addParameter("accountId", accountId) - .addParameter("type", type) .addParameter("eventId", eventId) .executeUpdate(); } From fdf308a6947bb1660633b332ea685229eea84f47 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 28 Dec 2018 17:04:54 -0500 Subject: [PATCH 2/3] loot tracker client: add get and delete methods --- .../api/loottracker/LootTrackerClient.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java b/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java index 41d80311c1..ac967af886 100644 --- a/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java +++ b/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java @@ -25,7 +25,13 @@ package net.runelite.http.api.loottracker; import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Collection; +import java.util.List; import java.util.UUID; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -75,4 +81,68 @@ public class LootTrackerClient } }); } + + public Collection get() throws IOException + { + HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("loottracker") + .build(); + + Request request = new Request.Builder() + .header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString()) + .get() + .url(url) + .build(); + + try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) + { + if (!response.isSuccessful()) + { + log.debug("Error looking up loot: {}", response.message()); + return null; + } + + InputStream in = response.body().byteStream(); + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), new TypeToken>() + { + }.getType()); + } + catch (JsonParseException ex) + { + throw new IOException(ex); + } + } + + public void delete(String eventId) + { + HttpUrl.Builder builder = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("loottracker"); + + if (eventId != null) + { + builder.addQueryParameter("eventId", eventId); + } + + Request request = new Request.Builder() + .header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString()) + .delete() + .url(builder.build()) + .build(); + + RuneLiteAPI.CLIENT.newCall(request).enqueue(new Callback() + { + @Override + public void onFailure(Call call, IOException e) + { + log.warn("unable to delete loot", e); + } + + @Override + public void onResponse(Call call, Response response) + { + log.debug("Deleted loot"); + response.close(); + } + }); + } } From 1d53e4e64ca3d8d28a931c4863662c30e3d48692 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 28 Dec 2018 17:05:53 -0500 Subject: [PATCH 3/3] loot tracker plugin: support saving loot tracker data across restart Co-authored-by: TheStonedTurtle --- .../plugins/loottracker/LootTrackerBox.java | 2 + .../loottracker/LootTrackerConfig.java | 10 ++ .../plugins/loottracker/LootTrackerPanel.java | 33 ++++++ .../loottracker/LootTrackerPlugin.java | 105 +++++++++++++++--- 4 files changed, 133 insertions(+), 17 deletions(-) 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 9ebc9f78b1..49faacbf2b 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 @@ -42,6 +42,7 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; +import lombok.AccessLevel; import lombok.Getter; import net.runelite.client.game.AsyncBufferedImage; import net.runelite.client.game.ItemManager; @@ -58,6 +59,7 @@ class LootTrackerBox extends JPanel private final JLabel priceLabel = new JLabel(); private final JLabel subTitleLabel = new JLabel(); private final ItemManager itemManager; + @Getter(AccessLevel.PACKAGE) private final String id; @Getter 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 5776543b81..98b7053117 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 @@ -48,4 +48,14 @@ public interface LootTrackerConfig extends Config description = "" ) void setIgnoredItems(String key); + + @ConfigItem( + keyName = "saveLoot", + name = "Save loot", + description = "Save loot between client sessions (requires being logged in)" + ) + default boolean saveLoot() + { + return true; + } } \ No newline at end of file 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 001e747bb4..6db0459d7a 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 @@ -33,6 +33,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import javax.swing.Box; import javax.swing.BoxLayout; @@ -50,6 +51,7 @@ import net.runelite.client.ui.components.PluginErrorPanel; import net.runelite.client.util.ColorUtil; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.StackFormatter; +import net.runelite.http.api.loottracker.LootTrackerClient; class LootTrackerPanel extends PluginPanel { @@ -292,6 +294,13 @@ class LootTrackerPanel extends PluginPanel updateOverall(); logsContainer.removeAll(); logsContainer.repaint(); + + // Delete all loot, or loot matching the current view + LootTrackerClient client = plugin.getLootTrackerClient(); + if (client != null) + { + client.delete(currentView); + } }); // Create popup menu @@ -335,6 +344,23 @@ class LootTrackerPanel extends PluginPanel } } + /** + * Adds a Collection of records to the panel + */ + void addRecords(Collection recs) + { + for (LootTrackerRecord r : recs) + { + records.add(r); + LootTrackerBox box = buildBox(r); + if (box != null) + { + box.rebuild(); + } + } + updateOverall(); + } + /** * Changes grouping mode of panel * @@ -451,6 +477,13 @@ class LootTrackerPanel extends PluginPanel updateOverall(); logsContainer.remove(box); logsContainer.repaint(); + + LootTrackerClient client = plugin.getLootTrackerClient(); + // Without loot being grouped we have no way to identify single kills to be deleted + if (client != null && groupLoot) + { + client.delete(box.getId()); + } }); popupMenu.add(reset); 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 90664a5622..d05be74f57 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 @@ -29,17 +29,21 @@ import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.inject.Provides; import java.awt.image.BufferedImage; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.concurrent.ScheduledExecutorService; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.inject.Inject; import javax.swing.SwingUtilities; +import lombok.AccessLevel; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; @@ -58,6 +62,7 @@ import net.runelite.api.events.WidgetLoaded; import net.runelite.api.widgets.WidgetID; import net.runelite.client.account.AccountSession; import net.runelite.client.account.SessionManager; +import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.NpcLootReceived; @@ -111,15 +116,22 @@ public class LootTrackerPlugin extends Plugin @Inject private Client client; + @Inject + private ClientThread clientThread; + @Inject private SessionManager sessionManager; + @Inject + private ScheduledExecutorService executor; + private LootTrackerPanel panel; private NavigationButton navButton; private String eventType; private List ignoredItems = new ArrayList<>(); + @Getter(AccessLevel.PACKAGE) private LootTrackerClient lootTrackerClient; private static Collection stack(Collection items) @@ -209,6 +221,46 @@ public class LootTrackerPlugin extends Plugin if (accountSession != null) { lootTrackerClient = new LootTrackerClient(accountSession.getUuid()); + + clientThread.invokeLater(() -> + { + switch (client.getGameState()) + { + case STARTING: + case UNKNOWN: + return false; + } + + executor.submit(() -> + { + Collection lootRecords; + + if (!config.saveLoot()) + { + // don't load loot if we're not saving loot + return; + } + + 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; + }); } } @@ -229,7 +281,7 @@ public class LootTrackerPlugin extends Plugin final LootTrackerItem[] entries = buildEntries(stack(items)); SwingUtilities.invokeLater(() -> panel.add(name, combat, entries)); - if (lootTrackerClient != null) + if (lootTrackerClient != null && config.saveLoot()) { LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items)); lootTrackerClient.submit(lootRecord); @@ -246,7 +298,7 @@ public class LootTrackerPlugin extends Plugin final LootTrackerItem[] entries = buildEntries(stack(items)); SwingUtilities.invokeLater(() -> panel.add(name, combat, entries)); - if (lootTrackerClient != null) + if (lootTrackerClient != null && config.saveLoot()) { LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items)); lootTrackerClient.submit(lootRecord); @@ -305,7 +357,7 @@ public class LootTrackerPlugin extends Plugin final LootTrackerItem[] entries = buildEntries(stack(items)); SwingUtilities.invokeLater(() -> panel.add(eventType, -1, entries)); - if (lootTrackerClient != null) + if (lootTrackerClient != null && config.saveLoot()) { LootRecord lootRecord = new LootRecord(eventType, LootRecordType.EVENT, toGameItems(items)); lootTrackerClient.submit(lootRecord); @@ -368,22 +420,26 @@ public class LootTrackerPlugin extends Plugin return ignoredItems.contains(name); } + private LootTrackerItem buildLootTrackerItem(int itemId, int quantity) + { + final ItemComposition itemComposition = itemManager.getItemComposition(itemId); + final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemId; + final long price = (long) itemManager.getItemPrice(realItemId) * (long) quantity; + final boolean ignored = ignoredItems.contains(itemComposition.getName()); + + return new LootTrackerItem( + itemId, + itemComposition.getName(), + quantity, + price, + ignored); + } + private LootTrackerItem[] buildEntries(final Collection itemStacks) { - return itemStacks.stream().map(itemStack -> - { - final ItemComposition itemComposition = itemManager.getItemComposition(itemStack.getId()); - final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemStack.getId(); - final long price = (long) itemManager.getItemPrice(realItemId) * (long) itemStack.getQuantity(); - final boolean ignored = ignoredItems.contains(itemComposition.getName()); - - return new LootTrackerItem( - itemStack.getId(), - itemComposition.getName(), - itemStack.getQuantity(), - price, - ignored); - }).toArray(LootTrackerItem[]::new); + return itemStacks.stream() + .map(itemStack -> buildLootTrackerItem(itemStack.getId(), itemStack.getQuantity())) + .toArray(LootTrackerItem[]::new); } private static Collection toGameItems(Collection items) @@ -392,4 +448,19 @@ public class LootTrackerPlugin extends Plugin .map(item -> new GameItem(item.getId(), item.getQuantity())) .collect(Collectors.toList()); } + + private Collection convertToLootTrackerRecord(final Collection records) + { + Collection trackerRecords = new ArrayList<>(); + for (LootRecord record : records) + { + LootTrackerItem[] drops = record.getDrops().stream().map(itemStack -> + buildLootTrackerItem(itemStack.getId(), itemStack.getQty()) + ).toArray(LootTrackerItem[]::new); + + trackerRecords.add(new LootTrackerRecord(record.getEventId(), "", drops, -1)); + } + + return trackerRecords; + } }