loot tracker: store aggregated drops

This modifies both the service and the client plugin to no longer store
loot per-kill, with the exception of the current sessions kill log. The
amount of loot data returned now from the service defaults to the last
1024 unique events
This commit is contained in:
Adam
2020-01-09 17:35:17 -05:00
committed by Adam
parent bb6ac3a2be
commit 97b4f0d56f
10 changed files with 203 additions and 163 deletions

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2020, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.http.api.loottracker;
import java.time.Instant;
import java.util.Collection;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LootAggregate
{
private String eventId;
private LootRecordType type;
private Collection<GameItem> drops;
private Instant first_time;
private Instant last_time;
private int amount;
}

View File

@@ -81,7 +81,7 @@ public class LootTrackerClient
});
}
public Collection<LootRecord> get() throws IOException
public Collection<LootAggregate> get() throws IOException
{
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
.addPathSegment("loottracker")
@@ -101,7 +101,7 @@ public class LootTrackerClient
}
InputStream in = response.body().byteStream();
return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), new TypeToken<List<LootRecord>>()
return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), new TypeToken<List<LootAggregate>>()
{
}.getType());
}

View File

@@ -32,9 +32,11 @@ import net.runelite.http.api.loottracker.LootRecordType;
class LootResult
{
private int killId;
private Instant time;
private Instant first_time;
private Instant last_time;
private LootRecordType type;
private String eventId;
private int amount;
private int itemId;
private int itemQuantity;
}

View File

