loot tracker: batch loot submissions

This commit is contained in:
Adam
2019-08-18 12:26:33 -04:00
parent bd0badf7fc
commit c8ffa3994d
5 changed files with 98 additions and 27 deletions

View File

@@ -53,7 +53,7 @@ public class LootTrackerClient
private final UUID uuid; private final UUID uuid;
public void submit(LootRecord lootRecord) public void submit(Collection<LootRecord> lootRecords)
{ {
HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() HttpUrl url = RuneLiteAPI.getApiBase().newBuilder()
.addPathSegment("loottracker") .addPathSegment("loottracker")
@@ -61,7 +61,7 @@ public class LootTrackerClient
Request request = new Request.Builder() Request request = new Request.Builder()
.header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString()) .header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString())
.post(RequestBody.create(JSON, GSON.toJson(lootRecord))) .post(RequestBody.create(JSON, GSON.toJson(lootRecords)))
.url(url) .url(url)
.build(); .build();

View File

@@ -53,7 +53,7 @@ public class LootTrackerController
private AuthFilter auth; private AuthFilter auth;
@RequestMapping(method = RequestMethod.POST) @RequestMapping(method = RequestMethod.POST)
public void storeLootRecord(HttpServletRequest request, HttpServletResponse response, @RequestBody LootRecord record) throws IOException public void storeLootRecord(HttpServletRequest request, HttpServletResponse response, @RequestBody Collection<LootRecord> records) throws IOException
{ {
SessionEntry e = auth.handle(request, response); SessionEntry e = auth.handle(request, response);
if (e == null) if (e == null)
@@ -62,7 +62,7 @@ public class LootTrackerController
return; return;
} }
service.store(record, e.getUser()); service.store(records, e.getUser());
response.setStatus(HttpStatusCodes.STATUS_CODE_OK); response.setStatus(HttpStatusCodes.STATUS_CODE_OK);
} }

View File

@@ -64,7 +64,7 @@ public class LootTrackerService
// Queries for inserting kills // 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_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 (LAST_INSERT_ID(), :itemId, :itemQuantity)"; private static final String INSERT_DROP_QUERY = "INSERT INTO drops (killId, itemId, itemQuantity) VALUES (:killId, :itemId, :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,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";
@@ -89,29 +89,49 @@ public class LootTrackerService
/** /**
* Store LootRecord * Store LootRecord
* *
* @param record LootRecord to store * @param records LootRecords to store
* @param accountId runelite account id to tie data too * @param accountId runelite account id to tie data too
*/ */
public void store(LootRecord record, int accountId) public void store(Collection<LootRecord> records, int accountId)
{ {
try (Connection con = sql2o.beginTransaction()) try (Connection con = sql2o.beginTransaction())
{ {
// Kill Entry Query // Kill Entry Query
con.createQuery(INSERT_KILL_QUERY, true) Query killQuery = con.createQuery(INSERT_KILL_QUERY, true);
.addParameter("accountId", accountId)
.addParameter("type", record.getType()) for (LootRecord record : records)
.addParameter("eventId", record.getEventId()) {
.executeUpdate(); killQuery
.addParameter("accountId", accountId)
.addParameter("type", record.getType())
.addParameter("eventId", record.getEventId())
.addToBatch();
}
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); Query insertDrop = con.createQuery(INSERT_DROP_QUERY);
// Append all queries for inserting drops // Append all queries for inserting drops
for (GameItem drop : record.getDrops()) int idx = 0;
for (LootRecord record : records)
{ {
insertDrop for (GameItem drop : record.getDrops())
.addParameter("itemId", drop.getId()) {
.addParameter("itemQuantity", drop.getQty()) insertDrop
.addToBatch(); .addParameter("killId", keys[idx])
.addParameter("itemId", drop.getId())
.addParameter("itemQuantity", drop.getQty())
.addToBatch();
}
++idx;
} }
insertDrop.executeBatch(); insertDrop.executeBatch();

View File

@@ -83,10 +83,10 @@ public class LootTrackerControllerTest
lootRecord.setTime(Instant.now()); lootRecord.setTime(Instant.now());
lootRecord.setDrops(Collections.singletonList(new GameItem(4151, 1))); lootRecord.setDrops(Collections.singletonList(new GameItem(4151, 1)));
String data = RuneLiteAPI.GSON.toJson(lootRecord); String data = RuneLiteAPI.GSON.toJson(Collections.singletonList(lootRecord));
mockMvc.perform(post("/loottracker").content(data).contentType(MediaType.APPLICATION_JSON)) mockMvc.perform(post("/loottracker").content(data).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk()); .andExpect(status().isOk());
verify(lootTrackerService).store(eq(lootRecord), anyInt()); verify(lootTrackerService).store(eq(Collections.singletonList(lootRecord)), anyInt());
} }
} }

