loot tracker: batch loot submissions
This commit is contained in:
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user