@@ -30,6 +30,7 @@ import java.io.IOException;
import java.util.Collection;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.runelite.http.api.loottracker.LootAggregate;
import net.runelite.http.api.loottracker.LootRecord;
import net.runelite.http.service.account.AuthFilter;
import net.runelite.http.service.account.beans.SessionEntry;
@@ -67,7 +68,7 @@ public class LootTrackerController
}
@GetMapping
public Collection<LootRecord> getLootRecords(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "count", defaultValue = "1024") int count, @RequestParam(value = "start", defaultValue = "0") int start) throws IOException
public Collection<LootAggregate> getLootAggregate(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "count", defaultValue = "1024") int count, @RequestParam(value = "start", defaultValue = "0") int start) throws IOException
{
SessionEntry e = auth.handle(request, response);
if (e == null)

View File

@@ -29,6 +29,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import net.runelite.http.api.loottracker.GameItem;
import net.runelite.http.api.loottracker.LootAggregate;
import net.runelite.http.api.loottracker.LootRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
@@ -41,35 +42,37 @@ import org.sql2o.Sql2o;
@Service
public class LootTrackerService
{
// Table for storing individual LootRecords
private static final String CREATE_KILLS = "CREATE TABLE IF NOT EXISTS `kills` (\n"
+ " `id` INT AUTO_INCREMENT UNIQUE,\n"
+ " `time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),\n"
+ " `accountId` INT NOT NULL,\n"
+ " `type` enum('NPC', 'PLAYER', 'EVENT', 'UNKNOWN') NOT NULL,\n"
+ " `eventId` VARCHAR(255) NOT NULL,\n"
+ " PRIMARY KEY (id),\n"
+ " FOREIGN KEY (accountId) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,\n"
+ " INDEX idx_acc (accountId, time),"
+ " INDEX idx_time (time)"
+ ") ENGINE=InnoDB";
private static final String CREATE_KILLS = "CREATE TABLE IF NOT EXISTS `loottracker_kills` (\n" +
" `id` int(11) NOT NULL AUTO_INCREMENT,\n" +
" `first_time` timestamp NOT NULL DEFAULT current_timestamp(),\n" +
" `last_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),\n" +
" `accountId` int(11) NOT NULL,\n" +
" `type` enum('NPC','PLAYER','EVENT','UNKNOWN') NOT NULL,\n" +
" `eventId` varchar(255) NOT NULL,\n" +
" `amount` int(11) NOT NULL,\n" +
" PRIMARY KEY (`id`),\n" +
" FOREIGN KEY (accountId) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,\n" +
" INDEX idx_acc_lasttime (`accountId` ,`last_time`),\n" +
" UNIQUE INDEX idx_acc_type_event (`accountId`, `type`, `eventId`),\n" +
" INDEX idx_time (last_time)" +
") ENGINE=InnoDB;";
// Table for storing Items received as loot for individual LootRecords
private static final String CREATE_DROPS = "CREATE TABLE IF NOT EXISTS `drops` (\n"
+ " `killId` INT NOT NULL,\n"
+ " `itemId` INT NOT NULL,\n"
+ " `itemQuantity` INT NOT NULL,\n"
+ " FOREIGN KEY (killId) REFERENCES kills(id) ON DELETE CASCADE\n"
+ ") ENGINE=InnoDB";
private static final String CREATE_DROPS = "CREATE TABLE IF NOT EXISTS `loottracker_drops` (\n" +
" `killId` int(11),\n" +
" `itemId` int(11) NOT NULL,\n" +
" `itemQuantity` int(11) NOT NULL,\n" +
" UNIQUE INDEX idx_kill_item (`killId`, `itemId`),\n" +
" FOREIGN KEY (killId) REFERENCES loottracker_kills(id) ON DELETE CASCADE\n" +
") ENGINE=InnoDB;\n";
// Queries for inserting kills
private static final String INSERT_KILL_QUERY = "INSERT INTO kills (accountId, type, eventId) VALUES (:accountId, :type, :eventId)";
private static final String INSERT_DROP_QUERY = "INSERT INTO drops (killId, itemId, itemQuantity) VALUES (:killId, :itemId, :itemQuantity)";
private static final String INSERT_KILL_QUERY = "INSERT INTO loottracker_kills (accountId, type, eventId, amount) VALUES (:accountId, :type, :eventId, 1) ON DUPLICATE KEY UPDATE amount = amount + 1";
private static final String INSERT_DROP_QUERY = "INSERT INTO loottracker_drops (killId, itemId, itemQuantity) VALUES (:killId, :itemId, :itemQuantity) ON DUPLICATE KEY UPDATE itemQuantity = itemQuantity + :itemQuantity";
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 OFFSET :offset";
private static final String SELECT_LOOT_QUERY = "SELECT killId,first_time,last_time,type,eventId,amount,itemId,itemQuantity FROM loottracker_kills JOIN loottracker_drops ON loottracker_drops.killId = loottracker_kills.id WHERE accountId = :accountId ORDER BY last_time DESC LIMIT :limit OFFSET :offset";
private static final String DELETE_LOOT_ACCOUNT = "DELETE FROM kills WHERE accountId = :accountId";
private static final String DELETE_LOOT_ACCOUNT_EVENTID = "DELETE FROM kills WHERE accountId = :accountId AND eventId = :eventId";
private static final String DELETE_LOOT_ACCOUNT = "DELETE FROM loottracker_kills WHERE accountId = :accountId";
private static final String DELETE_LOOT_ACCOUNT_EVENTID = "DELETE FROM loottracker_kills WHERE accountId = :accountId AND eventId = :eventId";
private final Sql2o sql2o;
@@ -96,8 +99,8 @@ public class LootTrackerService
{
try (Connection con = sql2o.beginTransaction())
{
// Kill Entry Query
Query killQuery = con.createQuery(INSERT_KILL_QUERY, true);
Query insertDrop = con.createQuery(INSERT_DROP_QUERY);
for (LootRecord record : records)
{
@@ -105,41 +108,26 @@ public class LootTrackerService
.addParameter("accountId", accountId)
.addParameter("type", record.getType())
.addParameter("eventId", record.getEventId())
.addToBatch();
}
.executeUpdate();
Object[] keys = con.getKeys();
killQuery.executeBatch();
Object[] keys = con.getKeys();
if (keys.length != records.size())
{
throw new RuntimeException("Mismatch in keys vs records size");
}
Query insertDrop = con.createQuery(INSERT_DROP_QUERY);
// Append all queries for inserting drops
int idx = 0;
for (LootRecord record : records)
{
for (GameItem drop : record.getDrops())
{
insertDrop
.addParameter("killId", keys[idx])
.addParameter("killId", keys[0])
.addParameter("itemId", drop.getId())
.addParameter("itemQuantity", drop.getQty())
.addToBatch();
}
++idx;
insertDrop.executeBatch();
}
insertDrop.executeBatch();
con.commit(false);
}
}
public Collection<LootRecord> get(int accountId, int limit, int offset)
public Collection<LootAggregate> get(int accountId, int limit, int offset)
{
List<LootResult> lootResults;
@@ -153,7 +141,7 @@ public class LootTrackerService
}
LootResult current = null;
List<LootRecord> lootRecords = new ArrayList<>();
List<LootAggregate> lootRecords = new ArrayList<>();
List<GameItem> gameItems = new ArrayList<>();
for (LootResult lootResult : lootResults)
@@ -162,7 +150,7 @@ public class LootTrackerService
{
if (!gameItems.isEmpty())
{
LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems, current.getTime());
LootAggregate lootRecord = new LootAggregate(current.getEventId(), current.getType(), gameItems, current.getFirst_time(), current.getLast_time(), current.getAmount());
lootRecords.add(lootRecord);
gameItems = new ArrayList<>();
@@ -177,7 +165,7 @@ public class LootTrackerService
if (!gameItems.isEmpty())
{
LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems, current.getTime());
LootAggregate lootRecord = new LootAggregate(current.getEventId(), current.getType(), gameItems, current.getFirst_time(), current.getLast_time(), current.getAmount());
lootRecords.add(lootRecord);
}
@@ -204,12 +192,12 @@ public class LootTrackerService
}
}
@Scheduled(fixedDelay = 15 * 60 * 1000)
@Scheduled(fixedDelay = 60 * 60 * 1000)
public void expire()
{
try (Connection con = sql2o.open())
{
con.createQuery("delete from kills where time < current_timestamp() - interval 30 day")
con.createQuery("delete from loottracker_kills where last_time < current_timestamp() - interval 30 day")
.executeUpdate();
}
}