View File

@@ -34,6 +34,7 @@ import com.google.inject.Provides;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@@ -81,6 +82,7 @@ import net.runelite.client.game.ItemStack;
import net.runelite.client.game.SpriteManager; import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.task.Schedule;
import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.ClientToolbar;
import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.NavigationButton;
import net.runelite.client.util.ImageUtil; import net.runelite.client.util.ImageUtil;
@@ -167,6 +169,7 @@ public class LootTrackerPlugin extends Plugin
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private LootTrackerClient lootTrackerClient; private LootTrackerClient lootTrackerClient;
private final List<LootRecord> queuedLoots = new ArrayList<>();
private static Collection<ItemStack> stack(Collection<ItemStack> items) private static Collection<ItemStack> stack(Collection<ItemStack> items)
{ {
@@ -220,6 +223,7 @@ public class LootTrackerPlugin extends Plugin
@Subscribe @Subscribe
public void onSessionClose(SessionClose sessionClose) public void onSessionClose(SessionClose sessionClose)
{ {
submitLoot();
lootTrackerClient = null; lootTrackerClient = null;
} }
@@ -300,6 +304,7 @@ public class LootTrackerPlugin extends Plugin
@Override @Override
protected void shutDown() protected void shutDown()
{ {
submitLoot();
clientToolbar.removeNavigation(navButton); clientToolbar.removeNavigation(navButton);
lootTrackerClient = null; lootTrackerClient = null;
chestLooted = false; chestLooted = false;
@@ -324,10 +329,13 @@ 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 && config.saveLoot()) if (config.saveLoot())
{ {
LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items), Instant.now()); LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items), Instant.now());
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
} }
@@ -347,10 +355,13 @@ 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 && config.saveLoot()) if (config.saveLoot())
{ {
LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items), Instant.now()); LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items), Instant.now());
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
} }
@@ -420,10 +431,13 @@ 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 && config.saveLoot()) if (config.saveLoot())
{ {
LootRecord lootRecord = new LootRecord(eventType, LootRecordType.EVENT, toGameItems(items), Instant.now()); LootRecord lootRecord = new LootRecord(eventType, LootRecordType.EVENT, toGameItems(items), Instant.now());
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
} }
@@ -521,6 +535,40 @@ public class LootTrackerPlugin extends Plugin
} }
} }
@Schedule(
period = 5,
unit = ChronoUnit.MINUTES,
asynchronous = true
)
public void submitLootTask()
{
submitLoot();
}
private void submitLoot()
{
List<LootRecord> copy;
synchronized (queuedLoots)
{
if (queuedLoots.isEmpty())
{
return;
}
copy = new ArrayList<>(queuedLoots);
queuedLoots.clear();
}
if (lootTrackerClient == null || !config.saveLoot())
{
return;
}
log.debug("Submitting {} loot records", copy.size());
lootTrackerClient.submit(copy);
}
private void takeInventorySnapshot() private void takeInventorySnapshot()
{ {
final ItemContainer itemContainer = client.getItemContainer(InventoryID.INVENTORY); final ItemContainer itemContainer = client.getItemContainer(InventoryID.INVENTORY);
@@ -549,10 +597,13 @@ public class LootTrackerPlugin extends Plugin
final LootTrackerItem[] entries = buildEntries(stack(items)); final LootTrackerItem[] entries = buildEntries(stack(items));
SwingUtilities.invokeLater(() -> panel.add(chestType, -1, entries)); SwingUtilities.invokeLater(() -> panel.add(chestType, -1, entries));
if (lootTrackerClient != null && config.saveLoot()) if (config.saveLoot())
{ {
LootRecord lootRecord = new LootRecord(chestType, LootRecordType.EVENT, toGameItems(items), Instant.now()); LootRecord lootRecord = new LootRecord(chestType, LootRecordType.EVENT, toGameItems(items), Instant.now());
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
inventorySnapshot = null; inventorySnapshot = null;