1package com.runehive.content.tradingpost;
3import com.google.common.collect.Lists;
4import com.google.gson.Gson;
5import com.google.gson.GsonBuilder;
6import com.google.gson.JsonObject;
7import com.google.gson.JsonParser;
8import com.google.gson.reflect.TypeToken;
9import com.hankcs.algorithm.AhoCorasickDoubleArrayTrie;
10import com.runehive.game.task.Task;
11import com.runehive.game.world.World;
12import com.runehive.game.world.entity.mob.player.Player;
13import com.runehive.game.world.items.Item;
14import com.runehive.game.world.items.ItemDefinition;
15import com.runehive.net.packet.out.*;
16import com.runehive.util.GsonUtils;
17import com.runehive.util.Utility;
18import org.slf4j.Logger;
19import org.slf4j.LoggerFactory;
22import java.nio.charset.StandardCharsets;
23import java.nio.file.Files;
24import java.nio.file.Path;
25import java.nio.file.Paths;
26import java.nio.file.StandardCopyOption;
28import java.util.concurrent.CompletableFuture;
29import java.util.function.Consumer;
30import java.util.stream.Collectors;
45 private final AhoCorasickDoubleArrayTrie<String>
acdat =
new AhoCorasickDoubleArrayTrie<>();
50 private static final List<TradingPostListing>
allListings =
new ArrayList<>();
55 private static final Map<String, Coffer>
allCoffers = Collections.synchronizedMap(
new HashMap<>());
60 private List<TradingPostListing>
myListings =
new ArrayList<>();
95 private static final Map<String, List<ItemHistory>>
itemHistories =
new HashMap<>();
129 player.send(new SendString(Utility.formatDigits(coffer.getAmount()), 80005));
154 player.dialogueFactory.sendOption(
"Search item", this::searchExistingItemInput,
"Search player", this::searchPlayerInput,
"Nevermind", () ->
player.dialogueFactory.clear()).execute();
205 if(Objects.equals(input,
"")) {
210 List<ItemHistory> historyItems =
new ArrayList<>();
211 TreeMap<String, String> map =
new TreeMap<>();
212 map.put(input,input);
215 for(Map.Entry<String, List<ItemHistory>> lf :
itemHistories.entrySet()) {
216 acdat.parseText(lf.getKey().toLowerCase(), (b,e,v) -> {
217 List<ItemHistory> temp = lf.getValue();
219 for(ItemHistory h : temp) {
220 if(!historyItems.contains(h)) {
224 if(historyItems.size() == 50) {
259 protected void execute() {
272 protected void execute() {
290 .sorted(Comparator.comparingInt(
294 .collect(Collectors.toList());
327 if (coffer.getAmount() == 0) {
332 int amount = Integer.parseInt(input);
334 if (amount > coffer.getAmount()) {
335 amount = (int) coffer.getAmount();
338 if (amount > coffer.getAmount()) {
343 coffer.subtractAmount(amount);
347 player.send(
new SendMessage(
"You do not have enough inventory spaces to withdraw this gold"));
364 List<TradingPostListing> temp;
375 if(
page == 0)
return;
415 if(search.
text.length() == 0) {
436 List<TradingPostListing> temp =
new ArrayList<>();
437 TreeMap<String, String> map =
new TreeMap<>();
438 map.put(search,search);
442 if(!temp.contains(listing)) {
451 return listings.subList(
455 .filter(tradingPostListing -> !tradingPostListing.getSeller().equalsIgnoreCase(
player.getName()))
457 .collect(Collectors.toList());
493 player.interfaceManager.close();
501 case KING_DONATOR -> {
504 case ELITE_DONATOR -> {
508 case EXTREME_DONATOR -> {
511 case SUPER_DONATOR -> {
578 player.send(
new SendMessage(
"Unable to add listing with max number listings listed."));
600 .filter(tradingPostListing -> tradingPostListing.getSeller().equals(name))
601 .collect(Collectors.toList());
609 : tradingPostListing.getItemId(), tradingPostListing.getAmountLeft()))
610 .toArray(
Item[]::new);
618 : ih.getItemId(), ih.getQuantity()))
619 .toArray(
Item[]::new);
648 if(btnId >= 14878 && btnId <= 14927) {
649 int index = btnId - 14878;
678 if((
long)amount +
listingToBuy.getPrice() > Integer.MAX_VALUE) {
687 int finalAmount = amount;
690 .sendOption(
"Yes", () ->
purchase(finalAmount),
"Nevermind", () ->
player.dialogueFactory.clear()).execute();
693 private void alertSeller(String sellerName, String item,
int amount) {
694 Optional<Player> playerOptional =
World.
search(sellerName);
696 if(playerOptional.isPresent()) {
699 player.send(
new SendMessage(
"@red@Trading post: " + amount +
" of your " + item +
"s have been sold"));
701 player.tradingPost.updateExistingListingsList();
704 player.tradingPost.openOverviewInterface();
717 if(totalPrice > getPlayerCurrencyAmount) {
724 player.send(
new SendMessage(
"Your purchase amount has been lowered to " + amount));
728 player.send(
new SendMessage(
"This item does not exist in the trading post anymore."));
757 player.send(
new SendMessage(
"You do not have enough inventory spaces to complete this transaction"));
765 if(itemHistoryList.size() == 50) {
766 itemHistoryList.remove(49);
769 itemHistoryList.add(itemHistory);
786 coffer.addAmount((
int) (amount - (
TAX_RATE/100.0*amount)));
792 final Consumer<Coffer> useCoffer) {
794 if (coffer ==
null) {
799 useCoffer.accept(coffer);
804 if(btnId >= 14516 && btnId <= 14535) {
805 int index = btnId - 14516;
859 for(
int i = 0; i < 1000; i++) {
867 System.out.println(
"Adding listing: " + addCounter);
873 Thread.startVirtualThread(() -> {
877 final Path path = Path.of(
"./data/profile/save/tradingpost/coffers/", coffer.
getOwner(),
".json");
878 Path parent = path.getParent();
879 if (parent ==
null) {
880 throw new UnsupportedOperationException(
"Path must have a parent " + path);
882 if (!Files.exists(parent)) {
883 parent = Files.createDirectories(parent);
886 final Path tempFile = Files.createTempFile(parent, path.getFileName().toString(),
".tmp");
887 Files.writeString(tempFile, json, StandardCharsets.UTF_8);
889 Files.move(tempFile, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
890 }
catch (
final Exception ex) {
891 logger.error(String.format(
"Error while saving player=%s",
player.getName()), ex);
896 public CompletableFuture<Coffer>
getCoffer(
final String owner) {
898 if (cachedCoffer !=
null) {
899 return CompletableFuture.completedFuture(cachedCoffer);
903 return CompletableFuture.supplyAsync(() -> {
904 final Path path = Path.of(
"./data/profile/save/tradingpost/coffers/", owner,
".json");
905 if (!Files.exists(path)) {
910 try (
final BufferedReader reader = Files.newBufferedReader(path)) {
911 final Coffer coffer = gson.fromJson(reader,
Coffer.class);
912 if (coffer !=
null) {
916 }
catch (
final IOException e) {
917 logger.error(
"Failed reading coffer for \"" + owner +
"\"", e);
923 public void saveListings(
final String owner,
final List<TradingPostListing> listings) {
924 Thread.startVirtualThread(() -> {
928 final Path path = Path.of(
"./data/profile/save/tradingpost/listings/", owner,
".json");
929 Path parent = path.getParent();
930 if (parent ==
null) {
931 throw new UnsupportedOperationException(
"Path must have a parent " + path);
933 if (!Files.exists(parent)) {
934 parent = Files.createDirectories(parent);
937 final Path tempFile = Files.createTempFile(parent, path.getFileName().toString(),
".tmp");
938 Files.writeString(tempFile, json, StandardCharsets.UTF_8);
940 Files.move(tempFile, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
942 }
catch (
final Exception ex) {
943 logger.error(String.format(
"Error while saving player=%s",
player.getName()), ex);
950 File folder =
new File(
"./data/profile/save/tradingpost/listings/");
951 File[] files = folder.listFiles();
952 if (files ==
null)
return;
953 for(File f : files) {
955 if(f.isDirectory()) {
956 toRead = Objects.requireNonNull(f.getAbsoluteFile().listFiles())[0];
959 assert toRead !=
null;
961 try (
final BufferedReader reader = Files.newBufferedReader(Path.of(toRead.getPath()))) {
962 final List<TradingPostListing> listings = List.of(gson.fromJson(reader,
TradingPostListing[].class));
964 }
catch (
final IOException e) {
965 logger.error(
"Failed reading listings", e);
967 }
catch (Exception e) {
971 }
catch (Exception e) {
977 Path path = Paths.get(
"./data/profile/save/tradingpost/itemhistory.json");
978 File file = path.toFile();
979 file.getParentFile().setWritable(
true);
981 if(!file.getParentFile().exists()) {
983 file.getParentFile().mkdirs();
984 }
catch (SecurityException e) {
985 System.out.println(
"Error while creating item history directory");
988 try(FileWriter
writer =
new FileWriter(file)) {
989 Gson builder =
new GsonBuilder().setPrettyPrinting().create();
990 JsonObject
object =
new JsonObject();
992 writer.write(builder.toJson(
object));
993 }
catch (Exception e) {
999 Path path = Paths.get(
"./data/profile/save/tradingpost/itemhistoryrecent.json");
1000 File file = path.toFile();
1001 file.getParentFile().setWritable(
true);
1003 if(!file.getParentFile().exists()) {
1005 file.getParentFile().mkdirs();
1006 }
catch (SecurityException e) {
1007 System.out.println(
"Error while creating recent item history directory");
1010 try(FileWriter
writer =
new FileWriter(file)) {
1011 Gson builder =
new GsonBuilder().setPrettyPrinting().create();
1012 JsonObject
object =
new JsonObject();
1014 writer.write(builder.toJson(
object));
1015 }
catch (Exception e) {
1016 e.printStackTrace();
1021 Path path = Paths.get(
"./data/profile/save/tradingpost/itemhistory.json");
1022 File file = path.toFile();
1025 if(file.length() == 0)
return;
1027 try (FileReader fileReader =
new FileReader(file)) {
1028 JsonParser fileParser =
new JsonParser();
1029 Gson builder =
new GsonBuilder()
1031 JsonObject reader = (JsonObject) fileParser.parse(fileReader);
1033 HashMap<String, List<ItemHistory>> itemHistory = builder.fromJson(reader.get(
"itemhistory"),
1034 new TypeToken<HashMap<String, List<ItemHistory>>>() {
1037 }
catch (Exception e) {
1038 e.printStackTrace();
1043 Path path = Paths.get(
"./data/profile/save/tradingpost/itemhistoryrecent.json");
1044 File file = path.toFile();
1047 if(file.length() == 0)
return;
1049 try (FileReader fileReader =
new FileReader(file)) {
1050 JsonParser fileParser =
new JsonParser();
1051 Gson builder =
new GsonBuilder()
1053 JsonObject reader = (JsonObject) fileParser.parse(fileReader);
1054 ItemHistory[] temp = builder.fromJson(reader.get(
"recentitemhistory").getAsJsonArray(),
ItemHistory[].class);
1056 }
catch (Exception e) {
1057 e.printStackTrace();
1062 if (!file.getParentFile().exists() || !file.exists()) {
1064 file.getParentFile().mkdirs();
1065 file.createNewFile();
1066 }
catch (SecurityException e) {
1067 System.out.println(
"Unable to create directory");
1068 }
catch (IOException e) {
1069 throw new RuntimeException(e);
1082 private final AhoCorasickDoubleArrayTrie<String>
acdat =
new AhoCorasickDoubleArrayTrie<>();
1086 this.text =
text.toLowerCase();
TradingPostActiveListingSearch(TradingPostActiveListingSearchType searchType, String text)
final AhoCorasickDoubleArrayTrie< String > acdat
final TradingPostActiveListingSearchType searchType
TradingPostActiveListingSort(TradingPostSortType postSortType)
TradingPostSortType postSortType
TradingPostActiveListingSort setPostSortType(TradingPostSortType postSortType)
TradingPostSortType getPostSortType()
List< TradingPostListing > searchedListings
The current searched listings.
boolean delist(TradingPostListing listing)
static final int OVERVIEW_INTERFACE_ID
void addSearchResultsAndUpdateInterface(TradingPostActiveListingSearch search)
static final int MAX_LISTING_SIZE
void clearSellingOverlay()
void displayMostRecentListings()
Item[] getItemArrayFromActiveListings(List< TradingPostListing > list)
void searchExistingItemInput()
void purchase(int amount)
static final int BUYING_PAGE_INTERFACE_ID
static final Map< String, Coffer > allCoffers
Hashmap containing the coffers <PlayerName, Coffer>
void addToCoffer(String owner, int amount)
static final int MAX_LISTINGS_SHOWN
boolean handleBuyingButton(int btnId)
static final int CURRENCY_ID
void updatePriceStrings()
void searchExistingListing(TradingPostActiveListingSearch search)
boolean handleDismissListingButton(int btnId)
TradingPostListing listingToBuy
The listing that the player selects in the buying page.
void selectItemToList(Item item)
Item[] getItemArrayFromItemHistory(List< ItemHistory > list)
boolean handleButtonClick(int btnId)
int page
The page # of the buying interface.
static void loadAllListings()
static final List< ItemHistory > recentlySoldItems
contains the last 50 sold items on the trading post
static final int HISTORY_PAGE_INTERFACE_ID
static final List< TradingPostListing > allListings
A list containing all the trading post listings.
void withdrawFromCoffer()
static void saveRecentHistory()
void toggleSellingOverlayVisibilityAndOpen(boolean isHidden)
List< TradingPostListing > getPageListings(List< TradingPostListing > listings)
List< ItemHistory > getPlayersItemHistory()
List< TradingPostListing > myListings
List containing the players existing listings/offers.
void searchItemHistoryInput()
final AhoCorasickDoubleArrayTrie< String > acdat
void confirmToAddListing()
void openOverviewInterface()
void buyingDialogueOptions(int amount)
void alertSeller(String sellerName, String item, int amount)
void getOrMakeNewCofferIfNotExists(final String owner, final Consumer< Coffer > useCoffer)
void openSellingOverlay()
void updateQuantityString()
static void saveAllItemHistory()
void sendItemHistoryData(List< ItemHistory > itemHistories)
void updateExistingListingsWidgets()
TradingPostActiveListingSearch tradingPostSearch
The current item or player search.
List< TradingPostListing > getListingsByName(String name)
static void loadItemHistory()
void updateCofferAmountWidget()
int getSlotSizeByDonatorRank()
static final int TAX_RATE
static final Logger logger
List< TradingPostListing > displayedPageListings
The displayed listings on the buying page.
boolean handleSellingOverlayButtons(int btnId)
static void createFileAndDirIfNotExists(File file)
void editQuantityInputPrompt()
final List< ItemHistory > playersItemHistory
contains last 50 players buying history
void saveListings(final String owner, final List< TradingPostListing > listings)
List< TradingPostListing > getSearchResults(TradingPostActiveListingSearchType type, String search)
TradingPostActiveListingSort tradingPostSort
The current sort of the buyable listings.
void updateExistingListingsList()
void updateBuyingPageWidgets(List< TradingPostListing > listings)
TradingPostListing listingToAdd
The listing that the player will add to their existing listings.
void editPriceInputPrompt()
void sendOverviewWidgetVisibility(int i, boolean isHidden)
void toggleBuyingPageWidgetVisibility(int i, boolean isHidden)
static final Map< String, List< ItemHistory > > itemHistories
Contains a list of the last 50 sold of each item.
void saveCoffer(final Coffer coffer)
void addToItemHistory(ItemHistory itemHistory)
TradingPost(Player player)
static void loadRecentItemHistory()
CompletableFuture< Coffer > getCoffer(final String owner)
void setPrice(Player player, int price)
void setInitialQuantity(int initialQuantity)
A game representing a cyclic unit of work.
Represents the game world.
static void schedule(Task task)
Submits a new event.
static Optional< Player > search(String name)
Gets a player by name.
This class represents a character controlled by a player.
Represents all of an in-game Item's attributes.
boolean isNoted()
Gets the item note state.
static ItemDefinition get(int id)
Gets an item definition.
String getName()
Gets the item name.
static ItemDefinition[] DEFINITIONS
An array of item definitions.
The container class that represents an item that can be interacted with.
final int getId()
Gets the identification of this item.
ItemDefinition getDefinition()
Gets the item definition for the item identifier.
The OutgoingPacket that opens the inventory with itemcontainer.
The OutgoingPacket that sends a message to a Players chatbox in the client.
Handles sending the progress bar data to the client.
The OutgoingPacket that sends a string to a Players itemcontainer in the client.
Handles miscellaneous methods.
static String formatDigits(final int amount)
Formats digits for integers.
static final ThreadLocal< Gson > JSON_PRETTY_ALLOW_NULL
static final ThreadLocal< Gson > JSON_ALLOW_NULL