Revert to old project structure
Signed-off-by: PKLite <stonewall@pklite.xyz>
This commit is contained in:
@@ -29,5 +29,6 @@ public enum LootRecordType
|
|||||||
NPC,
|
NPC,
|
||||||
PLAYER,
|
PLAYER,
|
||||||
EVENT,
|
EVENT,
|
||||||
|
DEATH,
|
||||||
UNKNOWN
|
UNKNOWN
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -229,7 +229,7 @@ class LootTrackerBox extends JPanel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quantity > 0)
|
if (quantity != 0)
|
||||||
{
|
{
|
||||||
int newQuantity = entry.getQuantity() + quantity;
|
int newQuantity = entry.getQuantity() + quantity;
|
||||||
long pricePerItem = entry.getPrice() == 0 ? 0 : (entry.getPrice() / entry.getQuantity());
|
long pricePerItem = entry.getPrice() == 0 ? 0 : (entry.getPrice() / entry.getQuantity());
|
||||||
@@ -263,7 +263,7 @@ class LootTrackerBox extends JPanel
|
|||||||
imageLabel.setVerticalAlignment(SwingConstants.CENTER);
|
imageLabel.setVerticalAlignment(SwingConstants.CENTER);
|
||||||
imageLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
imageLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||||
|
|
||||||
AsyncBufferedImage itemImage = itemManager.getImage(item.getId(), item.getQuantity(), item.getQuantity() > 1);
|
AsyncBufferedImage itemImage = itemManager.getImage(item.getId(), Math.abs(item.getQuantity()), Math.abs(item.getQuantity()) > 1);
|
||||||
|
|
||||||
if (item.isIgnored())
|
if (item.isIgnored())
|
||||||
{
|
{
|
||||||
|
|||||||
+12
@@ -80,4 +80,16 @@ public interface LootTrackerConfig extends Config
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConfigItem(
|
||||||
|
keyName = "localPersistence",
|
||||||
|
name = "Local Record Persistence",
|
||||||
|
description = "Stores/syncs loot records locally in the JSON format. Note: records will not be saved locally" +
|
||||||
|
" if they are successfully saved online. "
|
||||||
|
)
|
||||||
|
default boolean localPersistence()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+202
-11
@@ -27,11 +27,22 @@ package net.runelite.client.plugins.loottracker;
|
|||||||
|
|
||||||
import com.google.common.collect.HashMultiset;
|
import com.google.common.collect.HashMultiset;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Multiset;
|
import com.google.common.collect.Multiset;
|
||||||
import com.google.common.collect.Multisets;
|
import com.google.common.collect.Multisets;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonStreamParser;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.inject.Provides;
|
import com.google.inject.Provides;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -58,12 +69,16 @@ import net.runelite.api.ItemContainer;
|
|||||||
import net.runelite.api.NPC;
|
import net.runelite.api.NPC;
|
||||||
import net.runelite.api.Player;
|
import net.runelite.api.Player;
|
||||||
import net.runelite.api.SpriteID;
|
import net.runelite.api.SpriteID;
|
||||||
|
import net.runelite.api.Varbits;
|
||||||
|
import net.runelite.api.WorldType;
|
||||||
import net.runelite.api.coords.WorldPoint;
|
import net.runelite.api.coords.WorldPoint;
|
||||||
import net.runelite.api.events.ChatMessage;
|
import net.runelite.api.events.ChatMessage;
|
||||||
import net.runelite.api.events.ConfigChanged;
|
import net.runelite.api.events.ConfigChanged;
|
||||||
import net.runelite.api.events.ItemContainerChanged;
|
import net.runelite.api.events.ItemContainerChanged;
|
||||||
|
import net.runelite.api.events.LocalPlayerDeath;
|
||||||
import net.runelite.api.events.WidgetLoaded;
|
import net.runelite.api.events.WidgetLoaded;
|
||||||
import net.runelite.api.widgets.WidgetID;
|
import net.runelite.api.widgets.WidgetID;
|
||||||
|
import net.runelite.client.RuneLite;
|
||||||
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.callback.ClientThread;
|
||||||
@@ -87,6 +102,7 @@ import net.runelite.client.ui.NavigationButton;
|
|||||||
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.client.util.Text;
|
import net.runelite.client.util.Text;
|
||||||
|
import net.runelite.http.api.RuneLiteAPI;
|
||||||
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 net.runelite.http.api.loottracker.LootRecordType;
|
||||||
@@ -116,6 +132,17 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
11573, "Crystal Chest"
|
11573, "Crystal Chest"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Player deaths
|
||||||
|
private static final String PLAYER_DEATH_MESSAGE = "Oh dear, you are dead!";
|
||||||
|
private static final File LOOT_RECORDS_FILE = new File(RuneLite.RUNELITE_DIR, "lootRecords.json");
|
||||||
|
private static final Set<Integer> RESPAWN_REGIONS = ImmutableSet.of(
|
||||||
|
12850, // Lumbridge
|
||||||
|
11828, // Falador
|
||||||
|
12342, // Edgeville
|
||||||
|
11062 // Camelot
|
||||||
|
);
|
||||||
|
private boolean pvpDeath = false;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ClientToolbar clientToolbar;
|
private ClientToolbar clientToolbar;
|
||||||
|
|
||||||
@@ -151,8 +178,12 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
|
|
||||||
private Multiset<Integer> inventorySnapshot;
|
private Multiset<Integer> inventorySnapshot;
|
||||||
|
|
||||||
|
|
||||||
@Getter(AccessLevel.PACKAGE)
|
@Getter(AccessLevel.PACKAGE)
|
||||||
private LootTrackerClient lootTrackerClient;
|
private LootTrackerClient lootTrackerClient;
|
||||||
|
private BufferedReader bufferedReader;
|
||||||
|
private JsonStreamParser jsonStreamParser;
|
||||||
|
private Collection<LootRecord> lootRecords = new ArrayList<>();
|
||||||
|
|
||||||
private static Collection<ItemStack> stack(Collection<ItemStack> items)
|
private static Collection<ItemStack> stack(Collection<ItemStack> items)
|
||||||
{
|
{
|
||||||
@@ -209,6 +240,16 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
lootTrackerClient = null;
|
lootTrackerClient = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe
|
||||||
|
public void onLocalPlayerDeath(LocalPlayerDeath event)
|
||||||
|
{
|
||||||
|
if (client.getVar(Varbits.IN_WILDERNESS) == 1 || WorldType.isPvpWorld(client.getWorldType()))
|
||||||
|
{
|
||||||
|
deathInventorySnapshot();
|
||||||
|
pvpDeath = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onConfigChanged(ConfigChanged event)
|
public void onConfigChanged(ConfigChanged event)
|
||||||
{
|
{
|
||||||
@@ -238,9 +279,10 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
clientToolbar.addNavigation(navButton);
|
clientToolbar.addNavigation(navButton);
|
||||||
|
|
||||||
AccountSession accountSession = sessionManager.getAccountSession();
|
AccountSession accountSession = sessionManager.getAccountSession();
|
||||||
if (accountSession != null)
|
LOOT_RECORDS_FILE.createNewFile();
|
||||||
|
bufferedReader = Files.newBufferedReader(LOOT_RECORDS_FILE.toPath());
|
||||||
|
if (accountSession != null || config.localPersistence())
|
||||||
{
|
{
|
||||||
lootTrackerClient = new LootTrackerClient(accountSession.getUuid());
|
|
||||||
|
|
||||||
clientThread.invokeLater(() ->
|
clientThread.invokeLater(() ->
|
||||||
{
|
{
|
||||||
@@ -253,13 +295,10 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
|
|
||||||
executor.submit(() ->
|
executor.submit(() ->
|
||||||
{
|
{
|
||||||
Collection<LootRecord> lootRecords;
|
|
||||||
|
|
||||||
if (!config.syncPanel())
|
if (config.syncPanel() && lootTrackerClient != null)
|
||||||
{
|
{
|
||||||
return;
|
lootTrackerClient = new LootTrackerClient(accountSession.getUuid());
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
lootRecords = lootTrackerClient.get();
|
lootRecords = lootTrackerClient.get();
|
||||||
@@ -269,12 +308,28 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
log.debug("Unable to look up loot", e);
|
log.debug("Unable to look up loot", e);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
log.debug("Loaded {} remote data entries", lootRecords.size());
|
||||||
|
}
|
||||||
|
|
||||||
log.debug("Loaded {} data entries", lootRecords.size());
|
if (config.localPersistence() )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
lootRecords.addAll(RuneLiteAPI.GSON.fromJson(new FileReader(LOOT_RECORDS_FILE),
|
||||||
|
new TypeToken<ArrayList<LootRecord>>()
|
||||||
|
{ }.getType()));
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Collection<LootRecord> finalLootRecords = lootRecords;
|
||||||
clientThread.invokeLater(() ->
|
clientThread.invokeLater(() ->
|
||||||
{
|
{
|
||||||
Collection<LootTrackerRecord> records = convertToLootTrackerRecord(lootRecords);
|
Collection<LootTrackerRecord> records = convertToLootTrackerRecord(finalLootRecords);
|
||||||
SwingUtilities.invokeLater(() -> panel.addRecords(records));
|
SwingUtilities.invokeLater(() -> panel.addRecords(records));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -299,12 +354,16 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
final int combat = npc.getCombatLevel();
|
final int combat = npc.getCombatLevel();
|
||||||
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));
|
||||||
|
LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items), Instant.now());
|
||||||
|
|
||||||
if (lootTrackerClient != null && config.saveLoot())
|
if (lootTrackerClient != null && config.saveLoot())
|
||||||
{
|
{
|
||||||
LootRecord lootRecord = new LootRecord(name, LootRecordType.NPC, toGameItems(items), Instant.now());
|
|
||||||
lootTrackerClient.submit(lootRecord);
|
lootTrackerClient.submit(lootRecord);
|
||||||
}
|
}
|
||||||
|
if (config.localPersistence())
|
||||||
|
{
|
||||||
|
saveLocalLootRecord(lootRecord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@@ -316,12 +375,16 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
final int combat = player.getCombatLevel();
|
final int combat = player.getCombatLevel();
|
||||||
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));
|
||||||
|
LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items), Instant.now());
|
||||||
|
|
||||||
if (lootTrackerClient != null && config.saveLoot())
|
if (lootTrackerClient != null && config.saveLoot())
|
||||||
{
|
{
|
||||||
LootRecord lootRecord = new LootRecord(name, LootRecordType.PLAYER, toGameItems(items), Instant.now());
|
|
||||||
lootTrackerClient.submit(lootRecord);
|
lootTrackerClient.submit(lootRecord);
|
||||||
}
|
}
|
||||||
|
if (config.localPersistence() && lootTrackerClient == null)
|
||||||
|
{
|
||||||
|
saveLocalLootRecord(lootRecord);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
@@ -416,6 +479,34 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
|
|
||||||
final String message = event.getMessage();
|
final String message = event.getMessage();
|
||||||
|
|
||||||
|
if (message.equals(PLAYER_DEATH_MESSAGE))
|
||||||
|
{
|
||||||
|
ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY);
|
||||||
|
if (inventorySnapshot != null)
|
||||||
|
{
|
||||||
|
Multiset<Integer> currentInventory = HashMultiset.create();
|
||||||
|
if (inventory != null)
|
||||||
|
{
|
||||||
|
Arrays.stream(client.getItemContainer(InventoryID.INVENTORY).getItems())
|
||||||
|
.forEach(item -> currentInventory.add(item.getId(), item.getQuantity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Multiset<Integer> diff = Multisets.difference(inventorySnapshot, currentInventory);
|
||||||
|
|
||||||
|
log.info(inventorySnapshot.toString());
|
||||||
|
log.info(currentInventory.toString());
|
||||||
|
log.info(diff.toString());
|
||||||
|
|
||||||
|
List<ItemStack> itemsLost = diff.entrySet().stream()
|
||||||
|
.map(e -> new ItemStack(e.getElement(), (-1 * e.getCount()), client.getLocalPlayer().getLocalLocation()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final LootTrackerItem[] entries = buildEntries(stack(itemsLost));
|
||||||
|
SwingUtilities.invokeLater(() -> panel.add("Death: " + client.getLocalPlayer().getName(),
|
||||||
|
client.getLocalPlayer().getCombatLevel(), entries));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (message.equals(CHEST_LOOTED_MESSAGE))
|
if (message.equals(CHEST_LOOTED_MESSAGE))
|
||||||
{
|
{
|
||||||
final int regionID = client.getLocalPlayer().getWorldLocation().getRegionID();
|
final int regionID = client.getLocalPlayer().getWorldLocation().getRegionID();
|
||||||
@@ -470,6 +561,54 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
@Subscribe
|
@Subscribe
|
||||||
public void onItemContainerChanged(ItemContainerChanged event)
|
public void onItemContainerChanged(ItemContainerChanged event)
|
||||||
{
|
{
|
||||||
|
final ItemContainer itemContainer = event.getItemContainer();
|
||||||
|
|
||||||
|
if (pvpDeath && RESPAWN_REGIONS.contains(client.getLocalPlayer().getWorldLocation().getRegionID()))
|
||||||
|
{
|
||||||
|
Multiset snapshot = HashMultiset.create();
|
||||||
|
snapshot = inventorySnapshot;
|
||||||
|
deathInventorySnapshot();
|
||||||
|
if (inventorySnapshot != snapshot)
|
||||||
|
{
|
||||||
|
inventorySnapshot = snapshot;
|
||||||
|
ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY);
|
||||||
|
if (inventorySnapshot != null)
|
||||||
|
{
|
||||||
|
Multiset<Integer> currentInventory = HashMultiset.create();
|
||||||
|
if (inventory != null)
|
||||||
|
{
|
||||||
|
Arrays.stream(client.getItemContainer(InventoryID.INVENTORY).getItems())
|
||||||
|
.forEach(item -> currentInventory.add(item.getId(), item.getQuantity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Multiset<Integer> diff = Multisets.difference(inventorySnapshot, currentInventory);
|
||||||
|
|
||||||
|
List<ItemStack> itemsLost = diff.entrySet().stream()
|
||||||
|
.map(e -> new ItemStack(e.getElement(), (-1 * e.getCount()), client.getLocalPlayer().getLocalLocation()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final LootTrackerItem[] entries = buildEntries(stack(itemsLost));
|
||||||
|
String name = "Death: " + client.getLocalPlayer().getName();
|
||||||
|
SwingUtilities.invokeLater(() -> panel.add(name,
|
||||||
|
client.getLocalPlayer().getCombatLevel(), entries));
|
||||||
|
LootRecord lootRecord = new LootRecord(name, LootRecordType.DEATH, toGameItems(itemsLost),
|
||||||
|
Instant.now());
|
||||||
|
|
||||||
|
if (lootTrackerClient != null && config.saveLoot())
|
||||||
|
{
|
||||||
|
lootTrackerClient.submit(lootRecord);
|
||||||
|
}
|
||||||
|
if (config.localPersistence() && lootTrackerClient == null)
|
||||||
|
{
|
||||||
|
saveLocalLootRecord(lootRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
pvpDeath = false;
|
||||||
|
inventorySnapshot = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
if (eventType != null && (CHEST_EVENT_TYPES.containsValue(eventType) || HERBIBOR_EVENT.equals(eventType)))
|
if (eventType != null && (CHEST_EVENT_TYPES.containsValue(eventType) || HERBIBOR_EVENT.equals(eventType)))
|
||||||
{
|
{
|
||||||
if (event.getItemContainer() != client.getItemContainer(InventoryID.INVENTORY))
|
if (event.getItemContainer() != client.getItemContainer(InventoryID.INVENTORY))
|
||||||
@@ -482,6 +621,58 @@ public class LootTrackerPlugin extends Plugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void saveLocalLootRecord(LootRecord lootRecord)
|
||||||
|
{
|
||||||
|
lootRecords.add(lootRecord);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BufferedWriter bufferedWriter = Files.newBufferedWriter(LOOT_RECORDS_FILE.toPath());
|
||||||
|
|
||||||
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
|
gsonBuilder.setPrettyPrinting();
|
||||||
|
Gson gson = gsonBuilder.create();
|
||||||
|
bufferedWriter.append(gson.toJson(lootRecords));
|
||||||
|
bufferedWriter.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
if (e instanceof FileNotFoundException)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
LOOT_RECORDS_FILE.createNewFile();
|
||||||
|
}
|
||||||
|
catch (IOException ex)
|
||||||
|
{
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a snapshot of the local player's inventory and equipment right before respawn.
|
||||||
|
*/
|
||||||
|
private void deathInventorySnapshot()
|
||||||
|
{
|
||||||
|
final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY);
|
||||||
|
final ItemContainer equipment = client.getItemContainer(InventoryID.EQUIPMENT);
|
||||||
|
inventorySnapshot = HashMultiset.create();
|
||||||
|
if (inventory != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
Arrays.stream(inventory.getItems())
|
||||||
|
.forEach(item -> inventorySnapshot.add(item.getId(), item.getQuantity()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (equipment != null)
|
||||||
|
{
|
||||||
|
Arrays.stream(equipment.getItems())
|
||||||
|
.forEach(item -> inventorySnapshot.add(item.getId(), item.getQuantity()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void takeInventorySnapshot()
|
private void takeInventorySnapshot()
|
||||||
{
|
{
|
||||||
final ItemContainer itemContainer = client.getItemContainer(InventoryID.INVENTORY);
|
final ItemContainer itemContainer = client.getItemContainer(InventoryID.INVENTORY);
|
||||||
|
|||||||
+2
@@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package net.runelite.client.plugins.loottracker;
|
package net.runelite.client.plugins.loottracker;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
@Value
|
@Value
|
||||||
@@ -31,6 +32,7 @@ class LootTrackerRecord
|
|||||||
{
|
{
|
||||||
private final String title;
|
private final String title;
|
||||||
private final String subTitle;
|
private final String subTitle;
|
||||||
|
@SerializedName("item_records")
|
||||||
private final LootTrackerItem[] items;
|
private final LootTrackerItem[] items;
|
||||||
private final long timestamp;
|
private final long timestamp;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user