item manager: add batch item price lookup

This commit is contained in:
Adam
2018-03-18 11:32:48 -04:00
parent c12dc9b6c7
commit adc5fe2b26

View File

@@ -29,6 +29,10 @@ import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache; import com.google.common.cache.LoadingCache;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -36,6 +40,7 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.Value; import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import static net.runelite.api.Constants.CLIENT_DEFAULT_ZOOM; import static net.runelite.api.Constants.CLIENT_DEFAULT_ZOOM;
import net.runelite.api.ItemComposition; import net.runelite.api.ItemComposition;
@@ -45,6 +50,7 @@ import net.runelite.http.api.item.ItemPrice;
import net.runelite.http.api.item.SearchResult; import net.runelite.http.api.item.SearchResult;
@Singleton @Singleton
@Slf4j
public class ItemManager public class ItemManager
{ {
@Value @Value
@@ -58,17 +64,19 @@ public class ItemManager
/** /**
* not yet looked up * not yet looked up
*/ */
static final ItemPrice EMPTY = new ItemPrice(); static final ItemPrice EMPTY = new ItemPrice();
/** /**
* has no price * has no price
*/ */
static final ItemPrice NONE = new ItemPrice(); static final ItemPrice NONE = new ItemPrice();
private final Client client; private final Client client;
private final ScheduledExecutorService scheduledExecutorService;
private final ItemClient itemClient = new ItemClient(); private final ItemClient itemClient = new ItemClient();
private final LoadingCache<String, SearchResult> itemSearches; private final LoadingCache<String, SearchResult> itemSearches;
private final LoadingCache<Integer, ItemPrice> itemPrices; private final LoadingCache<Integer, ItemPrice> itemPriceCache;
private final LoadingCache<ImageKey, BufferedImage> itemImages; private final LoadingCache<ImageKey, BufferedImage> itemImages;
private final LoadingCache<Integer, ItemComposition> itemCompositions; private final LoadingCache<Integer, ItemComposition> itemCompositions;
@@ -76,8 +84,10 @@ public class ItemManager
public ItemManager(@Nullable Client client, ScheduledExecutorService executor) public ItemManager(@Nullable Client client, ScheduledExecutorService executor)
{ {
this.client = client; this.client = client;
itemPrices = CacheBuilder.newBuilder() this.scheduledExecutorService = executor;
.maximumSize(512L)
itemPriceCache = CacheBuilder.newBuilder()
.maximumSize(1024L)
.expireAfterAccess(1, TimeUnit.HOURS) .expireAfterAccess(1, TimeUnit.HOURS)
.build(new ItemPriceLoader(executor, itemClient)); .build(new ItemPriceLoader(executor, itemClient));
@@ -126,16 +136,74 @@ public class ItemManager
*/ */
public ItemPrice getItemPriceAsync(int itemId) public ItemPrice getItemPriceAsync(int itemId)
{ {
ItemPrice itemPrice = itemPrices.getIfPresent(itemId); ItemPrice itemPrice = itemPriceCache.getIfPresent(itemId);
if (itemPrice != null && itemPrice != EMPTY) if (itemPrice != null && itemPrice != EMPTY)
{ {
return itemPrice == NONE ? null : itemPrice; return itemPrice == NONE ? null : itemPrice;
} }
itemPrices.refresh(itemId); itemPriceCache.refresh(itemId);
return null; 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<ItemPrice[]> getItemPriceBatch(List<Integer> itemIds)
{
final List<Integer> lookup = new ArrayList<>();
final List<ItemPrice> existing = new ArrayList<>();
for (int itemId : itemIds)
{
ItemPrice itemPrice = itemPriceCache.getIfPresent(itemId);
if (itemPrice != null)
{
existing.add(itemPrice);
}
else
{
lookup.add(itemId);
}
}
// All cached?
if (lookup.isEmpty())
{
return CompletableFuture.completedFuture(existing.toArray(new ItemPrice[existing.size()]));
}
final CompletableFuture<ItemPrice[]> 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()]));
if (itemPrices != null)
{
for (int itemId : lookup)
{
itemPriceCache.put(itemId, NONE);
}
for (ItemPrice itemPrice : itemPrices)
{
itemPriceCache.put(itemPrice.getItem().getId(), itemPrice);
}
// Append these to the already cached items
Arrays.stream(itemPrices).forEach(existing::add);
}
future.complete(existing.toArray(new ItemPrice[existing.size()]));
}
catch (Exception ex)
{
future.completeExceptionally(ex);
}
});
return future;
}
/** /**
* Look up an item's price synchronously * Look up an item's price synchronously
* *
@@ -145,7 +213,7 @@ public class ItemManager
*/ */
public ItemPrice getItemPrice(int itemId) throws IOException public ItemPrice getItemPrice(int itemId) throws IOException
{ {
ItemPrice itemPrice = itemPrices.getIfPresent(itemId); ItemPrice itemPrice = itemPriceCache.getIfPresent(itemId);
if (itemPrice != null && itemPrice != EMPTY) if (itemPrice != null && itemPrice != EMPTY)
{ {
return itemPrice == NONE ? null : itemPrice; return itemPrice == NONE ? null : itemPrice;
@@ -154,11 +222,11 @@ public class ItemManager
itemPrice = itemClient.lookupItemPrice(itemId); itemPrice = itemClient.lookupItemPrice(itemId);
if (itemPrice == null) if (itemPrice == null)
{ {
itemPrices.put(itemId, NONE); itemPriceCache.put(itemId, NONE);
return null; return null;
} }
itemPrices.put(itemId, itemPrice); itemPriceCache.put(itemId, itemPrice);
return itemPrice; return itemPrice;
} }