diff --git a/http-api/src/main/java/net/runelite/http/api/item/ItemClient.java b/http-api/src/main/java/net/runelite/http/api/item/ItemClient.java index 39ca8e2e90..8b6fbfef53 100644 --- a/http-api/src/main/java/net/runelite/http/api/item/ItemClient.java +++ b/http-api/src/main/java/net/runelite/http/api/item/ItemClient.java @@ -169,4 +169,35 @@ public class ItemClient throw new IOException(ex); } } + + public ItemPrice[] getPrices() throws IOException + { + HttpUrl.Builder urlBuilder = RuneLiteAPI.getApiBase().newBuilder() + .addPathSegment("item") + .addPathSegment("prices"); + + HttpUrl url = urlBuilder.build(); + + logger.debug("Built URI: {}", url); + + Request request = new Request.Builder() + .url(url) + .build(); + + try (Response response = RuneLiteAPI.CLIENT.newCall(request).execute()) + { + if (!response.isSuccessful()) + { + logger.warn("Error looking up prices: {}", response.message()); + return null; + } + + InputStream in = response.body().byteStream(); + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), ItemPrice[].class); + } + catch (JsonParseException ex) + { + throw new IOException(ex); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java index 4dcd3b62fd..f22afe0e15 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/ItemManager.java @@ -30,11 +30,8 @@ import com.google.common.cache.LoadingCache; import com.google.common.eventbus.Subscribe; import java.awt.image.BufferedImage; import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -65,23 +62,13 @@ public class ItemManager private final boolean stackable; } - /** - * not yet looked up - */ - static final ItemPrice EMPTY = new ItemPrice(); - - /** - * has no price - */ - static final ItemPrice NONE = new ItemPrice(); - private final Client client; private final ScheduledExecutorService scheduledExecutorService; private final ClientThread clientThread; private final ItemClient itemClient = new ItemClient(); private final LoadingCache itemSearches; - private final LoadingCache itemPriceCache; + private final ConcurrentMap itemPrices = new ConcurrentHashMap<>(); private final LoadingCache itemImages; private final LoadingCache itemCompositions; @@ -92,10 +79,7 @@ public class ItemManager this.scheduledExecutorService = executor; this.clientThread = clientThread; - itemPriceCache = CacheBuilder.newBuilder() - .maximumSize(1024L) - .expireAfterAccess(1, TimeUnit.HOURS) - .build(new ItemPriceLoader(executor, itemClient)); + scheduledExecutorService.scheduleWithFixedDelay(this::loadPrices, 0, 30, TimeUnit.MINUTES); itemSearches = CacheBuilder.newBuilder() .maximumSize(512L) @@ -134,6 +118,28 @@ public class ItemManager }); } + private void loadPrices() + { + try + { + ItemPrice[] prices = itemClient.getPrices(); + if (prices != null) + { + itemPrices.clear(); + for (ItemPrice price : prices) + { + itemPrices.put(price.getItem().getId(), price); + } + } + + log.debug("Loaded {} prices", itemPrices.size()); + } + catch (IOException e) + { + log.warn("error loading prices!", e); + } + } + @Subscribe public void onGameStateChanged(final GameStateChanged event) { @@ -144,137 +150,15 @@ public class ItemManager } /** - * Look up an item's price asynchronously. - * - * @param itemId item id - * @return the price, or null if the price is not yet loaded - */ - public ItemPrice getItemPriceAsync(int itemId) - { - itemId = ItemMapping.mapFirst(itemId); - - ItemPrice itemPrice = itemPriceCache.getIfPresent(itemId); - if (itemPrice != null && itemPrice != EMPTY) - { - return itemPrice == NONE ? null : itemPrice; - } - - itemPriceCache.refresh(itemId); - return null; - } - - /** - * Look up an item's price from the price cache - * - * @param itemId - * @return - */ - public ItemPrice getCachedItemPrice(int itemId) - { - itemId = ItemMapping.mapFirst(itemId); - - ItemPrice itemPrice = itemPriceCache.getIfPresent(itemId); - if (itemPrice != null && itemPrice != EMPTY && itemPrice != NONE) - { - return itemPrice; - } - - return null; - } - - /** - * Look up bulk item prices asynchronously - * - * @param itemIds array of item Ids - * @return a future called with the looked up prices - */ - public CompletableFuture getItemPriceBatch(Collection itemIds) - { - final List lookup = new ArrayList<>(); - final List existing = new ArrayList<>(); - for (int itemId : itemIds) - { - for (int mappedItemId : ItemMapping.map(itemId)) - { - ItemPrice itemPrice = itemPriceCache.getIfPresent(mappedItemId); - if (itemPrice != null) - { - existing.add(itemPrice); - } - else - { - lookup.add(mappedItemId); - } - } - } - // All cached? - if (lookup.isEmpty()) - { - return CompletableFuture.completedFuture(existing.toArray(new ItemPrice[existing.size()])); - } - - final CompletableFuture future = new CompletableFuture<>(); - scheduledExecutorService.execute(() -> - { - try - { - // Do a query for the items not in the cache - ItemPrice[] itemPrices = itemClient.lookupItemPrice(lookup.toArray(new Integer[lookup.size()])); - for (int itemId : lookup) - { - itemPriceCache.put(itemId, NONE); - } - if (itemPrices != null) - { - for (ItemPrice itemPrice : itemPrices) - { - itemPriceCache.put(itemPrice.getItem().getId(), itemPrice); - } - // Append these to the already cached items - existing.addAll(Arrays.asList(itemPrices)); - } - future.complete(existing.toArray(new ItemPrice[existing.size()])); - } - catch (Exception ex) - { - // cache unable to lookup - for (int itemId : lookup) - { - itemPriceCache.put(itemId, NONE); - } - - future.completeExceptionally(ex); - } - }); - return future; - } - - /** - * Look up an item's price synchronously + * Look up an item's price * * @param itemId item id * @return item price - * @throws IOException */ - public ItemPrice getItemPrice(int itemId) throws IOException + public ItemPrice getItemPrice(int itemId) { itemId = ItemMapping.mapFirst(itemId); - - ItemPrice itemPrice = itemPriceCache.getIfPresent(itemId); - if (itemPrice != null && itemPrice != EMPTY) - { - return itemPrice == NONE ? null : itemPrice; - } - - itemPrice = itemClient.lookupItemPrice(itemId); - if (itemPrice == null) - { - itemPriceCache.put(itemId, NONE); - return null; - } - - itemPriceCache.put(itemId, itemPrice); - return itemPrice; + return itemPrices.get(itemId); } /** diff --git a/runelite-client/src/main/java/net/runelite/client/game/ItemPriceLoader.java b/runelite-client/src/main/java/net/runelite/client/game/ItemPriceLoader.java deleted file mode 100644 index 87814325bd..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/game/ItemPriceLoader.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2017, Adam - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package net.runelite.client.game; - -import com.google.common.cache.CacheLoader; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListeningExecutorService; -import com.google.common.util.concurrent.MoreExecutors; -import java.io.IOException; -import java.util.concurrent.ScheduledExecutorService; -import lombok.extern.slf4j.Slf4j; -import static net.runelite.client.game.ItemManager.EMPTY; -import static net.runelite.client.game.ItemManager.NONE; -import net.runelite.http.api.item.ItemClient; -import net.runelite.http.api.item.ItemPrice; - -@Slf4j -class ItemPriceLoader extends CacheLoader -{ - private final ListeningExecutorService executorService; - private final ItemClient client; - - ItemPriceLoader(ScheduledExecutorService executor, ItemClient client) - { - this.executorService = MoreExecutors.listeningDecorator(executor); - this.client = client; - } - - @Override - public ItemPrice load(Integer key) throws Exception - { - // guava's Cache doesn't support null values - return EMPTY; - } - - @Override - public ListenableFuture reload(Integer key, ItemPrice oldValue) - { - log.debug("Submitting lookup for item {}", key); - - return executorService.submit(() -> fetch(key)); - } - - private ItemPrice fetch(Integer key) - { - try - { - ItemPrice itemPrice = client.lookupItemPrice(key); - if (itemPrice == null) - { - return NONE; - } - return itemPrice; - } - catch (IOException ex) - { - log.warn("unable to look up item!", ex); - return NONE; - } - } -}; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bankvalue/BankCalculation.java b/runelite-client/src/main/java/net/runelite/client/plugins/bankvalue/BankCalculation.java index 6b514ac883..19a6b6399a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bankvalue/BankCalculation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bankvalue/BankCalculation.java @@ -28,7 +28,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.CompletableFuture; import javax.inject.Inject; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -61,9 +60,6 @@ class BankCalculation @Getter private long haPrice; - @Getter - private boolean finished; - @Inject BankCalculation(QueryRunner queryRunner, ItemManager itemManager, BankValueConfig config) { @@ -87,7 +83,6 @@ class BankCalculation log.debug("Calculating new bank value..."); gePrice = haPrice = 0; - finished = false; List itemIds = new ArrayList<>(); @@ -137,65 +132,32 @@ class BankCalculation // Now do the calculations if (config.showGE() && !itemIds.isEmpty()) { - CompletableFuture future = itemManager.getItemPriceBatch(itemIds); - future.whenComplete((ItemPrice[] itemPrices, Throwable ex) -> + for (WidgetItem widgetItem : widgetItems) { - if (ex != null) + int itemId = widgetItem.getId(); + int quantity = widgetItem.getQuantity(); + + if (itemId <= 0 || quantity == 0 + || itemId == ItemID.COINS_995 || itemId == ItemID.PLATINUM_TOKEN) { - log.debug("Error looking up item prices", ex); - return; + continue; } - if (itemPrices == null) + long price = 0; + for (int mappedItemId : ItemMapping.map(itemId)) { - log.debug("Error looking up item prices"); - return; - } - - log.debug("Price lookup is complete. {} prices.", itemPrices.length); - - try - { - for (WidgetItem widgetItem : widgetItems) + ItemPrice cachedItemPrice = itemManager.getItemPrice(mappedItemId); + if (cachedItemPrice == null) { - int itemId = widgetItem.getId(); - int quantity = widgetItem.getQuantity(); - - if (itemId <= 0 || quantity == 0 - || itemId == ItemID.COINS_995 || itemId == ItemID.PLATINUM_TOKEN) - { - continue; - } - - long price = 0; - for (int mappedItemId : ItemMapping.map(itemId)) - { - ItemPrice cachedItemPrice = itemManager.getCachedItemPrice(mappedItemId); - if (cachedItemPrice == null) - { - // this happens to items which have no ge price - continue; - } - - price += cachedItemPrice.getPrice(); - } - - gePrice += price * quantity; + // this happens to items which have no ge price + continue; } + + price += cachedItemPrice.getPrice(); } - catch (Exception ex2) - { - log.warn("error calculating price", ex2); - } - finally - { - finished = true; - } - }); - } - else - { - finished = true; + + gePrice += price * quantity; + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bankvalue/BankValuePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/bankvalue/BankValuePlugin.java index eab4cb2df7..e4739cfa56 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bankvalue/BankValuePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bankvalue/BankValuePlugin.java @@ -72,10 +72,7 @@ public class BankValuePlugin extends Plugin bankTitle.save(); calculate(widgetBankTitleBar); - if (bankCalculation.isFinished()) - { - bankTitle.update(bankCalculation.getGePrice(), bankCalculation.getHaPrice()); - } + bankTitle.update(bankCalculation.getGePrice(), bankCalculation.getHaPrice()); } private void calculate(Widget bankTitleBar) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barrows/BarrowsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/barrows/BarrowsPlugin.java index ce32ec66cc..c499c955ef 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barrows/BarrowsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barrows/BarrowsPlugin.java @@ -27,11 +27,8 @@ package net.runelite.client.plugins.barrows; import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import com.google.inject.Provides; -import java.util.Arrays; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; import javax.inject.Inject; import lombok.AccessLevel; import lombok.Getter; @@ -113,8 +110,6 @@ public class BarrowsPlugin extends Plugin @Inject private BarrowsConfig config; - private long chestPrice; - @Provides BarrowsConfig provideConfig(ConfigManager configManager) { @@ -215,7 +210,7 @@ public class BarrowsPlugin extends Plugin { ItemContainer barrowsRewardContainer = client.getItemContainer(InventoryID.BARROWS_REWARD); Item[] items = barrowsRewardContainer.getItems(); - chestPrice = 0; + long chestPrice = 0; for (Item item : items) { @@ -225,55 +220,29 @@ public class BarrowsPlugin extends Plugin } } - CompletableFuture future = itemManager.getItemPriceBatch( - Arrays.stream(items).map(Item::getId).collect(Collectors.toList())); - future.whenComplete((ItemPrice[] itemPrices, Throwable ex) -> + for (Item item : items) { - if (ex != null) + ItemPrice cachedItemPrice = itemManager.getItemPrice(item.getId()); + if (cachedItemPrice == null) { - log.debug("Error looking up item prices", ex); - return; + continue; } - if (itemPrices == null) - { - log.debug("Error looking up item prices"); - return; - } + long itemStack = (long) cachedItemPrice.getPrice() * (long) item.getQuantity(); + chestPrice += itemStack; + } - log.debug("Price lookup is complete. {} prices.", itemPrices.length); + final ChatMessageBuilder message = new ChatMessageBuilder() + .append(ChatColorType.HIGHLIGHT) + .append("Your chest is worth around ") + .append(StackFormatter.formatNumber(chestPrice)) + .append(" coins.") + .append(ChatColorType.NORMAL); - try - { - for (Item item : items) - { - ItemPrice cachedItemPrice = itemManager.getCachedItemPrice(item.getId()); - if (cachedItemPrice == null) - { - continue; - } - - long itemStack = (long) cachedItemPrice.getPrice() * (long) item.getQuantity(); - chestPrice += itemStack; - } - - final ChatMessageBuilder message = new ChatMessageBuilder() - .append(ChatColorType.HIGHLIGHT) - .append("Your chest is worth around ") - .append(StackFormatter.formatNumber(chestPrice)) - .append(" coins.") - .append(ChatColorType.NORMAL); - - chatMessageManager.queue(QueuedMessage.builder() - .type(ChatMessageType.EXAMINE_ITEM) - .runeLiteFormattedMessage(message.build()) - .build()); - } - catch (Exception ex2) - { - log.warn("error calculating price", ex2); - } - }); + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.EXAMINE_ITEM) + .runeLiteFormattedMessage(message.build()) + .build()); } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index 809a30a9d4..5592f738d4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java @@ -189,17 +189,7 @@ public class ChatCommandsPlugin extends Plugin } int itemId = item.getId(); - ItemPrice itemPrice; - - try - { - itemPrice = itemManager.getItemPrice(itemId); - } - catch (IOException ex) - { - log.warn("Unable to fetch item price for {}", itemId, ex); - return; - } + ItemPrice itemPrice = itemManager.getItemPrice(itemId); final ChatMessageBuilder builder = new ChatMessageBuilder() .append(ChatColorType.NORMAL) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java index 7ebeec48c3..d8cd6205dd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/examine/ExaminePlugin.java @@ -27,7 +27,6 @@ package net.runelite.client.plugins.examine; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.eventbus.Subscribe; -import java.io.IOException; import java.time.Instant; import java.util.ArrayDeque; import java.util.Deque; @@ -271,14 +270,9 @@ public class ExaminePlugin extends Plugin final boolean note = itemComposition.getNote() != -1; final int id = note ? itemComposition.getLinkedNoteId() : itemComposition.getId(); - ItemPrice itemPrice; - try + ItemPrice itemPrice = itemManager.getItemPrice(id); + if (itemPrice == null) { - itemPrice = itemManager.getItemPrice(id); - } - catch (IOException e) - { - log.warn("Error looking up item price", e); return; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java index dec55a7b29..959b45c280 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeSearchPanel.java @@ -225,16 +225,7 @@ class GrandExchangeSearchPanel extends JPanel continue; } - ItemPrice itemPrice = null; - try - { - itemPrice = itemManager.getItemPrice(itemId); - } - catch (IOException ex) - { - log.warn("Unable to fetch item price for {}", itemId, ex); - } - + ItemPrice itemPrice = itemManager.getItemPrice(itemId); AsyncBufferedImage itemImage = itemManager.getImage(itemId); itemsList.add(new GrandExchangeItems(itemImage, item.getName(), itemId, itemPrice != null ? itemPrice.getPrice() : 0, itemComp.getPrice() * 0.6)); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java index 60ae51cd31..01dec4de24 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java @@ -174,7 +174,7 @@ public class GroundItemsOverlay extends Overlay } // Update GE price for item - final ItemPrice itemPrice = itemManager.getItemPriceAsync(item.getItemId()); + final ItemPrice itemPrice = itemManager.getItemPrice(item.getItemId()); if (itemPrice != null && itemPrice.getPrice() > 0) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java index 527b9ba094..636bfde71d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java @@ -403,7 +403,7 @@ public class GroundItemsPlugin extends Plugin final ItemComposition itemComposition = itemManager.getItemComposition(itemId); final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemComposition.getId(); - final ItemPrice itemPrice = itemManager.getItemPriceAsync(realItemId); + final ItemPrice itemPrice = itemManager.getItemPrice(realItemId); final int price = itemPrice == null ? itemComposition.getPrice() : itemPrice.getPrice(); final int haPrice = Math.round(itemComposition.getPrice() * HIGH_ALCHEMY_CONSTANT) * quantity; final int gePrice = quantity * price; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java index 5406b44119..bda9ec979e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemprices/ItemPricesOverlay.java @@ -198,7 +198,7 @@ class ItemPricesOverlay extends Overlay if (config.showGEPrice()) { - final ItemPrice price = itemManager.getItemPriceAsync(id); + final ItemPrice price = itemManager.getItemPrice(id); if (price != null) { gePrice = price.getPrice();