Merge remote-tracking branch 'runelite/master' into 2308-merge

This commit is contained in:
Owain van Brakel
2019-08-23 02:31:44 +02:00
30 changed files with 416 additions and 123 deletions

View File

@@ -53,13 +53,13 @@ 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")
.build(); .build();
RequestBody body = RequestBody.Companion.create(GSON.toJson(lootRecord), JSON); RequestBody body = RequestBody.Companion.create(GSON.toJson(lootRecords), JSON);
Request request = new Request.Builder() Request request = new Request.Builder()
.header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString()) .header(RuneLiteAPI.RUNELITE_AUTH, uuid.toString())
.post(body) .post(body)

View File

@@ -262,6 +262,8 @@ public class AccountService
return; return;
} }
auth.invalidate(session.getUuid());
try (Connection con = sql2o.open()) try (Connection con = sql2o.open())
{ {
con.createQuery("delete from sessions where uuid = :uuid") con.createQuery("delete from sessions where uuid = :uuid")

View File

@@ -24,14 +24,18 @@
*/ */
package net.runelite.http.service.account; package net.runelite.http.service.account;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalNotification;
import java.io.IOException; import java.io.IOException;
import net.runelite.http.service.account.beans.SessionEntry;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.time.Instant; import java.time.Instant;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.RuneLiteAPI;
import net.runelite.http.service.account.beans.SessionEntry;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -43,6 +47,12 @@ public class AuthFilter
{ {
private final Sql2o sql2o; private final Sql2o sql2o;
private final Cache<UUID, SessionEntry> sessionCache = CacheBuilder.newBuilder()
.maximumSize(10000L)
.expireAfterAccess(30, TimeUnit.MINUTES)
.removalListener(this::removalListener)
.build();
@Autowired @Autowired
public AuthFilter(@Qualifier("Runelite SQL2O") Sql2o sql2o) public AuthFilter(@Qualifier("Runelite SQL2O") Sql2o sql2o)
{ {
@@ -59,30 +69,48 @@ public class AuthFilter
} }
UUID uuid = UUID.fromString(runeliteAuth); UUID uuid = UUID.fromString(runeliteAuth);
SessionEntry sessionEntry = sessionCache.getIfPresent(uuid);
if (sessionEntry != null)
{
return sessionEntry;
}
try (Connection con = sql2o.open()) try (Connection con = sql2o.open())
{ {
SessionEntry sessionEntry = con.createQuery("select user, uuid, created from sessions where uuid = :uuid") sessionEntry = con.createQuery("select user, uuid, created, last_used as lastUsed from sessions where uuid = :uuid")
.addParameter("uuid", uuid.toString()) .addParameter("uuid", uuid.toString())
.executeAndFetchFirst(SessionEntry.class); .executeAndFetchFirst(SessionEntry.class);
}
if (sessionEntry == null) if (sessionEntry == null)
{ {
response.sendError(401, "Access denied"); response.sendError(401, "Access denied");
return null; return null;
} }
Instant now = Instant.now(); sessionCache.put(uuid, sessionEntry);
return sessionEntry;
}
private void removalListener(RemovalNotification<UUID, SessionEntry> notification)
{
UUID uuid = notification.getKey();
Instant now = Instant.now();
try (Connection con = sql2o.open())
{
con.createQuery("update sessions set last_used = :last_used where uuid = :uuid") con.createQuery("update sessions set last_used = :last_used where uuid = :uuid")
.addParameter("last_used", Timestamp.from(now)) .addParameter("last_used", Timestamp.from(now))
.addParameter("uuid", uuid.toString()) .addParameter("uuid", uuid.toString())
.executeUpdate(); .executeUpdate();
sessionEntry.setLastUsed(now);
return sessionEntry;
} }
} }
public void invalidate(UUID uuid)
{
// If we ever run multiple services, may need to publish something here to invalidate...
sessionCache.invalidate(uuid);
}
} }

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

