diff --git a/http-api/src/main/java/net/runelite/http/api/loottracker/LootAggregate.java b/http-api/src/main/java/net/runelite/http/api/loottracker/LootAggregate.java new file mode 100644 index 0000000000..a5437226cc --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/loottracker/LootAggregate.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020, 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.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 drops; + private Instant first_time; + private Instant last_time; + private int amount; +} 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 71eb151364..793b661fdd 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 @@ -81,7 +81,7 @@ public class LootTrackerClient }); } - public Collection get() throws IOException + public Collection 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>() + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), new TypeToken>() { }.getType()); } diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootResult.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootResult.java index 5bfd7afee0..4cb3375154 100644 --- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootResult.java +++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootResult.java @@ -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; } 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 1033d62187..ac4da593c6 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 @@ -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 getLootRecords(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "count", defaultValue = "1024") int count, @RequestParam(value = "start", defaultValue = "0") int start) throws IOException + public Collection 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) 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 8f97ce144d..f7f9ac5004 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 @@ -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 get(int accountId, int limit, int offset) + public Collection get(int accountId, int limit, int offset) { List lootResults; @@ -153,7 +141,7 @@ public class LootTrackerService } LootResult current = null; - List lootRecords = new ArrayList<>(); + List lootRecords = new ArrayList<>(); List 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(); } } 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 24e521d3be..4bfb960917 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 @@ -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 records = new ArrayList<>(); + private final List 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 allItems = new ArrayList<>(); - final List items = new ArrayList<>(); totalPrice = 0; - for (LootTrackerRecord record : records) - { - allItems.addAll(Arrays.asList(record.getItems())); - } - + List 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 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 "" + name + " x " + quantity + ignoredLabel + "
GE: " + QuantityFormatter.quantityToStackSize(gePrice) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java index 81aedb3a4a..eb969bbac9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java @@ -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; + } } 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 519559807a..22ce4561fa 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 @@ -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 records = new ArrayList<>(); + // Aggregate of all kills + private final List aggregateRecords = new ArrayList<>(); + // Individual records for the individual kills this session + private final List sessionRecords = new ArrayList<>(); private final List 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 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 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(); } } 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 8a15bad789..adb5217a4e 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 @@ -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 lootRecords; - if (!config.syncPanel()) { return; } + Collection 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 convertToLootTrackerRecord(final Collection records) + private Collection convertToLootTrackerRecord(final Collection 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)); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerRecord.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerRecord.java index da69caff7d..502401a968 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerRecord.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerRecord.java @@ -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 */