Merge pull request #5866 from Adam-/item-search

Move item search client side
This commit is contained in:
Adam
2018-10-08 18:37:29 -04:00
committed by GitHub
8 changed files with 80 additions and 110 deletions

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;
}
/**

View File

@@ -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;
}
/**

View File

@@ -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(() ->