@@ -49,6 +49,7 @@ public interface Actor extends Entity
* *
* @return the name * @return the name
*/ */
@Nullable
String getName(); String getName();
/** /**

View File

@@ -342,6 +342,7 @@ public interface Client extends GameShell
* *
* @return the logged in player * @return the logged in player
*/ */
@Nullable
Player getLocalPlayer(); Player getLocalPlayer();
/** /**
@@ -422,6 +423,7 @@ public interface Client extends GameShell
* *
* @return the selected tile * @return the selected tile
*/ */
@Nullable
Tile getSelectedSceneTile(); Tile getSelectedSceneTile();
/** /**
@@ -436,6 +438,7 @@ public interface Client extends GameShell
* *
* @return the dragged widget, null if not dragging any widget * @return the dragged widget, null if not dragging any widget
*/ */
@Nullable
Widget getDraggedWidget(); Widget getDraggedWidget();
/** /**
@@ -446,6 +449,7 @@ public interface Client extends GameShell
* *
* @return the dragged on widget, null if not dragging any widget * @return the dragged on widget, null if not dragging any widget
*/ */
@Nullable
Widget getDraggedOnWidget(); Widget getDraggedOnWidget();
/** /**

View File

@@ -290,9 +290,9 @@ public class ChatboxItemSearch extends ChatboxTextInput
for (int i = 0; i < client.getItemCount() && results.size() < MAX_RESULTS; i++) for (int i = 0; i < client.getItemCount() && results.size() < MAX_RESULTS; i++)
{ {
ItemDefinition itemComposition = itemManager.getItemDefinition(itemManager.canonicalize(i)); ItemDefinition itemComposition = itemManager.getItemDefinition(itemManager.canonicalize(i));
String name = itemComposition.getName(); String name = itemComposition.getName().toLowerCase();
// The client assigns "null" to item names of items it doesn't know about // The client assigns "null" to item names of items it doesn't know about
if (!name.equals("null") && name.toLowerCase().contains(search)) if (!name.equals("null") && name.contains(search))
{ {
// This may already be in the map due to canonicalize mapping the item to something we've already seen // This may already be in the map due to canonicalize mapping the item to something we've already seen
results.putIfAbsent(itemComposition.getId(), itemComposition); results.putIfAbsent(itemComposition.getId(), itemComposition);

View File

@@ -322,7 +322,7 @@ public class CrypticClue extends ClueScroll implements TextClueScroll, NpcClueSc
new CrypticClue("More resources than I can handle, but in a very dangerous area. Can't wait to strike gold!", new WorldPoint(3183, 3941, 0), "Dig between the three gold ores in the Wilderness Resource Area."), new CrypticClue("More resources than I can handle, but in a very dangerous area. Can't wait to strike gold!", new WorldPoint(3183, 3941, 0), "Dig between the three gold ores in the Wilderness Resource Area."),
new CrypticClue("Observing someone in a swamp, under the telescope lies treasure.", new WorldPoint(2221, 3091, 0), "Dig next to the telescope on Broken Handz's island in the poison wastes. (Accessible only through fairy ring DLR)"), new CrypticClue("Observing someone in a swamp, under the telescope lies treasure.", new WorldPoint(2221, 3091, 0), "Dig next to the telescope on Broken Handz's island in the poison wastes. (Accessible only through fairy ring DLR)"),
new CrypticClue("A general who sets a 'shining' example.", "General Hining", new WorldPoint(2186, 3148, 0), "Talk to General Hining in Tyras Camp."), new CrypticClue("A general who sets a 'shining' example.", "General Hining", new WorldPoint(2186, 3148, 0), "Talk to General Hining in Tyras Camp."),
new CrypticClue("Has no one told you it is rude to ask a lady her age?", "Lady Tangwen Trahaearn", new WorldPoint(3280, 6042, 0), "Talk to Lady Tangwen Trahaearn, SSE Prifddinas by the teleporter."), new CrypticClue("Has no one told you it is rude to ask a lady her age?", "Mawrth", new WorldPoint(2333, 3165, 0), "Talk to Mawrth in Lletya."),
new CrypticClue("Elvish onions.", new WorldPoint(3303, 6092, 0), "Dig in the onion patch east of the Prifddinas allotments.") new CrypticClue("Elvish onions.", new WorldPoint(3303, 6092, 0), "Dig in the onion patch east of the Prifddinas allotments.")
); );

View File

@@ -158,13 +158,13 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll
new SkillChallengeClue("Complete a lap of Rellekka's Rooftop Agility Course", "complete a lap of the rellekka rooftop agility course whilst sporting the finest amount of grace.", true, new SkillChallengeClue("Complete a lap of Rellekka's Rooftop Agility Course", "complete a lap of the rellekka rooftop agility course whilst sporting the finest amount of grace.", true,
all("A full Graceful set", all("A full Graceful set",
any("", item(ItemID.GRACEFUL_HOOD), item(ItemID.GRACEFUL_HOOD_11851), item(ItemID.GRACEFUL_HOOD_13579), item(ItemID.GRACEFUL_HOOD_13580), item(ItemID.GRACEFUL_HOOD_13591), item(ItemID.GRACEFUL_HOOD_13592), item(ItemID.GRACEFUL_HOOD_13603), item(ItemID.GRACEFUL_HOOD_13604), item(ItemID.GRACEFUL_HOOD_13615), item(ItemID.GRACEFUL_HOOD_13616), item(ItemID.GRACEFUL_HOOD_13627), item(ItemID.GRACEFUL_HOOD_13628), item(ItemID.GRACEFUL_HOOD_13667), item(ItemID.GRACEFUL_HOOD_13668), item(ItemID.GRACEFUL_HOOD_21061), item(ItemID.GRACEFUL_HOOD_21063)), any("", item(ItemID.GRACEFUL_HOOD), item(ItemID.GRACEFUL_HOOD_11851), item(ItemID.GRACEFUL_HOOD_13579), item(ItemID.GRACEFUL_HOOD_13580), item(ItemID.GRACEFUL_HOOD_13591), item(ItemID.GRACEFUL_HOOD_13592), item(ItemID.GRACEFUL_HOOD_13603), item(ItemID.GRACEFUL_HOOD_13604), item(ItemID.GRACEFUL_HOOD_13615), item(ItemID.GRACEFUL_HOOD_13616), item(ItemID.GRACEFUL_HOOD_13627), item(ItemID.GRACEFUL_HOOD_13628), item(ItemID.GRACEFUL_HOOD_13667), item(ItemID.GRACEFUL_HOOD_13668), item(ItemID.GRACEFUL_HOOD_21061), item(ItemID.GRACEFUL_HOOD_21063)),
any("", item(ItemID.GRACEFUL_CAPE), item(ItemID.GRACEFUL_CAPE_11853), item(ItemID.GRACEFUL_CAPE_13581), item(ItemID.GRACEFUL_CAPE_13582), item(ItemID.GRACEFUL_CAPE_13593), item(ItemID.GRACEFUL_CAPE_13594), item(ItemID.GRACEFUL_CAPE_13605), item(ItemID.GRACEFUL_CAPE_13606), item(ItemID.GRACEFUL_CAPE_13617), item(ItemID.GRACEFUL_CAPE_13618), item(ItemID.GRACEFUL_CAPE_13629), item(ItemID.GRACEFUL_CAPE_13630), item(ItemID.GRACEFUL_CAPE_13669), item(ItemID.GRACEFUL_CAPE_13670), item(ItemID.GRACEFUL_CAPE_21064), item(ItemID.GRACEFUL_CAPE_21066)), any("", item(ItemID.GRACEFUL_CAPE), item(ItemID.GRACEFUL_CAPE_11853), item(ItemID.GRACEFUL_CAPE_13581), item(ItemID.GRACEFUL_CAPE_13582), item(ItemID.GRACEFUL_CAPE_13593), item(ItemID.GRACEFUL_CAPE_13594), item(ItemID.GRACEFUL_CAPE_13605), item(ItemID.GRACEFUL_CAPE_13606), item(ItemID.GRACEFUL_CAPE_13617), item(ItemID.GRACEFUL_CAPE_13618), item(ItemID.GRACEFUL_CAPE_13629), item(ItemID.GRACEFUL_CAPE_13630), item(ItemID.GRACEFUL_CAPE_13669), item(ItemID.GRACEFUL_CAPE_13670), item(ItemID.GRACEFUL_CAPE_21064), item(ItemID.GRACEFUL_CAPE_21066), item(ItemID.AGILITY_CAPE), item(ItemID.AGILITY_CAPET), item(ItemID.MAX_CAPE)),
any("", item(ItemID.GRACEFUL_TOP), item(ItemID.GRACEFUL_TOP_11855), item(ItemID.GRACEFUL_TOP_13583), item(ItemID.GRACEFUL_TOP_13584), item(ItemID.GRACEFUL_TOP_13595), item(ItemID.GRACEFUL_TOP_13596), item(ItemID.GRACEFUL_TOP_13607), item(ItemID.GRACEFUL_TOP_13608), item(ItemID.GRACEFUL_TOP_13619), item(ItemID.GRACEFUL_TOP_13620), item(ItemID.GRACEFUL_TOP_13631), item(ItemID.GRACEFUL_TOP_13632), item(ItemID.GRACEFUL_TOP_13671), item(ItemID.GRACEFUL_TOP_13672), item(ItemID.GRACEFUL_TOP_21067), item(ItemID.GRACEFUL_TOP_21069)), any("", item(ItemID.GRACEFUL_TOP), item(ItemID.GRACEFUL_TOP_11855), item(ItemID.GRACEFUL_TOP_13583), item(ItemID.GRACEFUL_TOP_13584), item(ItemID.GRACEFUL_TOP_13595), item(ItemID.GRACEFUL_TOP_13596), item(ItemID.GRACEFUL_TOP_13607), item(ItemID.GRACEFUL_TOP_13608), item(ItemID.GRACEFUL_TOP_13619), item(ItemID.GRACEFUL_TOP_13620), item(ItemID.GRACEFUL_TOP_13631), item(ItemID.GRACEFUL_TOP_13632), item(ItemID.GRACEFUL_TOP_13671), item(ItemID.GRACEFUL_TOP_13672), item(ItemID.GRACEFUL_TOP_21067), item(ItemID.GRACEFUL_TOP_21069)),
any("", item(ItemID.GRACEFUL_LEGS), item(ItemID.GRACEFUL_LEGS_11857), item(ItemID.GRACEFUL_LEGS_13585), item(ItemID.GRACEFUL_LEGS_13586), item(ItemID.GRACEFUL_LEGS_13597), item(ItemID.GRACEFUL_LEGS_13598), item(ItemID.GRACEFUL_LEGS_13609), item(ItemID.GRACEFUL_LEGS_13610), item(ItemID.GRACEFUL_LEGS_13621), item(ItemID.GRACEFUL_LEGS_13622), item(ItemID.GRACEFUL_LEGS_13633), item(ItemID.GRACEFUL_LEGS_13634), item(ItemID.GRACEFUL_LEGS_13673), item(ItemID.GRACEFUL_LEGS_13674), item(ItemID.GRACEFUL_LEGS_21070), item(ItemID.GRACEFUL_LEGS_21072)), any("", item(ItemID.GRACEFUL_LEGS), item(ItemID.GRACEFUL_LEGS_11857), item(ItemID.GRACEFUL_LEGS_13585), item(ItemID.GRACEFUL_LEGS_13586), item(ItemID.GRACEFUL_LEGS_13597), item(ItemID.GRACEFUL_LEGS_13598), item(ItemID.GRACEFUL_LEGS_13609), item(ItemID.GRACEFUL_LEGS_13610), item(ItemID.GRACEFUL_LEGS_13621), item(ItemID.GRACEFUL_LEGS_13622), item(ItemID.GRACEFUL_LEGS_13633), item(ItemID.GRACEFUL_LEGS_13634), item(ItemID.GRACEFUL_LEGS_13673), item(ItemID.GRACEFUL_LEGS_13674), item(ItemID.GRACEFUL_LEGS_21070), item(ItemID.GRACEFUL_LEGS_21072)),
any("", item(ItemID.GRACEFUL_GLOVES), item(ItemID.GRACEFUL_GLOVES_11859), item(ItemID.GRACEFUL_GLOVES_13587), item(ItemID.GRACEFUL_GLOVES_13588), item(ItemID.GRACEFUL_GLOVES_13599), item(ItemID.GRACEFUL_GLOVES_13600), item(ItemID.GRACEFUL_GLOVES_13611), item(ItemID.GRACEFUL_GLOVES_13612), item(ItemID.GRACEFUL_GLOVES_13623), item(ItemID.GRACEFUL_GLOVES_13624), item(ItemID.GRACEFUL_GLOVES_13635), item(ItemID.GRACEFUL_GLOVES_13636), item(ItemID.GRACEFUL_GLOVES_13675), item(ItemID.GRACEFUL_GLOVES_13676), item(ItemID.GRACEFUL_GLOVES_21073), item(ItemID.GRACEFUL_GLOVES_21075)), any("", item(ItemID.GRACEFUL_GLOVES), item(ItemID.GRACEFUL_GLOVES_11859), item(ItemID.GRACEFUL_GLOVES_13587), item(ItemID.GRACEFUL_GLOVES_13588), item(ItemID.GRACEFUL_GLOVES_13599), item(ItemID.GRACEFUL_GLOVES_13600), item(ItemID.GRACEFUL_GLOVES_13611), item(ItemID.GRACEFUL_GLOVES_13612), item(ItemID.GRACEFUL_GLOVES_13623), item(ItemID.GRACEFUL_GLOVES_13624), item(ItemID.GRACEFUL_GLOVES_13635), item(ItemID.GRACEFUL_GLOVES_13636), item(ItemID.GRACEFUL_GLOVES_13675), item(ItemID.GRACEFUL_GLOVES_13676), item(ItemID.GRACEFUL_GLOVES_21073), item(ItemID.GRACEFUL_GLOVES_21075)),
any("", item(ItemID.GRACEFUL_BOOTS), item(ItemID.GRACEFUL_BOOTS_11861), item(ItemID.GRACEFUL_BOOTS_13589), item(ItemID.GRACEFUL_BOOTS_13590), item(ItemID.GRACEFUL_BOOTS_13601), item(ItemID.GRACEFUL_BOOTS_13602), item(ItemID.GRACEFUL_BOOTS_13613), item(ItemID.GRACEFUL_BOOTS_13614), item(ItemID.GRACEFUL_BOOTS_13625), item(ItemID.GRACEFUL_BOOTS_13626), item(ItemID.GRACEFUL_BOOTS_13637), item(ItemID.GRACEFUL_BOOTS_13638), item(ItemID.GRACEFUL_BOOTS_13677), item(ItemID.GRACEFUL_BOOTS_13678), item(ItemID.GRACEFUL_BOOTS_21076), item(ItemID.GRACEFUL_BOOTS_21078)))), any("", item(ItemID.GRACEFUL_BOOTS), item(ItemID.GRACEFUL_BOOTS_11861), item(ItemID.GRACEFUL_BOOTS_13589), item(ItemID.GRACEFUL_BOOTS_13590), item(ItemID.GRACEFUL_BOOTS_13601), item(ItemID.GRACEFUL_BOOTS_13602), item(ItemID.GRACEFUL_BOOTS_13613), item(ItemID.GRACEFUL_BOOTS_13614), item(ItemID.GRACEFUL_BOOTS_13625), item(ItemID.GRACEFUL_BOOTS_13626), item(ItemID.GRACEFUL_BOOTS_13637), item(ItemID.GRACEFUL_BOOTS_13638), item(ItemID.GRACEFUL_BOOTS_13677), item(ItemID.GRACEFUL_BOOTS_13678), item(ItemID.GRACEFUL_BOOTS_21076), item(ItemID.GRACEFUL_BOOTS_21078)))),
new SkillChallengeClue("Mix an anti-venom potion.", item(ItemID.ANTIDOTE4_5952), xOfItem(ItemID.ZULRAHS_SCALES, 20)), new SkillChallengeClue("Mix an anti-venom potion.", item(ItemID.ANTIDOTE4_5952), xOfItem(ItemID.ZULRAHS_SCALES, 20)),
new SkillChallengeClue("Mine a piece of Runite ore", "mine a piece of runite ore whilst sporting the finest mining gear.", true, ANY_PICKAXE, all(item(ItemID.PROSPECTOR_HELMET), item(ItemID.PROSPECTOR_JACKET), item(ItemID.PROSPECTOR_LEGS), item(ItemID.PROSPECTOR_BOOTS))), new SkillChallengeClue("Mine a piece of Runite ore", "mine a piece of runite ore whilst sporting the finest mining gear.", true, ANY_PICKAXE, all("Prospector kit", item(ItemID.PROSPECTOR_HELMET), item(ItemID.PROSPECTOR_JACKET), item(ItemID.PROSPECTOR_LEGS), item(ItemID.PROSPECTOR_BOOTS))),
new SkillChallengeClue("Steal a gem from the Ardougne market."), new SkillChallengeClue("Steal a gem from the Ardougne market."),
new SkillChallengeClue("Pickpocket an elf."), new SkillChallengeClue("Pickpocket an elf."),
new SkillChallengeClue("Bind a blood rune at the blood altar.", item(ItemID.DARK_ESSENCE_FRAGMENTS)), new SkillChallengeClue("Bind a blood rune at the blood altar.", item(ItemID.DARK_ESSENCE_FRAGMENTS)),
@@ -173,8 +173,8 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll
new SkillChallengeClue("Cremate a set of fiyr remains.", any("Magic or Redwood Pyre Logs", item(ItemID.MAGIC_PYRE_LOGS), item(ItemID.REDWOOD_PYRE_LOGS)), item(ItemID.TINDERBOX), item(ItemID.FIYR_REMAINS)), new SkillChallengeClue("Cremate a set of fiyr remains.", any("Magic or Redwood Pyre Logs", item(ItemID.MAGIC_PYRE_LOGS), item(ItemID.REDWOOD_PYRE_LOGS)), item(ItemID.TINDERBOX), item(ItemID.FIYR_REMAINS)),
new SkillChallengeClue("Dissect a sacred eel.", item(ItemID.KNIFE), any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.FISHING_BAIT)), new SkillChallengeClue("Dissect a sacred eel.", item(ItemID.KNIFE), any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.FISHING_BAIT)),
new SkillChallengeClue("Kill a lizardman shaman."), new SkillChallengeClue("Kill a lizardman shaman."),
new SkillChallengeClue("Catch an Anglerfish.", "angle for an anglerfish in your finest fishing gear.", true, any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.SANDWORMS), all(item(ItemID.ANGLER_HAT), item(ItemID.ANGLER_TOP), item(ItemID.ANGLER_WADERS), item(ItemID.ANGLER_BOOTS))), new SkillChallengeClue("Catch an Anglerfish.", "angle for an anglerfish in your finest fishing gear.", true, any("Fishing rod", item(ItemID.FISHING_ROD), item(ItemID.PEARL_FISHING_ROD)), item(ItemID.SANDWORMS), all("Angler's outfit", item(ItemID.ANGLER_HAT), item(ItemID.ANGLER_TOP), item(ItemID.ANGLER_WADERS), item(ItemID.ANGLER_BOOTS))),
new SkillChallengeClue("Chop a redwood log.", "chop a redwood log whilst sporting the finest lumberjack gear.", true, ANY_AXE, all(item(ItemID.LUMBERJACK_HAT), item(ItemID.LUMBERJACK_TOP), item(ItemID.LUMBERJACK_LEGS), item(ItemID.LUMBERJACK_BOOTS))), new SkillChallengeClue("Chop a redwood log.", "chop a redwood log whilst sporting the finest lumberjack gear.", true, ANY_AXE, all("Lumberjack outfit", item(ItemID.LUMBERJACK_HAT), item(ItemID.LUMBERJACK_TOP), item(ItemID.LUMBERJACK_LEGS), item(ItemID.LUMBERJACK_BOOTS))),
new SkillChallengeClue("Craft a light orb in the Dorgesh-Kaan bank.", item(ItemID.CAVE_GOBLIN_WIRE), item(ItemID.EMPTY_LIGHT_ORB)), new SkillChallengeClue("Craft a light orb in the Dorgesh-Kaan bank.", item(ItemID.CAVE_GOBLIN_WIRE), item(ItemID.EMPTY_LIGHT_ORB)),
new SkillChallengeClue("Kill a reanimated Abyssal Demon.", "kill a reanimated abyssal.", xOfItem(ItemID.SOUL_RUNE, 4), xOfItem(ItemID.BLOOD_RUNE, 1), any("Nature Rune x4", xOfItem(ItemID.NATURE_RUNE, 4), item(ItemID.BRYOPHYTAS_STAFF)), item(ItemID.ENSOULED_ABYSSAL_HEAD)), new SkillChallengeClue("Kill a reanimated Abyssal Demon.", "kill a reanimated abyssal.", xOfItem(ItemID.SOUL_RUNE, 4), xOfItem(ItemID.BLOOD_RUNE, 1), any("Nature Rune x4", xOfItem(ItemID.NATURE_RUNE, 4), item(ItemID.BRYOPHYTAS_STAFF)), item(ItemID.ENSOULED_ABYSSAL_HEAD)),
new SkillChallengeClue("Kill a Fiyr shade inside Mort'tons shade catacombs.", any("Any Silver Shade Key", item(ItemID.SILVER_KEY_RED), item(ItemID.SILVER_KEY_BROWN), item(ItemID.SILVER_KEY_CRIMSON), item(ItemID.SILVER_KEY_BLACK), item(ItemID.SILVER_KEY_PURPLE))) new SkillChallengeClue("Kill a Fiyr shade inside Mort'tons shade catacombs.", any("Any Silver Shade Key", item(ItemID.SILVER_KEY_RED), item(ItemID.SILVER_KEY_BROWN), item(ItemID.SILVER_KEY_CRIMSON), item(ItemID.SILVER_KEY_BLACK), item(ItemID.SILVER_KEY_PURPLE)))

View File

@@ -272,7 +272,7 @@ public class FishingPlugin extends Plugin
} }
final NPC npc = (NPC) target; final NPC npc = (NPC) target;
FishingSpot spot = FishingSpot.getSPOTS().get(npc.getId()); FishingSpot spot = FishingSpot.findSpot(npc.getId());
if (spot == null) if (spot == null)
{ {
@@ -311,7 +311,7 @@ public class FishingPlugin extends Plugin
for (NPC npc : fishingSpots) for (NPC npc : fishingSpots)
{ {
if (FishingSpot.getSPOTS().get(npc.getId()) == FishingSpot.MINNOW && this.showMinnowOverlay) if (FishingSpot.findSpot(npc.getId()) == FishingSpot.MINNOW && this.showMinnowOverlay)
{ {
final int id = npc.getIndex(); final int id = npc.getIndex();
final MinnowSpot minnowSpot = minnowSpots.get(id); final MinnowSpot minnowSpot = minnowSpots.get(id);
@@ -336,7 +336,7 @@ public class FishingPlugin extends Plugin
{ {
final NPC npc = event.getNpc(); final NPC npc = event.getNpc();
if (!FishingSpot.getSPOTS().containsKey(npc.getId())) if (FishingSpot.findSpot(npc.getId()) == null)
{ {
return; return;
} }
@@ -448,8 +448,22 @@ public class FishingPlugin extends Plugin
private void inverseSortSpotDistanceFromPlayer() private void inverseSortSpotDistanceFromPlayer()
{ {
if (fishingSpots.isEmpty())
{
return;
}
final LocalPoint cameraPoint = new LocalPoint(client.getCameraX(), client.getCameraY()); final LocalPoint cameraPoint = new LocalPoint(client.getCameraX(), client.getCameraY());
fishingSpots.sort(Comparator.comparing(npc -> -1 * npc.getLocalLocation().distanceTo(cameraPoint))); fishingSpots.sort(
Comparator.comparing(
// Negate to have the furthest first
(NPC npc) -> -npc.getLocalLocation().distanceTo(cameraPoint))
// Order by position
.thenComparing(NPC::getLocalLocation, Comparator.comparing(LocalPoint::getX)
.thenComparing(LocalPoint::getY))
// And then by id
.thenComparing(NPC::getId)
);
} }
private void updateConfig() private void updateConfig()

View File

@@ -170,7 +170,6 @@ enum FishingSpot
COMMON_TENCH("Common tench, Bluegill, Greater siren, Mottled eel", ItemID.COMMON_TENCH, COMMON_TENCH("Common tench, Bluegill, Greater siren, Mottled eel", ItemID.COMMON_TENCH,
FISHING_SPOT_8523); FISHING_SPOT_8523);
@Getter
private static final Map<Integer, FishingSpot> SPOTS; private static final Map<Integer, FishingSpot> SPOTS;
private final String name; private final String name;
@@ -198,4 +197,9 @@ enum FishingSpot
this.fishSpriteId = fishSpriteId; this.fishSpriteId = fishSpriteId;
this.ids = ids; this.ids = ids;
} }
static FishingSpot findSpot(int id)
{
return SPOTS.get(id);
}
} }

View File

@@ -64,7 +64,7 @@ class FishingSpotMinimapOverlay extends Overlay
for (NPC npc : plugin.getFishingSpots()) for (NPC npc : plugin.getFishingSpots())
{ {
FishingSpot spot = FishingSpot.getSPOTS().get(npc.getId()); FishingSpot spot = FishingSpot.findSpot(npc.getId());
if (spot == null) if (spot == null)
{ {

View File

@@ -41,6 +41,7 @@ import net.runelite.api.NPC;
import net.runelite.api.Perspective; import net.runelite.api.Perspective;
import net.runelite.api.Point; import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemManager;
import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayLayer;
@@ -81,9 +82,11 @@ class FishingSpotOverlay extends Overlay
return null; return null;
} }
FishingSpot previousSpot = null;
WorldPoint previousLocation = null;
for (NPC npc : plugin.getFishingSpots()) for (NPC npc : plugin.getFishingSpots())
{ {
FishingSpot spot = FishingSpot.getSPOTS().get(npc.getId()); FishingSpot spot = FishingSpot.findSpot(npc.getId());
if (spot == null) if (spot == null)
{ {
@@ -95,7 +98,25 @@ class FishingSpotOverlay extends Overlay
continue; continue;
} }
Color color = npc.getSpotAnimation() == GraphicID.FLYING_FISH ? Color.RED : Color.CYAN; // This relies on the sort order to keep identical npcs on the same tile adjacent to each other
if (previousSpot == spot && previousLocation.equals(npc.getWorldLocation()))
{
continue;
}
Color color;
if (npc.getSpotAnimation() == GraphicID.FLYING_FISH)
{
color = Color.RED;
}
else if (spot == FishingSpot.COMMON_TENCH && npc.getWorldLocation().distanceTo2D(client.getLocalPlayer().getWorldLocation()) <= ONE_TICK_AERIAL_FISHING)
{
color = Color.GREEN;
}
else
{
color = Color.CYAN;
}
if (spot == FishingSpot.MINNOW && plugin.isShowMinnowOverlay()) if (spot == FishingSpot.MINNOW && plugin.isShowMinnowOverlay())
{ {
@@ -127,12 +148,6 @@ class FishingSpotOverlay extends Overlay
{ {
Polygon poly = npc.getCanvasTilePoly(); Polygon poly = npc.getCanvasTilePoly();
if (spot == FishingSpot.COMMON_TENCH
&& npc.getWorldLocation().distanceTo2D(client.getLocalPlayer().getWorldLocation()) <= ONE_TICK_AERIAL_FISHING)
{
color = Color.GREEN;
}
if (poly != null) if (poly != null)
{ {
OverlayUtil.renderPolygon(graphics, poly, color.darker()); OverlayUtil.renderPolygon(graphics, poly, color.darker());
@@ -164,17 +179,14 @@ class FishingSpotOverlay extends Overlay
String text = spot.getName(); String text = spot.getName();
Point textLocation = npc.getCanvasTextLocation(graphics, text, npc.getLogicalHeight() + 40); Point textLocation = npc.getCanvasTextLocation(graphics, text, npc.getLogicalHeight() + 40);
if (spot == FishingSpot.COMMON_TENCH
&& npc.getWorldLocation().distanceTo2D(client.getLocalPlayer().getWorldLocation()) <= ONE_TICK_AERIAL_FISHING)
{
color = Color.GREEN;
}
if (textLocation != null) if (textLocation != null)
{ {
OverlayUtil.renderTextLocation(graphics, textLocation, text, color.darker()); OverlayUtil.renderTextLocation(graphics, textLocation, text, color.darker());
} }
} }
previousSpot = spot;
previousLocation = npc.getWorldLocation();
} }
return null; return null;

View File

@@ -38,7 +38,6 @@ class TextureManager
private static final float PERC_64 = 1f / 64f; private static final float PERC_64 = 1f / 64f;
private static final float PERC_128 = 1f / 128f; private static final float PERC_128 = 1f / 128f;
private static final int SMALL_TEXTURE_SIZE = 64;
private static final int TEXTURE_SIZE = 128; private static final int TEXTURE_SIZE = 128;
int initTextureArray(TextureProvider textureProvider, GL4 gl) int initTextureArray(TextureProvider textureProvider, GL4 gl)
@@ -130,8 +129,15 @@ class TextureManager
++cnt; ++cnt;
int srcSize = srcPixels.length == 4096 ? SMALL_TEXTURE_SIZE : TEXTURE_SIZE; if (srcPixels.length != TEXTURE_SIZE * TEXTURE_SIZE)
byte[] pixels = convertPixels(srcPixels, srcSize, srcSize, TEXTURE_SIZE, TEXTURE_SIZE); {
// The texture storage is 128x128 bytes, and will only work correctly with the
// 128x128 textures from high detail mode
log.warn("Texture size for {} is {}!", textureId, srcPixels.length);
continue;
}
byte[] pixels = convertPixels(srcPixels, TEXTURE_SIZE, TEXTURE_SIZE, TEXTURE_SIZE, TEXTURE_SIZE);
ByteBuffer pixelBuffer = ByteBuffer.wrap(pixels); ByteBuffer pixelBuffer = ByteBuffer.wrap(pixels);
gl.glTexSubImage3D(gl.GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureId, TEXTURE_SIZE, TEXTURE_SIZE, gl.glTexSubImage3D(gl.GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureId, TEXTURE_SIZE, TEXTURE_SIZE,
1, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixelBuffer); 1, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, pixelBuffer);

View File

@@ -299,7 +299,15 @@ public class GroundMarkerPlugin extends Plugin
{ {
if (hotKeyPressed && event.getOption().equals(WALK_HERE)) if (hotKeyPressed && event.getOption().equals(WALK_HERE))
{ {
final Tile selectedSceneTile = client.getSelectedSceneTile();
if (selectedSceneTile == null)
{
return;
}
MenuEntry[] menuEntries = client.getMenuEntries(); MenuEntry[] menuEntries = client.getMenuEntries();
int lastIndex = menuEntries.length; int lastIndex = menuEntries.length;
menuEntries = Arrays.copyOf(menuEntries, lastIndex + 4); menuEntries = Arrays.copyOf(menuEntries, lastIndex + 4);

View File

@@ -214,7 +214,10 @@ enum FixedPriceItem
VERACS_FLAIL_25(ItemID.VERACS_FLAIL_25, 2500, ItemID.VERACS_FLAIL_0), VERACS_FLAIL_25(ItemID.VERACS_FLAIL_25, 2500, ItemID.VERACS_FLAIL_0),
VERACS_FLAIL_50(ItemID.VERACS_FLAIL_50, 5000, ItemID.VERACS_FLAIL_0), VERACS_FLAIL_50(ItemID.VERACS_FLAIL_50, 5000, ItemID.VERACS_FLAIL_0),
VERACS_FLAIL_75(ItemID.VERACS_FLAIL_75, 7500, ItemID.VERACS_FLAIL_0), VERACS_FLAIL_75(ItemID.VERACS_FLAIL_75, 7500, ItemID.VERACS_FLAIL_0),
VERACS_FLAIL_100(ItemID.VERACS_FLAIL_100, 10000, ItemID.VERACS_FLAIL_0); VERACS_FLAIL_100(ItemID.VERACS_FLAIL_100, 10000, ItemID.VERACS_FLAIL_0),
AVERNIC_DEFENDER(ItemID.AVERNIC_DEFENDER, 4040000),
;
private final int itemId; private final int itemId;
private final int offset; private final int offset;

View File

@@ -41,6 +41,7 @@ import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
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;
@@ -106,6 +107,7 @@ import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.loottracker.localstorage.LTItemEntry; import net.runelite.client.plugins.loottracker.localstorage.LTItemEntry;
import net.runelite.client.plugins.loottracker.localstorage.LTRecord; import net.runelite.client.plugins.loottracker.localstorage.LTRecord;
import net.runelite.client.plugins.loottracker.localstorage.LootRecordWriter; import net.runelite.client.plugins.loottracker.localstorage.LootRecordWriter;
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;
@@ -218,6 +220,7 @@ 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 final List<LootRecord> queuedLoots = new ArrayList<>();
private Map<String, Integer> killCountMap = new HashMap<>(); private Map<String, Integer> killCountMap = new HashMap<>();
private boolean gotPet = false; private boolean gotPet = false;
@@ -291,6 +294,7 @@ public class LootTrackerPlugin extends Plugin
private void onSessionClose(SessionClose sessionClose) private void onSessionClose(SessionClose sessionClose)
{ {
submitLoot();
lootTrackerClient = null; lootTrackerClient = null;
} }
@@ -331,7 +335,6 @@ public class LootTrackerPlugin extends Plugin
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{ {
addSubscriptions(); addSubscriptions();
ignoredItems = Text.fromCSV(config.getIgnoredItems()); ignoredItems = Text.fromCSV(config.getIgnoredItems());
@@ -421,6 +424,7 @@ public class LootTrackerPlugin extends Plugin
protected void shutDown() protected void shutDown()
{ {
eventBus.unregister(this); eventBus.unregister(this);
submitLoot();
clientToolbar.removeNavigation(navButton); clientToolbar.removeNavigation(navButton);
lootTrackerClient = null; lootTrackerClient = null;
@@ -528,9 +532,12 @@ public class LootTrackerPlugin extends Plugin
LootRecord lootRecord = new LootRecord(name, localUsername, LootRecordType.NPC, LootRecord lootRecord = new LootRecord(name, localUsername, LootRecordType.NPC,
toGameItems(items), Instant.now()); toGameItems(items), Instant.now());
if (config.saveLoot() && lootTrackerClient != null) if (config.saveLoot())
{ {
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
if (this.localPersistence) if (this.localPersistence)
{ {
@@ -578,9 +585,12 @@ public class LootTrackerPlugin extends Plugin
SwingUtilities.invokeLater(() -> panel.add(name, localUsername, combat, entries)); SwingUtilities.invokeLater(() -> panel.add(name, localUsername, combat, entries));
LootRecord lootRecord = new LootRecord(name, localUsername, LootRecordType.PLAYER, LootRecord lootRecord = new LootRecord(name, localUsername, LootRecordType.PLAYER,
toGameItems(items), Instant.now()); toGameItems(items), Instant.now());
if (lootTrackerClient != null && this.saveLoot) if (this.saveLoot)
{ {
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
if (config.localPersistence()) if (config.localPersistence())
{ {
@@ -691,9 +701,12 @@ public class LootTrackerPlugin extends Plugin
final int killCount = killCountMap.getOrDefault(eventType.toUpperCase(), -1); final int killCount = killCountMap.getOrDefault(eventType.toUpperCase(), -1);
if (lootTrackerClient != null && this.saveLoot) if (this.saveLoot)
{ {
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
if (config.localPersistence()) if (config.localPersistence())
{ {
@@ -871,9 +884,12 @@ public class LootTrackerPlugin extends Plugin
client.getLocalPlayer().getCombatLevel(), entries)); client.getLocalPlayer().getCombatLevel(), entries));
LootRecord lootRecord = new LootRecord(name, client.getLocalPlayer().getName(), LootRecordType.DEATH, LootRecord lootRecord = new LootRecord(name, client.getLocalPlayer().getName(), LootRecordType.DEATH,
toGameItems(itemsLost), Instant.now()); toGameItems(itemsLost), Instant.now());
if (lootTrackerClient != null && this.saveLoot) if (this.saveLoot)
{ {
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
if (this.localPersistence) if (this.localPersistence)
{ {
@@ -989,6 +1005,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);
@@ -1045,9 +1095,12 @@ public class LootTrackerPlugin extends Plugin
LootRecord lootRecord = new LootRecord(chestType, client.getLocalPlayer().getName(), LootRecord lootRecord = new LootRecord(chestType, client.getLocalPlayer().getName(),
LootRecordType.EVENT, toGameItems(items), Instant.now()); LootRecordType.EVENT, toGameItems(items), Instant.now());
if (lootTrackerClient != null && config.saveLoot()) if (config.saveLoot())
{ {
lootTrackerClient.submit(lootRecord); synchronized (queuedLoots)
{
queuedLoots.add(lootRecord);
}
} }
if (config.localPersistence()) if (config.localPersistence())

View File

@@ -26,7 +26,10 @@ package net.runelite.client.plugins.lowmemory;
import javax.inject.Inject; import javax.inject.Inject;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.events.GameStateChanged;
import net.runelite.client.callback.ClientThread; import net.runelite.client.callback.ClientThread;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginDescriptor;
@@ -44,10 +47,18 @@ public class LowMemoryPlugin extends Plugin
@Inject @Inject
private ClientThread clientThread; private ClientThread clientThread;
@Inject
private EventBus eventBus;
@Override @Override
protected void startUp() protected void startUp()
{ {
clientThread.invoke(() -> client.changeMemoryMode(true)); this.eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
if (client.getGameState() == GameState.LOGGED_IN)
{
clientThread.invoke(() -> client.changeMemoryMode(true));
}
} }
@Override @Override
@@ -55,4 +66,15 @@ public class LowMemoryPlugin extends Plugin
{ {
clientThread.invoke(() -> client.changeMemoryMode(false)); clientThread.invoke(() -> client.changeMemoryMode(false));
} }
private void onGameStateChanged(GameStateChanged event)
{
// When the client starts it initializes the texture size based on the memory mode setting.
// Don't set low memory before the login screen is ready to prevent loading the low detail textures,
// which breaks the gpu plugin due to it requiring the 128x128px textures
if (event.getGameState() == GameState.LOGIN_SCREEN)
{
client.changeMemoryMode(true);
}
}
} }

View File

@@ -54,6 +54,7 @@ import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Actor; import net.runelite.api.Actor;
import net.runelite.api.ChatMessageType; import net.runelite.api.ChatMessageType;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ItemID; import net.runelite.api.ItemID;
import net.runelite.api.MessageNode; import net.runelite.api.MessageNode;
import net.runelite.api.NPC; import net.runelite.api.NPC;
@@ -133,7 +134,7 @@ public class SlayerPlugin extends Plugin
private static final int GROTESQUE_GUARDIANS_REGION = 6727; private static final int GROTESQUE_GUARDIANS_REGION = 6727;
private static final Set<Task> weaknessTasks = ImmutableSet.of(Task.DESERT_LIZARDS, Task.GARGOYLES, private static final Set<Task> weaknessTasks = ImmutableSet.of(Task.LIZARDS, Task.GARGOYLES,
Task.GROTESQUE_GUARDIANS, Task.GROTESQUE_GUARDIANS, Task.MUTATED_ZYGOMITES, Task.ROCKSLUGS); Task.GROTESQUE_GUARDIANS, Task.GROTESQUE_GUARDIANS, Task.MUTATED_ZYGOMITES, Task.ROCKSLUGS);
// Chat Command // Chat Command
@@ -242,7 +243,7 @@ public class SlayerPlugin extends Plugin
private Task weaknessTask = null; private Task weaknessTask = null;
private TaskCounter counter; private TaskCounter counter;
private int cachedXp; private int cachedXp = -1;
private int cachedPoints; private int cachedPoints;
private Instant infoTimer; private Instant infoTimer;
private List<String> targetNames = new ArrayList<>(); private List<String> targetNames = new ArrayList<>();
@@ -328,6 +329,11 @@ public class SlayerPlugin extends Plugin
clientToolbar.addNavigation(navButton); clientToolbar.addNavigation(navButton);
if (client.getGameState() == GameState.LOGGED_IN)
{
cachedXp = client.getSkillExperience(SLAYER);
}
chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit); chatCommandManager.registerCommandAsync(TASK_COMMAND_STRING, this::taskLookup, this::taskSubmit);
chatCommandManager.registerCommandAsync(POINTS_COMMAND_STRING, this::pointsLookup); //here chatCommandManager.registerCommandAsync(POINTS_COMMAND_STRING, this::pointsLookup); //here
@@ -348,6 +354,8 @@ public class SlayerPlugin extends Plugin
chatCommandManager.unregisterCommand(TASK_COMMAND_STRING); chatCommandManager.unregisterCommand(TASK_COMMAND_STRING);
chatCommandManager.unregisterCommand(POINTS_COMMAND_STRING); chatCommandManager.unregisterCommand(POINTS_COMMAND_STRING);
clientToolbar.removeNavigation(navButton); clientToolbar.removeNavigation(navButton);
cachedXp = -1;
} }
private void addSubscriptions() private void addSubscriptions()
@@ -376,6 +384,7 @@ public class SlayerPlugin extends Plugin
{ {
case HOPPING: case HOPPING:
case LOGGING_IN: case LOGGING_IN:
cachedXp = -1;
cachedPoints = 0; cachedPoints = 0;
clearTrackedNPCs(); clearTrackedNPCs();
break; break;
@@ -736,45 +745,47 @@ public class SlayerPlugin extends Plugin
return; return;
} }
if (cachedXp != 0) if (cachedXp == -1)
{ {
final Task task = Task.getTask(taskName); // this is the initial xp sent on login
cachedXp = slayerExp;
return;
}
if (task == null) final Task task = Task.getTask(taskName);
// null tasks are technically valid, it only means they arent explicitly defined in the Task enum
// allow them through so that if there is a task capture failure the counter will still work
final int taskKillExp = task != null ? task.getExpectedKillExp() : 0;
// Only count exp gain as a kill if the task either has no expected exp for a kill, or if the exp gain is equal
// to the expected exp gain for the task.
if (taskKillExp == 0 || taskKillExp == slayerExp - cachedXp)
{
killedOne();
}
else
{
// this is not the initial xp sent on login so these are new xp gains
int gains = slayerExp - cachedXp;
// potential npcs to give xp drop are current highlighted npcs and the lingering presences
List<NPCPresence> potentialNPCs = new ArrayList<>(lingeringPresences);
for (NPC npc : highlightedTargets)
{ {
return; NPCPresence currentPresence = NPCPresence.buildPresence(npc);
potentialNPCs.add(currentPresence);
} }
final int taskKillExp = task.getExpectedKillExp(); int killCount = estimateKillCount(potentialNPCs, gains);
for (int i = 0; i < killCount; i++)
// Only count exp gain as a kill if the task either has no expected exp for a kill, or if the exp gain is equal
// to the expected exp gain for the task.
if (taskKillExp == 0 || taskKillExp == slayerExp - cachedXp)
{ {
killedOne(); killedOne();
} int delta = slayerExp - cachedXp;
else currentTask.setElapsedXp(currentTask.getElapsedXp() + delta);
{
// this is not the initial xp sent on login so these are new xp gains
int gains = slayerExp - cachedXp;
// potential npcs to give xp drop are current highlighted npcs and the lingering presences
List<NPCPresence> potentialNPCs = new ArrayList<>(lingeringPresences);
for (NPC npc : highlightedTargets)
{
NPCPresence currentPresence = NPCPresence.buildPresence(npc);
potentialNPCs.add(currentPresence);
}
int killCount = estimateKillCount(potentialNPCs, gains);
for (int i = 0; i < killCount; i++)
{
killedOne();
int delta = slayerExp - cachedXp;
currentTask.setElapsedXp(currentTask.getElapsedXp() + delta);
}
} }
} }
cachedXp = slayerExp; cachedXp = slayerExp;
} }

View File

@@ -112,8 +112,6 @@ enum Task
asList("Night beast"), Collections.emptyList()), asList("Night beast"), Collections.emptyList()),
DARK_WARRIORS("Dark warriors", ItemID.BLACK_MED_HELM), DARK_WARRIORS("Dark warriors", ItemID.BLACK_MED_HELM),
DERANGED_ARCHAEOLOGIST("Deranged Archaeologist", ItemID.ARCHAEOLOGISTS_DIARY), DERANGED_ARCHAEOLOGIST("Deranged Archaeologist", ItemID.ARCHAEOLOGISTS_DIARY),
DESERT_LIZARDS("Desert lizards", ItemID.DESERT_LIZARD,
asList("Small lizard", "Lizard"), Collections.emptyList(), 4, ItemID.ICE_COOLER),
DOGS("Dogs", ItemID.GUARD_DOG, asList("Jackal"), Collections.emptyList()), DOGS("Dogs", ItemID.GUARD_DOG, asList("Jackal"), Collections.emptyList()),
DRAKES("Drakes", ItemID.DRAKE), DRAKES("Drakes", ItemID.DRAKE),
DUST_DEVILS("Dust devils", ItemID.DUST_DEVIL, DUST_DEVILS("Dust devils", ItemID.DUST_DEVIL,
@@ -174,6 +172,8 @@ enum Task
LESSER_DEMONS("Lesser demons", ItemID.LESSER_DEMON_MASK), LESSER_DEMONS("Lesser demons", ItemID.LESSER_DEMON_MASK),
LIZARDMEN("Lizardmen", ItemID.LIZARDMAN_FANG, LIZARDMEN("Lizardmen", ItemID.LIZARDMAN_FANG,
asList("Lizardman"), Collections.emptyList()), asList("Lizardman"), Collections.emptyList()),
LIZARDS("Lizards", ItemID.DESERT_LIZARD,
asList("Desert lizard", "Sulphur lizard", "Small lizard", "Lizard"), Collections.emptyList(), 4, ItemID.ICE_COOLER),
MAGIC_AXES("Magic axes", ItemID.IRON_BATTLEAXE), MAGIC_AXES("Magic axes", ItemID.IRON_BATTLEAXE),
MAMMOTHS("Mammoths", ItemID.ATTACKER_HORN, MAMMOTHS("Mammoths", ItemID.ATTACKER_HORN,
asList("Mammoth"), Collections.emptyList()), asList("Mammoth"), Collections.emptyList()),

View File

@@ -59,7 +59,7 @@ enum GameTimer
ICEBURST(SpriteID.SPELL_ICE_BURST, GameTimerImageType.SPRITE, "Ice burst", GraphicID.ICE_BURST, 10, ChronoUnit.SECONDS, true), ICEBURST(SpriteID.SPELL_ICE_BURST, GameTimerImageType.SPRITE, "Ice burst", GraphicID.ICE_BURST, 10, ChronoUnit.SECONDS, true),
ICEBLITZ(SpriteID.SPELL_ICE_BLITZ, GameTimerImageType.SPRITE, "Ice blitz", GraphicID.ICE_BLITZ, 15, ChronoUnit.SECONDS, true), ICEBLITZ(SpriteID.SPELL_ICE_BLITZ, GameTimerImageType.SPRITE, "Ice blitz", GraphicID.ICE_BLITZ, 15, ChronoUnit.SECONDS, true),
ICEBARRAGE(SpriteID.SPELL_ICE_BARRAGE, GameTimerImageType.SPRITE, "Ice barrage", GraphicID.ICE_BARRAGE, 20, ChronoUnit.SECONDS, true), ICEBARRAGE(SpriteID.SPELL_ICE_BARRAGE, GameTimerImageType.SPRITE, "Ice barrage", GraphicID.ICE_BARRAGE, 20, ChronoUnit.SECONDS, true),
IMBUEDHEART(ItemID.IMBUED_HEART, GameTimerImageType.ITEM, "Imbued heart", GraphicID.IMBUED_HEART, 420, ChronoUnit.SECONDS), IMBUEDHEART(ItemID.IMBUED_HEART, GameTimerImageType.ITEM, "Imbued heart", GraphicID.IMBUED_HEART, 420, ChronoUnit.SECONDS, true),
VENGEANCE(SpriteID.SPELL_VENGEANCE, GameTimerImageType.SPRITE, "Vengeance", 30, ChronoUnit.SECONDS), VENGEANCE(SpriteID.SPELL_VENGEANCE, GameTimerImageType.SPRITE, "Vengeance", 30, ChronoUnit.SECONDS),
EXSUPERANTIFIRE(ItemID.EXTENDED_SUPER_ANTIFIRE4, GameTimerImageType.ITEM, "Extended Super AntiFire", 6, ChronoUnit.MINUTES), EXSUPERANTIFIRE(ItemID.EXTENDED_SUPER_ANTIFIRE4, GameTimerImageType.ITEM, "Extended Super AntiFire", 6, ChronoUnit.MINUTES),
OVERLOAD_RAID(ItemID.OVERLOAD_4_20996, GameTimerImageType.ITEM, "Overload", 5, ChronoUnit.MINUTES, true), OVERLOAD_RAID(ItemID.OVERLOAD_4_20996, GameTimerImageType.ITEM, "Overload", 5, ChronoUnit.MINUTES, true),

View File

@@ -48,11 +48,15 @@ class ClientConfigLoader
final RSConfig config = new RSConfig(); final RSConfig config = new RSConfig();
try (final Response response = RuneLiteAPI.CLIENT.newCall(request).execute(); try (final Response response = RuneLiteAPI.CLIENT.newCall(request).execute())
final BufferedReader in = new BufferedReader(new InputStreamReader(response.body().byteStream())))
{ {
String str; if (!response.isSuccessful())
{
throw new IOException("Unsuccessful response: " + response.message());
}
String str;
final BufferedReader in = new BufferedReader(new InputStreamReader(response.body().byteStream()));
while ((str = in.readLine()) != null) while ((str = in.readLine()) != null)
{ {
int idx = str.indexOf('='); int idx = str.indexOf('=');

View File

@@ -276,6 +276,14 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
if (overlayPosition == OverlayPosition.DYNAMIC || overlayPosition == OverlayPosition.TOOLTIP) if (overlayPosition == OverlayPosition.DYNAMIC || overlayPosition == OverlayPosition.TOOLTIP)
{ {
safeRender(client, overlay, layer, graphics, new Point()); safeRender(client, overlay, layer, graphics, new Point());
// Restore graphics2d properties
graphics.setTransform(transform);
graphics.setStroke(stroke);
graphics.setComposite(composite);
graphics.setPaint(paint);
graphics.setRenderingHints(renderingHints);
graphics.setBackground(background);
} }
else else
{ {
@@ -312,6 +320,14 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
safeRender(client, overlay, layer, graphics, location); safeRender(client, overlay, layer, graphics, location);
// Restore graphics2d properties prior to drawing bounds
graphics.setTransform(transform);
graphics.setStroke(stroke);
graphics.setComposite(composite);
graphics.setPaint(paint);
graphics.setRenderingHints(renderingHints);
graphics.setBackground(background);
final Rectangle bounds = overlay.getBounds(); final Rectangle bounds = overlay.getBounds();
if (!bounds.isEmpty()) if (!bounds.isEmpty())
@@ -330,14 +346,6 @@ public class OverlayRenderer extends MouseAdapter implements KeyListener
} }
} }
} }
// Restore graphics2d properties
graphics.setTransform(transform);
graphics.setStroke(stroke);
graphics.setComposite(composite);
graphics.setPaint(paint);
graphics.setRenderingHints(renderingHints);
graphics.setBackground(background);
} }
} }

View File

@@ -31,7 +31,7 @@ uniform float smoothBanding;
uniform vec4 fogColor; uniform vec4 fogColor;
in vec4 Color; in vec4 Color;
in float fHsl; centroid in float fHsl;
in vec4 fUv; in vec4 fUv;
in float fogAmount; in float fogAmount;

View File

@@ -52,7 +52,7 @@ in vec4 vUv[];
in float vFogAmount[]; in float vFogAmount[];
out vec4 Color; out vec4 Color;
out float fHsl; centroid out float fHsl;
out vec4 fUv; out vec4 fUv;
out float fogAmount; out float fogAmount;

View File

@@ -961,3 +961,7 @@ R 49 92 51 92
// Iorwerth dungeon // Iorwerth dungeon
#030A0A #030A0A
R 49 193 51 194 R 49 193 51 194
// Braindeath Island
#8AD2DF
R 33 79 33 80

View File

@@ -628,4 +628,29 @@ public class ItemsKeptOnDeathPluginTest
final List<ItemStack> lost = deathItems.getLostItems(); final List<ItemStack> lost = deathItems.getLostItems();
assertTrue(lost.contains(new ItemStack(ItemID.DRAGON_DEFENDER, 1))); assertTrue(lost.contains(new ItemStack(ItemID.DRAGON_DEFENDER, 1)));
} }
@Test
public void avernicDefenderPriceTest()
{
final Item defender = mItem(ItemID.AVERNIC_DEFENDER, 1, "Avernic defender", false, 0);
final int defenderOffset = FixedPriceItem.AVERNIC_DEFENDER.getOffset();
final Integer defenderBrokenPrice = BrokenOnDeathItem.getRepairPrice(ItemID.AVERNIC_DEFENDER);
final int defenderExpectedPrice = (defenderBrokenPrice == null ? 0 : defenderBrokenPrice) + defenderOffset;
assertEquals(defenderExpectedPrice, plugin.getDeathPrice(defender));
final Item[] inv = new Item[]
{
defender,
mItem(ItemID.BERSERKER_RING_I, 1, "Berserker Ring (i)", false, 3042579)
};
plugin.isSkulled = true;
plugin.protectingItem = true;
plugin.wildyLevel = 21;
final DeathItems deathItems = plugin.calculateKeptLostItems(inv, new Item[0]);
final List<ItemStack> kept = deathItems.getKeptItems();
assertTrue(kept.contains(new ItemStack(ItemID.AVERNIC_DEFENDER, 1)));
}
} }

View File

@@ -453,6 +453,49 @@ public class SlayerPluginTest
} }
} }
@Test
public void testCorrectlyCapturedTaskKill()
{
final Player player = mock(Player.class);
when(player.getLocalLocation()).thenReturn(new LocalPoint(0, 0));
when(client.getLocalPlayer()).thenReturn(player);
final ExperienceChanged experienceChanged = new ExperienceChanged();
experienceChanged.setSkill(Skill.SLAYER);
when(client.getSkillExperience(Skill.SLAYER)).thenReturn(100);
slayerPlugin.onExperienceChanged(experienceChanged);
slayerPlugin.setTask("Dagannoth", 143, 143, true, 0);
when(client.getSkillExperience(Skill.SLAYER)).thenReturn(110);
slayerPlugin.onExperienceChanged(experienceChanged);
assertEquals(142, slayerPlugin.getCurrentTask().getAmount());
}
@Test
public void testIncorrectlyCapturedTaskKill()
{
final Player player = mock(Player.class);
when(player.getLocalLocation()).thenReturn(new LocalPoint(0, 0));
when(client.getLocalPlayer()).thenReturn(player);
final ExperienceChanged experienceChanged = new ExperienceChanged();
experienceChanged.setSkill(Skill.SLAYER);
when(client.getSkillExperience(Skill.SLAYER)).thenReturn(100);
slayerPlugin.onExperienceChanged(experienceChanged);
slayerPlugin.setTask("Monster", 98, 98, true, 0);
assert Task.getTask("Monster") == null;
when(client.getSkillExperience(Skill.SLAYER)).thenReturn(110);
slayerPlugin.onExperienceChanged(experienceChanged);
assertEquals(97, slayerPlugin.getCurrentTask().getAmount());
}
@Test @Test
public void testJadTaskKill() public void testJadTaskKill()
{ {
@@ -508,4 +551,25 @@ public class SlayerPluginTest
assertEquals(0, slayerPlugin.getCurrentTask().getAmount()); assertEquals(0, slayerPlugin.getCurrentTask().getAmount());
} }
@Test
public void testNewAccountSlayerKill()
{
final Player player = mock(Player.class);
when(player.getLocalLocation()).thenReturn(new LocalPoint(0, 0));
when(client.getLocalPlayer()).thenReturn(player);
final ExperienceChanged experienceChanged = new ExperienceChanged();
experienceChanged.setSkill(Skill.SLAYER);
slayerPlugin.setTask("Bears", 35, 35, true, 0);
when(client.getSkillExperience(Skill.SLAYER)).thenReturn(0);
slayerPlugin.onExperienceChanged(experienceChanged);
when(client.getSkillExperience(Skill.SLAYER)).thenReturn(27);
slayerPlugin.onExperienceChanged(experienceChanged);
assertEquals(34, slayerPlugin.getCurrentTask().getAmount());
}
} }