View File

@@ -33,9 +33,11 @@ import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
import javax.annotation.Nullable;
import javax.swing.Box;
import javax.swing.BoxLayout;
@@ -48,10 +50,10 @@ import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import lombok.AccessLevel;
import lombok.Getter;
import net.runelite.client.util.AsyncBufferedImage;
import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.util.AsyncBufferedImage;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.QuantityFormatter;
import net.runelite.client.util.Text;
@@ -65,15 +67,15 @@ class LootTrackerBox extends JPanel
private final JLabel priceLabel = new JLabel();
private final JLabel subTitleLabel = new JLabel();
private final JPanel logTitle = new JPanel();
private final JLabel titleLabel = new JLabel();
private final ItemManager itemManager;
@Getter(AccessLevel.PACKAGE)
private final String id;
private final LootTrackerPriceType priceType;
private final boolean showPriceType;
private int kills;
@Getter
private final List<LootTrackerRecord> records = new ArrayList<>();
private final List<LootTrackerItem> items = new ArrayList<>();
private long totalPrice;
private boolean hideIgnoredItems;
@@ -102,6 +104,7 @@ class LootTrackerBox extends JPanel
logTitle.setBorder(new EmptyBorder(7, 7, 7, 7));
logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker());
JLabel titleLabel = new JLabel();
titleLabel.setText(Text.removeTags(id));
titleLabel.setFont(FontManager.getRunescapeSmallFont());
titleLabel.setForeground(Color.WHITE);
@@ -131,15 +134,13 @@ class LootTrackerBox extends JPanel
}
/**
* Returns total amount of kills, removing ignored kills when necessary
* Returns total amount of kills
*
* @return total amount of kills
*/
private long getTotalKills()
private int getTotalKills()
{
return hideIgnoredItems
? records.stream().filter(r -> !Arrays.stream(r.getItems()).allMatch(LootTrackerItem::isIgnored)).count()
: records.size();
return kills;
}
/**
@@ -171,16 +172,32 @@ class LootTrackerBox extends JPanel
/**
* Adds an record's data into a loot box.
* This will add new items to the list, re-calculating price and kill count.
*/
void combine(final LootTrackerRecord record)
void addKill(final LootTrackerRecord record)
{
if (!matches(record))
{
throw new IllegalArgumentException(record.toString());
}
records.add(record);
kills += record.getKills();
outer:
for (LootTrackerItem item : record.getItems())
{
// Combine it into an existing item if one already exists
for (int idx = 0; idx < items.size(); ++idx)
{
LootTrackerItem i = items.get(idx);
if (item.getId() == i.getId())
{
items.set(idx, new LootTrackerItem(i.getId(), i.getName(), i.getQuantity() + item.getQuantity(), i.getGePrice(), i.getHaPrice(), i.isIgnored()));
continue outer;
}
}
items.add(item);
}
}
void rebuild()
@@ -245,69 +262,31 @@ class LootTrackerBox extends JPanel
*/
private void buildItems()
{
final List<LootTrackerItem> allItems = new ArrayList<>();
final List<LootTrackerItem> items = new ArrayList<>();
totalPrice = 0;
for (LootTrackerRecord record : records)
{
allItems.addAll(Arrays.asList(record.getItems()));
}
List<LootTrackerItem> items = this.items;
if (hideIgnoredItems)
{
/* If all the items in this box are ignored */
boolean hideBox = allItems.stream().allMatch(LootTrackerItem::isIgnored);
setVisible(!hideBox);
if (hideBox)
{
return;
}
items = items.stream().filter(item -> !item.isIgnored()).collect(Collectors.toList());
}
for (final LootTrackerItem entry : allItems)
boolean isHidden = items.isEmpty();
setVisible(!isHidden);
if (isHidden)
{
if (entry.isIgnored() && hideIgnoredItems)
{
continue;
}
totalPrice += priceType == LootTrackerPriceType.HIGH_ALCHEMY ? entry.getHaPrice() : entry.getGePrice();
int quantity = 0;
for (final LootTrackerItem i : items)
{
if (i.getId() == entry.getId())
{
quantity = i.getQuantity();
items.remove(i);
break;
}
}
if (quantity > 0)
{
int newQuantity = entry.getQuantity() + quantity;
long gePricePerItem = entry.getGePrice() == 0 ? 0 : (entry.getGePrice() / entry.getQuantity());
long haPricePerItem = entry.getHaPrice() == 0 ? 0 : (entry.getHaPrice() / entry.getQuantity());
items.add(new LootTrackerItem(entry.getId(), entry.getName(), newQuantity, gePricePerItem * newQuantity, haPricePerItem * newQuantity, entry.isIgnored()));
}
else
{
items.add(entry);
}
return;
}
if (priceType == LootTrackerPriceType.HIGH_ALCHEMY)
{
items.sort((i1, i2) -> Long.compare(i2.getHaPrice(), i1.getHaPrice()));
}
else
{
items.sort((i1, i2) -> Long.compare(i2.getGePrice(), i1.getGePrice()));
}
ToLongFunction<LootTrackerItem> getPrice = priceType == LootTrackerPriceType.HIGH_ALCHEMY
? LootTrackerItem::getTotalHaPrice
: LootTrackerItem::getTotalGePrice;
totalPrice = items.stream()
.mapToLong(getPrice)
.sum();
items.sort(Comparator.comparingLong(getPrice).reversed());
// Calculates how many rows need to be display to fit all items
final int rowSize = ((items.size() % ITEMS_PER_ROW == 0) ? 0 : 1) + items.size() / ITEMS_PER_ROW;
@@ -372,8 +351,8 @@ class LootTrackerBox extends JPanel
{
final String name = item.getName();
final int quantity = item.getQuantity();
final long gePrice = item.getGePrice();
final long haPrice = item.getHaPrice();
final long gePrice = item.getTotalGePrice();
final long haPrice = item.getTotalHaPrice();
final String ignoredLabel = item.isIgnored() ? " - Ignored" : "";
return "<html>" + name + " x " + quantity + ignoredLabel
+ "<br>GE: " + QuantityFormatter.quantityToStackSize(gePrice)

View File

@@ -29,19 +29,24 @@ import lombok.Getter;
import lombok.Setter;
@AllArgsConstructor
@Getter
class LootTrackerItem
{
@Getter
private final int id;
@Getter
private final String name;
@Getter
private final int quantity;
@Getter
private final long gePrice;
@Getter
private final long haPrice;
@Getter
private int quantity;
private final int gePrice;
private final int haPrice;
@Setter
private boolean ignored;
long getTotalGePrice()
{
return (long) gePrice * quantity;
}
long getTotalHaPrice()
{
return (long) haPrice * quantity;
}
}

View File

@@ -25,6 +25,7 @@
*/
package net.runelite.client.plugins.loottracker;
import static com.google.common.collect.Iterables.concat;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
@@ -35,6 +36,7 @@ import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
@@ -101,8 +103,10 @@ class LootTrackerPanel extends PluginPanel
private final JLabel groupedLootBtn = new JLabel();
private final JLabel collapseBtn = new JLabel();
// Log collection
private final List<LootTrackerRecord> records = new ArrayList<>();
// Aggregate of all kills
private final List<LootTrackerRecord> aggregateRecords = new ArrayList<>();
// Individual records for the individual kills this session
private final List<LootTrackerRecord> sessionRecords = new ArrayList<>();
private final List<LootTrackerBox> boxes = new ArrayList<>();
private final ItemManager itemManager;
@@ -330,7 +334,8 @@ class LootTrackerPanel extends PluginPanel
}
// If not in detailed view, remove all, otherwise only remove for the currently detailed title
records.removeIf(r -> r.matches(currentView));
sessionRecords.removeIf(r -> r.matches(currentView));
aggregateRecords.removeIf(r -> r.matches(currentView));
boxes.removeIf(b -> b.matches(currentView));
updateOverall();
logsContainer.removeAll();
@@ -377,7 +382,7 @@ class LootTrackerPanel extends PluginPanel
private boolean isAllCollapsed()
{
return boxes.stream()
.filter(i -> i.isCollapsed())
.filter(LootTrackerBox::isCollapsed)
.count() == boxes.size();
}
@@ -394,8 +399,9 @@ class LootTrackerPanel extends PluginPanel
void add(final String eventName, final int actorLevel, LootTrackerItem[] items)
{
final String subTitle = actorLevel > -1 ? "(lvl-" + actorLevel + ")" : "";
final LootTrackerRecord record = new LootTrackerRecord(eventName, subTitle, items);
records.add(record);
final LootTrackerRecord record = new LootTrackerRecord(eventName, subTitle, items, 1);
sessionRecords.add(record);
LootTrackerBox box = buildBox(record);
if (box != null)
{
@@ -409,7 +415,7 @@ class LootTrackerPanel extends PluginPanel
*/
void addRecords(Collection<LootTrackerRecord> recs)
{
records.addAll(recs);
aggregateRecords.addAll(recs);
rebuild();
}
@@ -466,14 +472,11 @@ class LootTrackerPanel extends PluginPanel
*/
void updateIgnoredRecords()
{
for (LootTrackerRecord r : records)
for (LootTrackerRecord record : concat(aggregateRecords, sessionRecords))
{
for (LootTrackerItem item : r.getItems())
for (LootTrackerItem item : record.getItems())
{
if (plugin.isIgnored(item.getName()) != item.isIgnored())
{
item.setIgnored(plugin.isIgnored(item.getName()));
}
item.setIgnored(plugin.isIgnored(item.getName()));
}
}
@@ -487,15 +490,25 @@ class LootTrackerPanel extends PluginPanel
{
logsContainer.removeAll();
boxes.clear();
int start = 0;
if (!groupLoot && records.size() > MAX_LOOT_BOXES)
if (groupLoot)
{
start = records.size() - MAX_LOOT_BOXES;
aggregateRecords.forEach(this::buildBox);
sessionRecords.forEach(this::buildBox);
}
for (int i = start; i < records.size(); i++)
else
{
buildBox(records.get(i));
int start = 0;
if (sessionRecords.size() > MAX_LOOT_BOXES)
{
start = sessionRecords.size() - MAX_LOOT_BOXES;
}
for (int i = start; i < sessionRecords.size(); i++)
{
buildBox(sessionRecords.get(i));
}
}
boxes.forEach(LootTrackerBox::rebuild);
updateOverall();
logsContainer.revalidate();
@@ -522,7 +535,7 @@ class LootTrackerPanel extends PluginPanel
{
if (box.matches(record))
{
box.combine(record);
box.addKill(record);
return box;
}
}
@@ -536,7 +549,7 @@ class LootTrackerPanel extends PluginPanel
// Create box
final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getSubTitle(),
hideIgnoredItems, config.priceType(), config.showPriceType(), plugin::toggleItem);
box.combine(record);
box.addKill(record);
// Create popup menu
final JPopupMenu popupMenu = new JPopupMenu();
@@ -568,7 +581,13 @@ class LootTrackerPanel extends PluginPanel
final JMenuItem reset = new JMenuItem("Reset");
reset.addActionListener(e ->
{
records.removeAll(box.getRecords());
Predicate<LootTrackerRecord> match = groupLoot
// With grouped loot, remove any record with this title
? r -> r.matches(record.getTitle())
// Otherwise remove specifically this entry
: r -> r.equals(record);
sessionRecords.removeIf(match);
aggregateRecords.removeIf(match);
boxes.remove(box);
updateOverall();
logsContainer.remove(box);
@@ -614,7 +633,7 @@ class LootTrackerPanel extends PluginPanel
long overallGe = 0;
long overallHa = 0;
for (LootTrackerRecord record : records)
for (LootTrackerRecord record : concat(aggregateRecords, sessionRecords))
{
if (!record.matches(currentView))
{
@@ -631,13 +650,13 @@ class LootTrackerPanel extends PluginPanel
continue;
}
overallGe += item.getGePrice();
overallHa += item.getHaPrice();
overallGe += item.getTotalGePrice();
overallHa += item.getTotalHaPrice();
}
if (present > 0)
{
overallKills++;
overallKills += record.getKills();
}
}

View File

@@ -90,6 +90,7 @@ import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.Text;
import net.runelite.http.api.loottracker.GameItem;
import net.runelite.http.api.loottracker.LootAggregate;
import net.runelite.http.api.loottracker.LootRecord;
import net.runelite.http.api.loottracker.LootRecordType;
import net.runelite.http.api.loottracker.LootTrackerClient;
@@ -276,13 +277,12 @@ public class LootTrackerPlugin extends Plugin
executor.submit(() ->
{
Collection<LootRecord> lootRecords;
if (!config.syncPanel())
{
return;
}
Collection<LootAggregate> lootRecords;
try
{
lootRecords = lootTrackerClient.get();
@@ -624,8 +624,8 @@ public class LootTrackerPlugin extends Plugin
{
final ItemComposition itemComposition = itemManager.getItemComposition(itemId);
final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemId;
final long gePrice = (long) itemManager.getItemPrice(realItemId) * (long) quantity;
final long haPrice = (long) Math.round(itemComposition.getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER) * (long) quantity;
final int gePrice = itemManager.getItemPrice(realItemId);
final int haPrice = Math.round(itemComposition.getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER);
final boolean ignored = ignoredItems.contains(itemComposition.getName());
return new LootTrackerItem(
@@ -651,17 +651,17 @@ public class LootTrackerPlugin extends Plugin
.collect(Collectors.toList());
}
private Collection<LootTrackerRecord> convertToLootTrackerRecord(final Collection<LootRecord> records)
private Collection<LootTrackerRecord> convertToLootTrackerRecord(final Collection<LootAggregate> records)
{
return records.stream()
.sorted(Comparator.comparing(LootRecord::getTime))
.sorted(Comparator.comparing(LootAggregate::getLast_time))
.map(record ->
{
LootTrackerItem[] drops = record.getDrops().stream().map(itemStack ->
buildLootTrackerItem(itemStack.getId(), itemStack.getQty())
).toArray(LootTrackerItem[]::new);
return new LootTrackerRecord(record.getEventId(), "", drops);
return new LootTrackerRecord(record.getEventId(), "", drops, record.getAmount());
})
.collect(Collectors.toCollection(ArrayList::new));
}

View File

@@ -32,9 +32,11 @@ class LootTrackerRecord
private final String title;
private final String subTitle;
private final LootTrackerItem[] items;
private final int kills;
/**
* Checks if this record matches specified id
*
* @param id other record id
* @return true if match is made
*/