Merge pull request #7068 from TheStonedTurtle/LTGetData

Display persistent data inside Loot Tracker UI
This commit is contained in:
Adam
2018-12-28 19:10:09 -05:00
committed by GitHub
7 changed files with 210 additions and 27 deletions

View File

@@ -25,7 +25,13 @@
package net.runelite.http.api.loottracker; package net.runelite.http.api.loottracker;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;
import java.io.IOException; 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 java.util.UUID;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@@ -75,4 +81,68 @@ public class LootTrackerClient
} }
}); });
} }
public Collection<LootRecord> 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<List<LootRecord>>()
{
}.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();
}
});
}
} }

View File

@@ -31,7 +31,6 @@ import java.util.Collection;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import net.runelite.http.api.loottracker.LootRecord; 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.AuthFilter;
import net.runelite.http.service.account.beans.SessionEntry; import net.runelite.http.service.account.beans.SessionEntry;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@@ -81,7 +80,7 @@ public class LootTrackerController
@DeleteMapping @DeleteMapping
public void deleteLoot(HttpServletRequest request, HttpServletResponse response, 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); SessionEntry e = auth.handle(request, response);
if (e == null) if (e == null)
@@ -90,6 +89,6 @@ public class LootTrackerController
return; return;
} }
service.delete(e.getUser(), type, eventId); service.delete(e.getUser(), eventId);
} }
} }

View File

@@ -30,7 +30,6 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import net.runelite.http.api.loottracker.GameItem; import net.runelite.http.api.loottracker.GameItem;
import net.runelite.http.api.loottracker.LootRecord; 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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Scheduled; 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 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 = "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; private final Sql2o sql2o;
@@ -164,21 +163,20 @@ public class LootTrackerService
return lootRecords; return lootRecords;
} }
public void delete(int accountId, LootRecordType type, String eventId) public void delete(int accountId, String eventId)
{ {
try (Connection con = sql2o.open()) try (Connection con = sql2o.open())
{ {
if (eventId == null && type == null) if (eventId == null)
{ {
con.createQuery(DELETE_LOOT_ACCOUNT) con.createQuery(DELETE_LOOT_ACCOUNT)
.addParameter("accountId", accountId) .addParameter("accountId", accountId)
.executeUpdate(); .executeUpdate();
} }
else if (eventId != null && type != null) else
{ {
con.createQuery(DELETE_LOOT_ACCOUNT_TYPE) con.createQuery(DELETE_LOOT_ACCOUNT_EVENTID)
.addParameter("accountId", accountId) .addParameter("accountId", accountId)
.addParameter("type", type)
.addParameter("eventId", eventId) .addParameter("eventId", eventId)
.executeUpdate(); .executeUpdate();
} }

View File

@@ -42,6 +42,7 @@ import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.SwingConstants; import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import net.runelite.client.game.AsyncBufferedImage; import net.runelite.client.game.AsyncBufferedImage;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
@@ -58,6 +59,7 @@ class LootTrackerBox extends JPanel
private final JLabel priceLabel = new JLabel(); private final JLabel priceLabel = new JLabel();
private final JLabel subTitleLabel = new JLabel(); private final JLabel subTitleLabel = new JLabel();
private final ItemManager itemManager; private final ItemManager itemManager;
@Getter(AccessLevel.PACKAGE)
private final String id; private final String id;
@Getter @Getter

View File

@@ -48,4 +48,14 @@ public interface LootTrackerConfig extends Config
description = "" description = ""
) )
void setIgnoredItems(String key); 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;
}
} }

View File

