Merge pull request #5866 from Adam-/item-search
Move item search client side
This commit is contained in:
@@ -30,7 +30,8 @@ import lombok.Data;
|
||||
@Data
|
||||
public class ItemPrice
|
||||
{
|
||||
private Item item;
|
||||
private int id;
|
||||
private String name;
|
||||
private int price;
|
||||
private Instant time;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -50,7 +49,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RequestMapping("/item")
|
||||
public class ItemController
|
||||
{
|
||||
private static final Duration CACHE_DUATION = Duration.ofMinutes(30);
|
||||
private static final String RUNELITE_CACHE = "RuneLite-Cache";
|
||||
private static final int MAX_BATCH_LOOKUP = 1024;
|
||||
|
||||
@@ -70,11 +68,9 @@ public class ItemController
|
||||
memorizedPrices = Suppliers.memoizeWithExpiration(() -> itemService.fetchPrices().stream()
|
||||
.map(priceEntry ->
|
||||
{
|
||||
Item item = new Item();
|
||||
item.setId(priceEntry.getItem()); // fake item
|
||||
|
||||
ItemPrice itemPrice = new ItemPrice();
|
||||
itemPrice.setItem(item);
|
||||
itemPrice.setId(priceEntry.getItem());
|
||||
itemPrice.setName(priceEntry.getName());
|
||||
itemPrice.setPrice(priceEntry.getPrice());
|
||||
itemPrice.setTime(priceEntry.getTime());
|
||||
return itemPrice;
|
||||
@@ -173,7 +169,8 @@ public class ItemController
|
||||
}
|
||||
|
||||
ItemPrice itemPrice = new ItemPrice();
|
||||
itemPrice.setItem(item.toItem());
|
||||
itemPrice.setId(item.getId());
|
||||
itemPrice.setName(item.getName());
|
||||
itemPrice.setPrice(priceEntry.getPrice());
|
||||
itemPrice.setTime(priceEntry.getTime());
|
||||
|
||||
@@ -209,11 +206,9 @@ public class ItemController
|
||||
return prices.stream()
|
||||
.map(priceEntry ->
|
||||
{
|
||||
Item item = new Item();
|
||||
item.setId(priceEntry.getItem()); // fake item
|
||||
|
||||
ItemPrice itemPrice = new ItemPrice();
|
||||
itemPrice.setItem(item);
|
||||
itemPrice.setId(priceEntry.getItem());
|
||||
itemPrice.setName(priceEntry.getName());
|
||||
itemPrice.setPrice(priceEntry.getPrice());
|
||||
itemPrice.setTime(priceEntry.getTime());
|
||||
return itemPrice;
|
||||
|
||||
@@ -30,7 +30,7 @@ import net.runelite.http.api.item.Item;
|
||||
import net.runelite.http.api.item.ItemType;
|
||||
|
||||
@Data
|
||||
public class ItemEntry
|
||||
class ItemEntry
|
||||
{
|
||||
private int id;
|
||||
private String name;
|
||||
|
||||
@@ -124,14 +124,14 @@ public class ItemService
|
||||
{
|
||||
if (time != null)
|
||||
{
|
||||
return con.createQuery("select item, price, time, fetched_time from prices where item = :item and time <= :time order by time desc limit 1")
|
||||
return con.createQuery("select item, name, price, time, fetched_time from prices t1 join items t2 on t1.item=t2.id where item = :item and time <= :time order by time desc limit 1")
|
||||
.addParameter("item", itemId)
|
||||
.addParameter("time", time.toString())
|
||||
.executeAndFetchFirst(PriceEntry.class);
|
||||
}
|
||||
else
|
||||
{
|
||||
return con.createQuery("select item, price, time, fetched_time from prices where item = :item order by time desc limit 1")
|
||||
return con.createQuery("select item, name, price, time, fetched_time from prices t1 join items t2 on t1.item=t2.id where item = :item order by time desc limit 1")
|
||||
.addParameter("item", itemId)
|
||||
.executeAndFetchFirst(PriceEntry.class);
|
||||
}
|
||||
@@ -295,9 +295,10 @@ public class ItemService
|
||||
{
|
||||
try (Connection con = sql2o.beginTransaction())
|
||||
{
|
||||
Query query = con.createQuery("select t2.item, t2.time, prices.price, prices.fetched_time from (select t1.item as item, max(t1.time) as time from prices t1 group by item) t2 join prices on t2.item=prices.item and t2.time=prices.time");
|
||||
List<PriceEntry> entries = query.executeAndFetch(PriceEntry.class);
|
||||
return entries;
|
||||
Query query = con.createQuery("select t2.item, t3.name, t2.time, prices.price, prices.fetched_time from (select t1.item as item, max(t1.time) as time from prices t1 group by item) t2 " +
|
||||
" join prices on t2.item=prices.item and t2.time=prices.time" +
|
||||
" join items t3 on t2.item=t3.id");
|
||||
return query.executeAndFetch(PriceEntry.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,10 @@ import java.time.Instant;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class PriceEntry
|
||||
class PriceEntry
|
||||
{
|
||||
private int item;
|
||||
private String name;
|
||||
private int price;
|
||||
private Instant time;
|
||||
private Instant fetched_time;
|
||||
|
||||
@@ -32,7 +32,9 @@ import com.google.common.eventbus.Subscribe;
|
||||
import java.awt.Color;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
@@ -51,7 +53,6 @@ import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.http.api.item.ItemClient;
|
||||
import net.runelite.http.api.item.ItemPrice;
|
||||
import net.runelite.http.api.item.SearchResult;
|
||||
|
||||
@Singleton
|
||||
@Slf4j
|
||||
@@ -78,7 +79,6 @@ public class ItemManager
|
||||
private final ClientThread clientThread;
|
||||
|
||||
private final ItemClient itemClient = new ItemClient();
|
||||
private final LoadingCache<String, SearchResult> itemSearches;
|
||||
private Map<Integer, ItemPrice> itemPrices = Collections.emptyMap();
|
||||
private final LoadingCache<ImageKey, AsyncBufferedImage> itemImages;
|
||||
private final LoadingCache<Integer, ItemComposition> itemCompositions;
|
||||
@@ -93,18 +93,6 @@ public class ItemManager
|
||||
|
||||
scheduledExecutorService.scheduleWithFixedDelay(this::loadPrices, 0, 30, TimeUnit.MINUTES);
|
||||
|
||||
itemSearches = CacheBuilder.newBuilder()
|
||||
.maximumSize(512L)
|
||||
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||
.build(new CacheLoader<String, SearchResult>()
|
||||
{
|
||||
@Override
|
||||
public SearchResult load(String key) throws Exception
|
||||
{
|
||||
return itemClient.search(key);
|
||||
}
|
||||
});
|
||||
|
||||
itemImages = CacheBuilder.newBuilder()
|
||||
.maximumSize(128L)
|
||||
.expireAfterAccess(1, TimeUnit.HOURS)
|
||||
@@ -152,7 +140,7 @@ public class ItemManager
|
||||
ImmutableMap.Builder<Integer, ItemPrice> map = ImmutableMap.builderWithExpectedSize(prices.length);
|
||||
for (ItemPrice price : prices)
|
||||
{
|
||||
map.put(price.getItem().getId(), price);
|
||||
map.put(price.getId(), price);
|
||||
}
|
||||
itemPrices = map.build();
|
||||
}
|
||||
@@ -205,16 +193,25 @@ public class ItemManager
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up an item's composition
|
||||
* Search for tradeable items based on item name
|
||||
*
|
||||
* @param itemName item name
|
||||
* @return item search result
|
||||
* @throws java.util.concurrent.ExecutionException exception when item
|
||||
* is not found
|
||||
* @return
|
||||
*/
|
||||
public SearchResult searchForItem(String itemName) throws ExecutionException
|
||||
public List<ItemPrice> search(String itemName)
|
||||
{
|
||||
return itemSearches.get(itemName);
|
||||
itemName = itemName.toLowerCase();
|
||||
|
||||
List<ItemPrice> result = new ArrayList<>();
|
||||
for (ItemPrice itemPrice : itemPrices.values())
|
||||
{
|
||||
final String name = itemPrice.getName();
|
||||
if (name.toLowerCase().contains(itemName))
|
||||
{
|
||||
result.add(itemPrice);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Provides;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -69,8 +68,7 @@ import net.runelite.http.api.hiscore.HiscoreResult;
|
||||
import net.runelite.http.api.hiscore.HiscoreSkill;
|
||||
import net.runelite.http.api.hiscore.SingleHiscoreSkillResult;
|
||||
import net.runelite.http.api.hiscore.Skill;
|
||||
import net.runelite.http.api.item.Item;
|
||||
import net.runelite.http.api.item.SearchResult;
|
||||
import net.runelite.http.api.item.ItemPrice;
|
||||
import net.runelite.http.api.kc.KillCountClient;
|
||||
|
||||
@PluginDescriptor(
|
||||
@@ -205,7 +203,7 @@ public class ChatCommandsPlugin extends Plugin implements ChatboxInputListener
|
||||
String search = message.substring(PRICE_COMMAND_STRING.length() + 1);
|
||||
|
||||
log.debug("Running price lookup for {}", search);
|
||||
executor.submit(() -> itemPriceLookup(setMessage.getMessageNode(), search));
|
||||
itemPriceLookup(setMessage.getMessageNode(), search);
|
||||
}
|
||||
else if (config.lvl() && message.toLowerCase().startsWith(LEVEL_COMMAND_STRING + " "))
|
||||
{
|
||||
@@ -457,29 +455,14 @@ public class ChatCommandsPlugin extends Plugin implements ChatboxInputListener
|
||||
*/
|
||||
private void itemPriceLookup(MessageNode messageNode, String search)
|
||||
{
|
||||
SearchResult result;
|
||||
List<ItemPrice> results = itemManager.search(search);
|
||||
|
||||
try
|
||||
if (!results.isEmpty())
|
||||
{
|
||||
result = itemManager.searchForItem(search);
|
||||
}
|
||||
catch (ExecutionException ex)
|
||||
{
|
||||
log.warn("Unable to search for item {}", search, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (result != null && !result.getItems().isEmpty())
|
||||
{
|
||||
Item item = retrieveFromList(result.getItems(), search);
|
||||
if (item == null)
|
||||
{
|
||||
log.debug("Unable to find item {} in result {}", search, result);
|
||||
return;
|
||||
}
|
||||
ItemPrice item = retrieveFromList(results, search);
|
||||
|
||||
int itemId = item.getId();
|
||||
int itemPrice = itemManager.getItemPrice(itemId);
|
||||
int itemPrice = item.getPrice();
|
||||
|
||||
final ChatMessageBuilder builder = new ChatMessageBuilder()
|
||||
.append(ChatColorType.NORMAL)
|
||||
@@ -767,23 +750,31 @@ public class ChatCommandsPlugin extends Plugin implements ChatboxInputListener
|
||||
|
||||
/**
|
||||
* Compares the names of the items in the list with the original input.
|
||||
* Returns the item if its name is equal to the original input or null
|
||||
* if it can't find the item.
|
||||
* Returns the item if its name is equal to the original input or the
|
||||
* shortest match if no exact match is found.
|
||||
*
|
||||
* @param items List of items.
|
||||
* @param originalInput String with the original input.
|
||||
* @return Item which has a name equal to the original input.
|
||||
*/
|
||||
private Item retrieveFromList(List<Item> items, String originalInput)
|
||||
private ItemPrice retrieveFromList(List<ItemPrice> items, String originalInput)
|
||||
{
|
||||
for (Item item : items)
|
||||
ItemPrice shortest = null;
|
||||
for (ItemPrice item : items)
|
||||
{
|
||||
if (item.getName().toLowerCase().equals(originalInput.toLowerCase()))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
if (shortest == null || item.getName().length() < shortest.getName().length())
|
||||
{
|
||||
shortest = item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
// Take a guess
|
||||
return shortest;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,8 +50,7 @@ import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.components.IconTextField;
|
||||
import net.runelite.client.ui.components.PluginErrorPanel;
|
||||
import net.runelite.client.util.RunnableExceptionLogger;
|
||||
import net.runelite.http.api.item.Item;
|
||||
import net.runelite.http.api.item.SearchResult;
|
||||
import net.runelite.http.api.item.ItemPrice;
|
||||
|
||||
/**
|
||||
* This panel holds the search section of the Grand Exchange Plugin.
|
||||
@@ -169,19 +168,13 @@ class GrandExchangeSearchPanel extends JPanel
|
||||
searchBar.setEditable(false);
|
||||
searchBar.setIcon(IconTextField.Icon.LOADING);
|
||||
|
||||
SearchResult result;
|
||||
|
||||
try
|
||||
List<ItemPrice> result = itemManager.search(lookup);
|
||||
if (result.isEmpty())
|
||||
{
|
||||
result = itemManager.searchForItem(lookup);
|
||||
}
|
||||
catch (Exception ex) // handle com.google.common.cache.CacheLoader$InvalidCacheLoadException
|
||||
{
|
||||
log.warn("Unable to search for item {}", lookup, ex);
|
||||
searchBar.setIcon(IconTextField.Icon.ERROR);
|
||||
searchBar.setEditable(true);
|
||||
errorPanel.setContent("Error fetching results", "An error occurred while trying to fetch item data, please try again later.");
|
||||
errorPanel.setContent("No results found.", "No items were found with that name, please try again.");
|
||||
cardLayout.show(centerPanel, ERROR_PANEL);
|
||||
searchBar.setEditable(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -189,42 +182,33 @@ class GrandExchangeSearchPanel extends JPanel
|
||||
clientThread.invokeLater(() -> processResult(result, lookup, exactMatch));
|
||||
}
|
||||
|
||||
private void processResult(SearchResult result, String lookup, boolean exactMatch)
|
||||
private void processResult(List<ItemPrice> result, String lookup, boolean exactMatch)
|
||||
{
|
||||
itemsList.clear();
|
||||
|
||||
if (result != null && !result.getItems().isEmpty())
|
||||
{
|
||||
cardLayout.show(centerPanel, RESULTS_PANEL);
|
||||
cardLayout.show(centerPanel, RESULTS_PANEL);
|
||||
|
||||
for (Item item : result.getItems())
|
||||
for (ItemPrice item : result)
|
||||
{
|
||||
int itemId = item.getId();
|
||||
|
||||
ItemComposition itemComp = itemManager.getItemComposition(itemId);
|
||||
if (itemComp == null)
|
||||
{
|
||||
int itemId = item.getId();
|
||||
|
||||
ItemComposition itemComp = itemManager.getItemComposition(itemId);
|
||||
if (itemComp == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int itemPrice = itemManager.getItemPrice(itemId);
|
||||
int itemLimit = itemGELimits.getOrDefault(itemId, 0);
|
||||
AsyncBufferedImage itemImage = itemManager.getImage(itemId);
|
||||
|
||||
itemsList.add(new GrandExchangeItems(itemImage, item.getName(), itemId, itemPrice, itemComp.getPrice() * 0.6, itemLimit));
|
||||
|
||||
// If using hotkey to lookup item, stop after finding match.
|
||||
if (exactMatch && item.getName().equalsIgnoreCase(lookup))
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
int itemPrice = item.getPrice();
|
||||
int itemLimit = itemGELimits.getOrDefault(itemId, 0);
|
||||
AsyncBufferedImage itemImage = itemManager.getImage(itemId);
|
||||
|
||||
itemsList.add(new GrandExchangeItems(itemImage, item.getName(), itemId, itemPrice, itemComp.getPrice() * 0.6, itemLimit));
|
||||
|
||||
// If using hotkey to lookup item, stop after finding match.
|
||||
if (exactMatch && item.getName().equalsIgnoreCase(lookup))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
searchBar.setIcon(IconTextField.Icon.ERROR);
|
||||
errorPanel.setContent("No results found.", "No items were found with that name, please try again.");
|
||||
cardLayout.show(centerPanel, ERROR_PANEL);
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() ->
|
||||
|
||||
Reference in New Issue
Block a user