@@ -33,6 +33,7 @@ import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.List; import java.util.List;
import javax.swing.Box; import javax.swing.Box;
import javax.swing.BoxLayout; 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.ColorUtil;
import net.runelite.client.util.ImageUtil; import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.StackFormatter; import net.runelite.client.util.StackFormatter;
import net.runelite.http.api.loottracker.LootTrackerClient;
class LootTrackerPanel extends PluginPanel class LootTrackerPanel extends PluginPanel
{ {
@@ -292,6 +294,13 @@ class LootTrackerPanel extends PluginPanel
updateOverall(); updateOverall();
logsContainer.removeAll(); logsContainer.removeAll();
logsContainer.repaint(); logsContainer.repaint();
// Delete all loot, or loot matching the current view
LootTrackerClient client = plugin.getLootTrackerClient();
if (client != null)
{
client.delete(currentView);
}
}); });
// Create popup menu // Create popup menu
@@ -335,6 +344,23 @@ class LootTrackerPanel extends PluginPanel
} }
} }
/**
* Adds a Collection of records to the panel
*/
void addRecords(Collection<LootTrackerRecord> recs)
{
for (LootTrackerRecord r : recs)
{
records.add(r);
LootTrackerBox box = buildBox(r);
if (box != null)
{
box.rebuild();
}
}
updateOverall();
}
/** /**
* Changes grouping mode of panel * Changes grouping mode of panel
* *
@@ -451,6 +477,13 @@ class LootTrackerPanel extends PluginPanel
updateOverall(); updateOverall();
logsContainer.remove(box); logsContainer.remove(box);
logsContainer.repaint(); 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); popupMenu.add(reset);

View File

@@ -29,17 +29,21 @@ import com.google.common.base.Joiner;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.inject.Inject; import javax.inject.Inject;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.ChatMessageType; import net.runelite.api.ChatMessageType;
import net.runelite.api.Client; import net.runelite.api.Client;
@@ -58,6 +62,7 @@ import net.runelite.api.events.WidgetLoaded;
import net.runelite.api.widgets.WidgetID; import net.runelite.api.widgets.WidgetID;
import net.runelite.client.account.AccountSession; import net.runelite.client.account.AccountSession;
import net.runelite.client.account.SessionManager; import net.runelite.client.account.SessionManager;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.events.NpcLootReceived; import net.runelite.client.events.NpcLootReceived;
@@ -111,15 +116,22 @@ public class LootTrackerPlugin extends Plugin
@Inject @Inject
private Client client; private Client client;
@Inject
private ClientThread clientThread;
@Inject @Inject
private SessionManager sessionManager; private SessionManager sessionManager;
@Inject
private ScheduledExecutorService executor;
private LootTrackerPanel panel; private LootTrackerPanel panel;
private NavigationButton navButton; private NavigationButton navButton;
private String eventType; private String eventType;
private List<String> ignoredItems = new ArrayList<>(); private List<String> ignoredItems = new ArrayList<>();
@Getter(AccessLevel.PACKAGE)
private LootTrackerClient lootTrackerClient; private LootTrackerClient lootTrackerClient;
private static Collection<ItemStack> stack(Collection<ItemStack> items) private static Collection<ItemStack> stack(Collection<ItemStack> items)
@@ -209,6 +221,46 @@ public class LootTrackerPlugin extends Plugin
if (accountSession != null) if (accountSession != null)
{ {
lootTrackerClient = new LootTrackerClient(accountSession.getUuid()); lootTrackerClient = new LootTrackerClient(accountSession.getUuid());
clientThread.invokeLater(() ->
{
switch (client.getGameState())
{
case STARTING:
case UNKNOWN:
return false;
}
executor.submit(() ->
{
Collection<LootRecord> 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<LootTrackerRecord> 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)); final LootTrackerItem[] entries = buildEntries(stack(items));
SwingUtilities.invokeLater(() -> panel.add(name, combat, entries)); SwingUtilities.invokeLater(() -> panel.add(name, combat, entries));
if (lootTrackerClient != null) if (lootTrackerClient != null && config.saveLoot())
{ {
LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items)); LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items));
lootTrackerClient.submit(lootRecord); lootTrackerClient.submit(lootRecord);
@@ -246,7 +298,7 @@ public class LootTrackerPlugin extends Plugin
final LootTrackerItem[] entries = buildEntries(stack(items)); final LootTrackerItem[] entries = buildEntries(stack(items));
SwingUtilities.invokeLater(() -> panel.add(name, combat, entries)); SwingUtilities.invokeLater(() -> panel.add(name, combat, entries));
if (lootTrackerClient != null) if (lootTrackerClient != null && config.saveLoot())
{ {
LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items)); LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items));
lootTrackerClient.submit(lootRecord); lootTrackerClient.submit(lootRecord);
@@ -305,7 +357,7 @@ public class LootTrackerPlugin extends Plugin
final LootTrackerItem[] entries = buildEntries(stack(items)); final LootTrackerItem[] entries = buildEntries(stack(items));
SwingUtilities.invokeLater(() -> panel.add(eventType, -1, entries)); SwingUtilities.invokeLater(() -> panel.add(eventType, -1, entries));
if (lootTrackerClient != null) if (lootTrackerClient != null && config.saveLoot())
{ {
LootRecord lootRecord = new LootRecord(eventType, LootRecordType.EVENT, toGameItems(items)); LootRecord lootRecord = new LootRecord(eventType, LootRecordType.EVENT, toGameItems(items));
lootTrackerClient.submit(lootRecord); lootTrackerClient.submit(lootRecord);
@@ -368,22 +420,26 @@ public class LootTrackerPlugin extends Plugin
return ignoredItems.contains(name); 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<ItemStack> itemStacks) private LootTrackerItem[] buildEntries(final Collection<ItemStack> itemStacks)
{ {
return itemStacks.stream().map(itemStack -> return itemStacks.stream()
{ .map(itemStack -> buildLootTrackerItem(itemStack.getId(), itemStack.getQuantity()))
final ItemComposition itemComposition = itemManager.getItemComposition(itemStack.getId()); .toArray(LootTrackerItem[]::new);
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);
} }
private static Collection<GameItem> toGameItems(Collection<ItemStack> items) private static Collection<GameItem> toGameItems(Collection<ItemStack> items)
@@ -392,4 +448,19 @@ public class LootTrackerPlugin extends Plugin
.map(item -> new GameItem(item.getId(), item.getQuantity())) .map(item -> new GameItem(item.getId(), item.getQuantity()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
private Collection<LootTrackerRecord> convertToLootTrackerRecord(final Collection<LootRecord> records)
{
Collection<LootTrackerRecord> 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;
}
} }