RuneHive-Tarnish
Neural OSRS Enhancement Framework
Loading...
Searching...
No Matches
TradingPost.java
1package com.osroyale.content.tradingpost;
2
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.osroyale.game.task.Task;
11import com.osroyale.game.world.World;
12import com.osroyale.game.world.entity.mob.player.Player;
13import com.osroyale.game.world.items.Item;
14import com.osroyale.game.world.items.ItemDefinition;
15import com.osroyale.net.packet.out.*;
16import com.osroyale.util.GsonUtils;
17import com.osroyale.util.Utility;
18import org.slf4j.Logger;
19import org.slf4j.LoggerFactory;
20
21import java.io.*;
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;
27import java.util.*;
28import java.util.concurrent.CompletableFuture;
29import java.util.function.Consumer;
30import java.util.stream.Collectors;
31
32//@Todo history, tax
73
74public class TradingPost {
75
76 private static final Logger logger = LoggerFactory.getLogger(TradingPost.class);
77
78 private static final int OVERVIEW_INTERFACE_ID = 80000;
79 public static final int BUYING_PAGE_INTERFACE_ID = 80174;
80 private static final int HISTORY_PAGE_INTERFACE_ID = 80616;
81 private static final int MAX_LISTINGS_SHOWN = 50;
82 private static final int MAX_LISTING_SIZE = 20;
83 private static final int CURRENCY_ID = 995;
84 private static final int TAX_RATE = 10;
85
86 private final AhoCorasickDoubleArrayTrie<String> acdat = new AhoCorasickDoubleArrayTrie<>();
87
91 private static final List<TradingPostListing> allListings = new ArrayList<>();
92
96 private static final Map<String, Coffer> allCoffers = Collections.synchronizedMap(new HashMap<>());
97
101 private List<TradingPostListing> myListings = new ArrayList<>();
102
106 private TradingPostListing listingToAdd;
107
111 private TradingPostListing listingToBuy;
112
116 private TradingPostActiveListingSearch tradingPostSearch;
117
121 private TradingPostActiveListingSort tradingPostSort;
122
126 private List<TradingPostListing> searchedListings = new ArrayList<>();
127
131 private List<TradingPostListing> displayedPageListings = new ArrayList<>();
132
136 private static final Map<String, List<ItemHistory>> itemHistories = new HashMap<>();
137
141 private static final List<ItemHistory> recentlySoldItems = new ArrayList<>();
142
146 private final List<ItemHistory> playersItemHistory = new ArrayList<>();
147
151 private int page;
152
153 private final Player player;
154
155 public TradingPost(Player player) {
156 this.player = player;
157 }
158
159 public void openOverviewInterface() {
160 cleanUp();
161 updateExistingListingsList();
162 updateExistingListingsWidgets();
163 updateCofferAmountWidget();
164 toggleSellingOverlayVisibilityAndOpen(true);
165 player.interfaceManager.open(OVERVIEW_INTERFACE_ID);
166 }
167
168 public void updateCofferAmountWidget() {
169 getOrMakeNewCofferIfNotExists(player.getName(), coffer -> {
170 player.send(new SendString(Utility.formatDigits(coffer.getAmount()), 80005));
171 });
172 }
173
174 public boolean handleButtonClick(int btnId) {
175 if(!player.interfaceManager.hasAnyOpen(OVERVIEW_INTERFACE_ID, BUYING_PAGE_INTERFACE_ID,HISTORY_PAGE_INTERFACE_ID)) {
176 return false;
177 }
178
179 switch (btnId) {
180 case 14650:
181 case 15418:
182 openOverviewInterface();
183 return true;
184 case 15091:
185 case 14597:
186 searchItemHistoryInput();
187 return true;
188 case 14600:
189 searchPlayerInput();
190 return true;
191 case 14603:
192 displayMostRecentListings();
193 return true;
194 case 14662:
195 player.dialogueFactory.sendOption("Search item", this::searchExistingItemInput, "Search player", this::searchPlayerInput, "Nevermind", () -> player.dialogueFactory.clear()).execute();
196 return true;
197 case 14474:
198 searchExistingItemInput();
199 return true;
200 case 14477:
201 openSellingOverlay();
202 return true;
203 case 14484:
204 sendItemHistoryData(playersItemHistory);
205 return true;
206 case 14487:
207 sendItemHistoryData(recentlySoldItems);
208 return true;
209 case 14614:
210 toggleSellingOverlayVisibilityAndOpen(true);
211 return true;
212 case 14659:
213 nextPage();
214 return true;
215 case 14653:
216 previousPage();
217 return true;
218 case 14656:
219 refresh();
220 return true;
221 case 14466:
222 withdrawFromCoffer();
223 return true;
224 case 14649:
225 sortPrice();
226 return true;
227 case 14648:
228 sortQuantity();
229 return true;
230 default: {
231 /*
232 * Returns true or false based on button ids
233 */
234 if(handleBuyingButton(btnId) || handleDismissListingButton(btnId) || handleSellingOverlayButtons(btnId)) {
235 return true;
236 }
237 }
238 }
239
240 return false;
241 }
242
243 private void searchItemHistoryInput() {
244 player.send(new SendInputMessage("Enter item name to search history for:", 16, input -> {
245
246 if(Objects.equals(input, "")) {
247 sendItemHistoryData(recentlySoldItems);
248 return;
249 }
250
251 List<ItemHistory> historyItems = new ArrayList<>();
252 TreeMap<String, String> map = new TreeMap<>();
253 map.put(input,input);
254 acdat.build(map);
255
256 for(Map.Entry<String, List<ItemHistory>> lf : itemHistories.entrySet()) {
257 acdat.parseText(lf.getKey().toLowerCase(), (b,e,v) -> {
258 List<ItemHistory> temp = lf.getValue();
259
260 for(ItemHistory h : temp) {
261 if(!historyItems.contains(h)) {
262 historyItems.add(h);
263 }
264
265 if(historyItems.size() == 50) {
266 break;
267 }
268
269 }
270 });
271 }
272
273 sendItemHistoryData(historyItems);
274 }));
275 }
276
277 public void sendItemHistoryData(List<ItemHistory> itemHistories) {
278 for(int i = 0; i < MAX_LISTINGS_SHOWN; i++) {
279 if(itemHistories.size() > i) {
280 ItemHistory ih = itemHistories.get(i);
281 player.send(new SendString(ItemDefinition.get(ih.getItemId()).getName(),80753+i));
282 player.send(new SendString(Utility.formatDigits(ih.getPrice()),80803+i));
283 player.send(new SendString(ih.getSeller(),80853+i));
284 player.send(new SendString(ih.getBuyer(),80903+i));
285 } else {
286 player.send(new SendString("",80753+i));
287 player.send(new SendString("",80803+i));
288 player.send(new SendString("",80853+i));
289 player.send(new SendString("",80903+i));
290 }
291 }
292
293 player.send(new SendItemOnInterface(80953, getItemArrayFromItemHistory(itemHistories)));
294 player.interfaceManager.open(HISTORY_PAGE_INTERFACE_ID);
295 }
296
297 private void searchExistingItemInput() {
298 World.schedule(new Task(1) { // task added so input prompt shows after dialogue
299 @Override
300 protected void execute() {
301 player.send(new SendInputMessage("Enter item name to search for:", 16, input -> {
302 searchExistingListing(new TradingPostActiveListingSearch(TradingPostActiveListingSearchType.ITEM, String.valueOf(input)));
303 player.interfaceManager.open(BUYING_PAGE_INTERFACE_ID);
304 }));
305 this.cancel();
306 }
307 });
308 }
309
310 private void searchPlayerInput() {
311 World.schedule(new Task(1) { // task added so input prompt shows after dialogue
312 @Override
313 protected void execute() {
314 player.send(new SendInputMessage("Enter player name to search for:", 16, input -> {
315 searchExistingListing(new TradingPostActiveListingSearch(TradingPostActiveListingSearchType.PLAYER, String.valueOf(input)));
316 player.interfaceManager.open(BUYING_PAGE_INTERFACE_ID);
317 }));
318 this.cancel();
319 }
320 });
321 }
322
323 private void sort() {
324 if(displayedPageListings == null) return;
325 if(tradingPostSort == null) return;
326
327 page = 0;
328
329 searchedListings = searchedListings
330 .stream()
331 .sorted(Comparator.comparingInt(
332 (tradingPostSort.postSortType == TradingPostSortType.HIGHEST_PRICE || tradingPostSort.postSortType == TradingPostSortType.LOWEST_PRICE)
333 ? TradingPostListing::getPrice
334 : TradingPostListing::getAmountLeft))
335 .collect(Collectors.toList());
336
337
338 if(tradingPostSort.postSortType == TradingPostSortType.HIGHEST_QUANTITY || tradingPostSort.postSortType == TradingPostSortType.HIGHEST_PRICE) {
339 Collections.reverse(searchedListings);
340 }
341
342 updateBuyingPageWidgets(getPageListings(searchedListings));
343 }
344
345 private void sortPrice() {
346 tradingPostSort = tradingPostSort != null ? tradingPostSort.setPostSortType(
347 tradingPostSort.postSortType == TradingPostSortType.HIGHEST_PRICE
348 ? TradingPostSortType.LOWEST_PRICE
349 : TradingPostSortType.HIGHEST_PRICE
350 ) : new TradingPostActiveListingSort(TradingPostSortType.LOWEST_PRICE);
351
352 sort();
353 }
354
355 public void sortQuantity() {
356 tradingPostSort = tradingPostSort != null ? tradingPostSort.setPostSortType(
357 tradingPostSort.postSortType == TradingPostSortType.HIGHEST_QUANTITY
358 ? TradingPostSortType.LOWEST_QUANTITY
359 : TradingPostSortType.HIGHEST_QUANTITY
360 ) : new TradingPostActiveListingSort(TradingPostSortType.HIGHEST_QUANTITY);
361
362 sort();
363 }
364
365
366 private void withdrawFromCoffer() {
367 getOrMakeNewCofferIfNotExists(player.getName(), coffer -> {
368 if (coffer.getAmount() == 0) {
369 return;
370 }
371
372 player.send(new SendInputAmount("How much gold would you like to withdraw?", 10, input -> {
373 int amount = Integer.parseInt(input);
374
375 if (amount > coffer.getAmount()) {
376 amount = (int) coffer.getAmount();
377 }
378
379 if (amount > coffer.getAmount()) {
380 return;
381 }
382
383 if (player.inventory.add(CURRENCY_ID, amount)) {
384 coffer.subtractAmount(amount);
385 updateCofferAmountWidget();
386 saveCoffer(coffer);
387 } else {
388 player.send(new SendMessage("You do not have enough inventory spaces to withdraw this gold"));
389 }
390
391 }));
392 });
393 }
394
395 private void refresh() {
396 if(tradingPostSearch != null) {
397 addSearchResultsAndUpdateInterface(tradingPostSearch);
398 } else {
399 displayMostRecentListings();
400 }
401 }
402
403 private void nextPage() {
404 page++;
405 List<TradingPostListing> temp;
406 temp = getPageListings(searchedListings);
407 if(temp.isEmpty()) {
408 page--;
409 player.send(new SendMessage("No more results on the next page"));
410 } else {
411 updateBuyingPageWidgets(temp);
412 }
413 }
414
415 private void previousPage() {
416 if(page == 0) return;
417 page--;
418 updateBuyingPageWidgets(getPageListings(searchedListings));
419 }
420
421 private boolean handleSellingOverlayButtons(int btnId) {
422 if(listingToAdd == null) return false;
423
424 switch (btnId) {
425 case 14618:
426 listingToAdd.addQuantity(player,1);
427 return true;
428 case 14621:
429 listingToAdd.removeQuantity(player,1);
430 return true;
431 case 14636:
432 editQuantityInputPrompt();
433 return true;
434 case 14628:
435 editPriceInputPrompt();
436 return true;
437 case 14631:
438 confirmToAddListing();
439 return true;
440 }
441
442 return false;
443 }
444
445 private void displayMostRecentListings() {
446 if(!player.interfaceManager.isInterfaceOpen(BUYING_PAGE_INTERFACE_ID)) {
447 player.interfaceManager.open(BUYING_PAGE_INTERFACE_ID);
448 }
449 player.send(new SendString("Search: Most recent",80176));
450
451 tradingPostSearch = new TradingPostActiveListingSearch(TradingPostActiveListingSearchType.MOST_RECENT, "");
452 addSearchResultsAndUpdateInterface(tradingPostSearch);
453 }
454
455 private void searchExistingListing(TradingPostActiveListingSearch search) {
456 if(search.text.length() == 0) {
457 cleanUp();
458 displayMostRecentListings();
459 return;
460 }
461 tradingPostSearch = search;
462 this.page = 0;
463 addSearchResultsAndUpdateInterface(search);
464 player.send(new SendString("Search: " + tradingPostSearch.text,80176));
465 }
466
467 public void addSearchResultsAndUpdateInterface(TradingPostActiveListingSearch search) {
468 searchedListings.clear();
469 searchedListings.addAll(getSearchResults(search.searchType,search.text));
470 updateBuyingPageWidgets(getPageListings(searchedListings));
471 }
472
473 private List<TradingPostListing> getSearchResults(TradingPostActiveListingSearchType type, String search) {
474 if(type == TradingPostActiveListingSearchType.MOST_RECENT) {
475 return Lists.reverse(allListings);
476 }
477 List<TradingPostListing> temp = new ArrayList<>();
478 TreeMap<String, String> map = new TreeMap<>();
479 map.put(search,search);
480 tradingPostSearch.acdat.build(map);
481 for(TradingPostListing listing : allListings) {
482 tradingPostSearch.acdat.parseText(type == TradingPostActiveListingSearchType.ITEM ? ItemDefinition.get(listing.getItemId()).getName().toLowerCase() : listing.getSeller().toLowerCase(), (begin, end, value) -> {
483 if(!temp.contains(listing)) {
484 temp.add(listing);
485 }
486 });
487 }
488 return temp;
489 }
490
491 private List<TradingPostListing> getPageListings(List<TradingPostListing> listings) {
492 return listings.subList(
493 Math.min(listings.size(), page * MAX_LISTINGS_SHOWN),
494 Math.min(listings.size(), (page * MAX_LISTINGS_SHOWN + MAX_LISTINGS_SHOWN))).
495 stream()
496 .filter(tradingPostListing -> !tradingPostListing.getSeller().equalsIgnoreCase(player.getName()))
497 .limit(MAX_LISTINGS_SHOWN)
498 .collect(Collectors.toList());
499 }
500
501 private void updateBuyingPageWidgets(List<TradingPostListing> listings) {
502 displayedPageListings = listings;
503
504 for(int i = 0; i < MAX_LISTINGS_SHOWN; i++) {
505 if(displayedPageListings.size() > i) {
506 TradingPostListing listing = displayedPageListings.get(i);
507 player.send(new SendString(ItemDefinition.get(listing.getItemId()).getName(),80214+i));
508 player.send(new SendString(Utility.formatDigits(listing.getAmountLeft()*listing.getPrice()),80264+i));
509 player.send(new SendString("= " + Utility.formatDigits(listing.getPrice()) + " (ea)",80314+i));
510 player.send(new SendString(listing.getSeller(),80364+i));
511 toggleBuyingPageWidgetVisibility(i,false);
512 } else {
513 toggleBuyingPageWidgetVisibility(i, true);
514 }
515 }
516
517 player.send(new SendString("Page: " + (page+1),80615));
518 player.send(new SendItemOnInterface(80614, getItemArrayFromActiveListings(displayedPageListings)));
519 player.send(new SendScrollbar(80211, Math.max(238, displayedPageListings.size()*34)));
520 }
521
522 private void toggleBuyingPageWidgetVisibility(int i, boolean isHidden) {
523 player.send(new SendInterfaceVisibility(80214+i, isHidden));
524 player.send(new SendInterfaceVisibility(80264+i, isHidden));
525 player.send(new SendInterfaceVisibility(80314+i, isHidden));
526 player.send(new SendInterfaceVisibility(80364+i, isHidden));
527 player.send(new SendInterfaceVisibility(80414+i, isHidden));
528 player.send(new SendInterfaceVisibility(80464+i, isHidden));
529 player.send(new SendInterfaceVisibility(80564+i, isHidden));
530 }
531
532 private void toggleSellingOverlayVisibilityAndOpen(boolean isHidden) {
533 if(isHidden) {
534 player.interfaceManager.close();
535 }
536 player.interfaceManager.open(OVERVIEW_INTERFACE_ID);
537 player.send(new SendInterfaceVisibility(80146, isHidden));
538 }
539
540 private int getSlotSizeByDonatorRank() {
541 switch (player.right) {
542 case KING_DONATOR -> {
543 return MAX_LISTING_SIZE;
544 }
545 case ELITE_DONATOR -> {
546 return 18;
547
548 }
549 case EXTREME_DONATOR -> {
550 return 15;
551 }
552 case SUPER_DONATOR -> {
553 return 12;
554 }
555 case DONATOR -> {
556 return 10;
557 }
558 default -> {
559 return 5;
560 }
561 }
562 }
563
564 private void openSellingOverlay() {
565 clearSellingOverlay();
566 toggleSellingOverlayVisibilityAndOpen(false);
567 listingToAdd = null;
568 player.send(new SendInventoryInterface(OVERVIEW_INTERFACE_ID, 65300));
569 player.send(new SendItemOnInterface(65301, player.inventory.toArray()));
570 }
571
572 public void selectItemToList(Item item) {
573 if(!item.isTradeable() || item.getId() == 995) {
574 player.send(new SendMessage("You cannot list this item."));
575 return;
576 }
577 listingToAdd = new TradingPostListing(item.getId(), player.getName());
578 player.send(new SendItemOnInterface(80153,item));
579 player.send(new SendString(item.getDefinition().getName(),80160));
580 updatePriceStrings();
581 updateQuantityString();
582 }
583
584 public void updatePriceStrings() {
585 if(listingToAdd == null) return;
586 player.send(new SendString("Price: @gre@" + Utility.formatDigits(listingToAdd.getPrice()), 80161));
587 player.send(new SendString("Total: @gre@" + Utility.formatDigits(listingToAdd.getPrice()*listingToAdd.getInitialQuantity()), 80163));
588 }
589
590 public void updateQuantityString() {
591 if(listingToAdd == null) return;
592 player.send(new SendString("Quantity: @gre@" + listingToAdd.getInitialQuantity(), 80162));
593 player.send(new SendString(listingToAdd.getInitialQuantity(), 80173));
594 }
595
596 private void clearSellingOverlay() {
597 player.send(new SendItemOnInterface(80153, new Item(-1,0)));
598 player.send(new SendString("",80160));
599 player.send(new SendString("",80161));
600 player.send(new SendString("",80162));
601 player.send(new SendString("",80163));
602 player.send(new SendString("",80173));
603 }
604
605 private void editQuantityInputPrompt() {
606 player.send(new SendInputAmount("How many would you like to sell?", 10, input -> listingToAdd.setQuantity(player,Integer.parseInt(input))));
607 }
608
609 private void editPriceInputPrompt() {
610 player.send(new SendInputAmount("How much would you like sell this for?", 10, input -> listingToAdd.setPrice(player, Integer.parseInt(input))));
611 }
612
613 private void confirmToAddListing() {
614 if(listingToAdd.getPrice() == 0) {
615 player.send(new SendMessage("Unable to add listing with a price of zero."));
616 return;
617 }
618 if(myListings.size() > getSlotSizeByDonatorRank()) {
619 player.send(new SendMessage("Unable to add listing with max number listings listed."));
620 return;
621 }
622
623 if(player.inventory.remove(listingToAdd.getItemId(), listingToAdd.getInitialQuantity())) {
624 allListings.add(listingToAdd);
625 updateExistingListingsList();
626 openSellingOverlay();
627 updateExistingListingsWidgets();
628 saveListings(player.getName(),getListingsByName(player.getName()));
629 } else {
630 player.send(new SendMessage("Unable to list this item. Please try again."));
631 }
632 }
633
634 private void updateExistingListingsList() {
635 myListings = getListingsByName(player.getName());
636 }
637
638 private List<TradingPostListing> getListingsByName(String name) {
639 return allListings
640 .stream()
641 .filter(tradingPostListing -> tradingPostListing.getSeller().equals(name))
642 .collect(Collectors.toList());
643 }
644
645 private Item[] getItemArrayFromActiveListings(List<TradingPostListing> list) {
646 return list
647 .stream()
648 .map(tradingPostListing -> new Item(ItemDefinition.get(tradingPostListing.getItemId()).isNoted()
649 ? ItemDefinition.get(tradingPostListing.getItemId()).getUnnotedId()
650 : tradingPostListing.getItemId(), tradingPostListing.getAmountLeft()))
651 .toArray(Item[]::new);
652 }
653
654 private Item[] getItemArrayFromItemHistory(List<ItemHistory> list) {
655 return list
656 .stream()
657 .map(ih -> new Item(ItemDefinition.get(ih.getItemId()).isNoted()
658 ? ItemDefinition.get(ih.getItemId()).getUnnotedId()
659 : ih.getItemId(), ih.getQuantity()))
660 .toArray(Item[]::new);
661 }
662
663 private void updateExistingListingsWidgets() {
664 player.send(new SendItemOnInterface(80132, getItemArrayFromActiveListings(myListings)));
665
666 for(int i = 0; i < MAX_LISTING_SIZE; i++) {
667 if(myListings.size() > i) {
668 TradingPostListing tradingPostListing = myListings.get(i);
669 sendOverviewWidgetVisibility(i,false);
670 player.send(new SendString(tradingPostListing.getAmountSold() + "/" + tradingPostListing.getInitialQuantity(), 80112+i));
671 player.send(new SendProgressBar(80032+i, ((tradingPostListing.getAmountSold() * 100) / tradingPostListing.getInitialQuantity())));
672 } else {
673 sendOverviewWidgetVisibility(i,true);
674 }
675 }
676
677 player.send(new SendScrollbar(80029, Math.max(38*myListings.size(), 157)));
678 }
679
680 private void sendOverviewWidgetVisibility(int i, boolean isHidden) {
681 player.send(new SendInterfaceVisibility(80032+i,isHidden));
682 player.send(new SendInterfaceVisibility(80052+i,isHidden));
683 player.send(new SendInterfaceVisibility(80072+i,isHidden));
684 player.send(new SendInterfaceVisibility(80112+i,isHidden));
685 }
686
687
688 private boolean handleBuyingButton(int btnId) {
689 if(btnId >= 14878 && btnId <= 14927) {
690 int index = btnId - 14878;
691
692 if(displayedPageListings.size() < index) {
693 return true;
694 }
695
696 TradingPostListing temp = displayedPageListings.get(index);
697
698 if(temp == null) {
699 return true;
700 }
701
702 if(!allListings.contains(temp)) {
703 return true;
704 }
705
706 listingToBuy = temp;
707
708 if(listingToBuy.getAmountLeft() > 1) {
709 player.send(new SendInputAmount("How many of " + ItemDefinition.get(listingToBuy.getItemId()).getName() + "?", 10, input -> buyingDialogueOptions(Integer.parseInt(input))));
710 } else {
711 buyingDialogueOptions(1);
712 }
713 }
714
715 return false;
716 }
717
718 public void buyingDialogueOptions(int amount) {
719 if((long)amount + listingToBuy.getPrice() > Integer.MAX_VALUE) {
720 player.send(new SendMessage("Cannot buy this quantity for this price."));
721 return;
722 }
723
724 if(amount > listingToBuy.getAmountLeft()) {
725 amount = listingToBuy.getAmountLeft();
726 }
727
728 int finalAmount = amount;
729 player.dialogueFactory.sendStatement("Purchase " + amount + " of " + ItemDefinition.get(listingToBuy.getItemId()).getName(),
730 "for <col=A52929>" + Utility.formatDigits(listingToBuy.getPrice()*amount) + " " + ItemDefinition.get(CURRENCY_ID).getName() + "?")
731 .sendOption("Yes", () -> purchase(finalAmount), "Nevermind", () -> player.dialogueFactory.clear()).execute();
732 }
733
734 private void alertSeller(String sellerName, String item, int amount) {
735 Optional<Player> playerOptional = World.search(sellerName);
736
737 if(playerOptional.isPresent()) {
738 Player player = playerOptional.get();
739
740 player.send(new SendMessage("@red@Trading post: " + amount + " of your " + item + "s have been sold"));
741
742 player.tradingPost.updateExistingListingsList();
743
744 if(player.interfaceManager.hasAnyOpen(OVERVIEW_INTERFACE_ID)) {
745 player.tradingPost.openOverviewInterface();
746 }
747 }
748 }
749
750 private void purchase(int amount) {
751 if(listingToBuy == null) {
752 return;
753 }
754
755 int getPlayerCurrencyAmount = player.inventory.computeAmountForId(CURRENCY_ID);
756 int totalPrice = listingToBuy.getPrice()*amount;
757
758 if(totalPrice > getPlayerCurrencyAmount) {
759 player.send(new SendMessage("Insufficient " + ItemDefinition.get(CURRENCY_ID).getName() + " to complete your transaction."));
760 return;
761 }
762
763 if(amount > listingToBuy.getAmountLeft()) {
764 amount = listingToBuy.getAmountLeft();
765 player.send(new SendMessage("Your purchase amount has been lowered to " + amount));
766 }
767
768 if(!allListings.contains(listingToBuy)) {
769 player.send(new SendMessage("This item does not exist in the trading post anymore."));
770 return;
771 }
772
773 if(player.inventory.add(listingToBuy.getItemId(), amount)) {
774 player.inventory.remove(CURRENCY_ID,totalPrice);
775 listingToBuy.addToAmountSold(amount);
776
777 if(listingToBuy.getAmountLeft() == 0) {
778 allListings.remove(listingToBuy);
779 displayedPageListings.remove(listingToBuy);
780 searchedListings.remove(listingToBuy);
781 }
782
783 if(tradingPostSearch != null) {
784 addSearchResultsAndUpdateInterface(tradingPostSearch);
785 } else {
786 displayMostRecentListings();
787 }
788
789 addToCoffer(listingToBuy.getSeller(),listingToBuy.getPrice()*amount);
790 alertSeller(listingToBuy.getSeller(),ItemDefinition.get(listingToBuy.getItemId()).getName(), amount);
791
792 String seller = listingToBuy.getSeller();
793 saveListings(seller,getListingsByName(seller));
794
795 addToItemHistory(new ItemHistory(amount, listingToBuy.getItemId(), listingToBuy.getPrice()*amount, listingToBuy.getSeller(), player.getName()));
796
797 } else {
798 player.send(new SendMessage("You do not have enough inventory spaces to complete this transaction"));
799 }
800
801 }
802
803 public void addToItemHistory(ItemHistory itemHistory) {
804 List<ItemHistory> itemHistoryList = itemHistories.computeIfAbsent(ItemDefinition.get(itemHistory.getItemId()).getName(), x -> new ArrayList<>());
805
806 if(itemHistoryList.size() == 50) {
807 itemHistoryList.remove(49);
808 }
809
810 itemHistoryList.add(itemHistory);
811
812 if(recentlySoldItems.size() == 50) {
813 recentlySoldItems.remove(49);
814 }
815
816 recentlySoldItems.add(itemHistory);
817
818 if(playersItemHistory.size() == 50) {
819 playersItemHistory.remove(49);
820 }
821
822 playersItemHistory.add(itemHistory);
823 }
824
825 public void addToCoffer(String owner, int amount) {
826 getOrMakeNewCofferIfNotExists(owner, coffer -> {
827 coffer.addAmount((int) (amount - (TAX_RATE/100.0*amount)));
828 saveCoffer(coffer);
829 });
830 }
831
832 public void getOrMakeNewCofferIfNotExists(final String owner,
833 final Consumer<Coffer> useCoffer) {
834 getCoffer(owner).thenAccept(coffer -> {
835 if (coffer == null) {
836 coffer = allCoffers.compute(owner, (k, v) -> new Coffer(owner));
837 saveCoffer(coffer);
838 }
839
840 useCoffer.accept(coffer);
841 });
842 }
843
844 private boolean handleDismissListingButton(int btnId) {
845 if(btnId >= 14516 && btnId <= 14535) {
846 int index = btnId - 14516;
847
848 if(myListings.isEmpty()) {
849 return true;
850 }
851
852 if(myListings.size() < index) {
853 return true;
854 }
855
856 TradingPostListing listing = myListings.get(index);
857
858 if(!delist(listing)) {
859 player.send(new SendMessage("Unable to delist this item. Please try again."));
860 } else {
861 saveListings(player.getName(),getListingsByName(player.getName()));
862 player.inventory.addOrBank(new Item(listing.getItemId(),listing.getAmountLeft()));
863 openOverviewInterface();
864 }
865
866 return true;
867 }
868
869 return false;
870 }
871
872 public boolean delist(TradingPostListing listing) {
873 if(myListings.isEmpty()) {
874 return false;
875 }
876
877 if(!myListings.contains(listing)) {
878 return false;
879 }
880
881 if(!allListings.contains(listing)) {
882 return false;
883 }
884
885 return allListings.remove(listing) && myListings.remove(listing);
886 }
887
888 public void cleanUp() {
889 displayedPageListings.clear();
890 searchedListings.clear();
891 tradingPostSearch = null;
892 tradingPostSort = null;
893 listingToAdd = null;
894 listingToBuy = null;
895 page = 0;
896 }
897
898 public void testData() {
899 int addCounter = 0;
900 for(int i = 0; i < 1000; i++) {
901 ItemDefinition def = ItemDefinition.DEFINITIONS[new Random().nextInt(ItemDefinition.DEFINITIONS.length)];
902 if(def != null) {
903 TradingPostListing listing = new TradingPostListing(def.getId(), "John"+i);
904 listing.setInitialQuantity(i+1);
905 listing.setPrice(i+1);
906 allListings.add(listing);
907 addCounter++;
908 System.out.println("Adding listing: " + addCounter);
909 }
910 }
911 }
912
913 public void saveCoffer(final Coffer coffer) {
914 Thread.startVirtualThread(() -> {
915 try {
916 final String json = GsonUtils.JSON_PRETTY_ALLOW_NULL.get().toJson(coffer);
917
918 final Path path = Path.of("./data/profile/save/tradingpost/coffers/", coffer.getOwner(), ".json");
919 Path parent = path.getParent();
920 if (parent == null) {
921 throw new UnsupportedOperationException("Path must have a parent " + path);
922 }
923 if (!Files.exists(parent)) {
924 parent = Files.createDirectories(parent);
925 }
926
927 final Path tempFile = Files.createTempFile(parent, path.getFileName().toString(), ".tmp");
928 Files.writeString(tempFile, json, StandardCharsets.UTF_8);
929
930 Files.move(tempFile, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
931 } catch (final Exception ex) {
932 logger.error(String.format("Error while saving player=%s", player.getName()), ex);
933 }
934 });
935 }
936
937 public CompletableFuture<Coffer> getCoffer(final String owner) {
938 final Coffer cachedCoffer = allCoffers.get(owner);
939 if (cachedCoffer != null) {
940 return CompletableFuture.completedFuture(cachedCoffer);
941 }
942
943 // retrieve coffer if coffer hasn't been retrieved since server start
944 return CompletableFuture.supplyAsync(() -> {
945 final Path path = Path.of("./data/profile/save/tradingpost/coffers/", owner, ".json");
946 if (!Files.exists(path)) {
947 return null;
948 }
949
950 final Gson gson = GsonUtils.JSON_ALLOW_NULL.get();
951 try (final BufferedReader reader = Files.newBufferedReader(path)) {
952 final Coffer coffer = gson.fromJson(reader, Coffer.class);
953 if (coffer != null) {
954 allCoffers.put(coffer.getOwner(), coffer);
955 return coffer;
956 }
957 } catch (final IOException e) {
958 logger.error("Failed reading coffer for \"" + owner + "\"", e);
959 }
960 return null;
961 });
962 }
963
964 public void saveListings(final String owner, final List<TradingPostListing> listings) {
965 Thread.startVirtualThread(() -> {
966 try {
967 final String json = GsonUtils.JSON_PRETTY_ALLOW_NULL.get().toJson(listings);
968
969 final Path path = Path.of("./data/profile/save/tradingpost/listings/", owner, ".json");
970 Path parent = path.getParent();
971 if (parent == null) {
972 throw new UnsupportedOperationException("Path must have a parent " + path);
973 }
974 if (!Files.exists(parent)) {
975 parent = Files.createDirectories(parent);
976 }
977
978 final Path tempFile = Files.createTempFile(parent, path.getFileName().toString(), ".tmp");
979 Files.writeString(tempFile, json, StandardCharsets.UTF_8);
980
981 Files.move(tempFile, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
982
983 } catch (final Exception ex) {
984 logger.error(String.format("Error while saving player=%s", player.getName()), ex);
985 }
986 });
987 }
988
989 public static void loadAllListings() {
990 try {
991 File folder = new File("./data/profile/save/tradingpost/listings/");
992 File[] files = folder.listFiles();
993 if (files == null) return;
994 for(File f : files) {
995 File toRead = null;
996 if(f.isDirectory()) {
997 toRead = Objects.requireNonNull(f.getAbsoluteFile().listFiles())[0];
998 }
999 try {
1000 assert toRead != null;
1001 final Gson gson = GsonUtils.JSON_ALLOW_NULL.get();
1002 try (final BufferedReader reader = Files.newBufferedReader(Path.of(toRead.getPath()))) {
1003 final List<TradingPostListing> listings = List.of(gson.fromJson(reader, TradingPostListing[].class));
1004 allListings.addAll(listings);
1005 } catch (final IOException e) {
1006 logger.error("Failed reading listings", e);
1007 }
1008 } catch (Exception e) {
1009 e.printStackTrace();
1010 }
1011 }
1012 } catch (Exception e) {
1013 e.printStackTrace();
1014 }
1015 }
1016
1017 public static void saveAllItemHistory() {
1018 Path path = Paths.get("./data/profile/save/tradingpost/itemhistory.json");
1019 File file = path.toFile();
1020 file.getParentFile().setWritable(true);
1021
1022 if(!file.getParentFile().exists()) {
1023 try {
1024 file.getParentFile().mkdirs();
1025 } catch (SecurityException e) {
1026 System.out.println("Error while creating item history directory");
1027 }
1028 }
1029 try(FileWriter writer = new FileWriter(file)) {
1030 Gson builder = new GsonBuilder().setPrettyPrinting().create();
1031 JsonObject object = new JsonObject();
1032 object.add("itemhistory",builder.toJsonTree(itemHistories));
1033 writer.write(builder.toJson(object));
1034 } catch (Exception e) {
1035 e.printStackTrace();
1036 }
1037 }
1038
1039 public static void saveRecentHistory() {
1040 Path path = Paths.get("./data/profile/save/tradingpost/itemhistoryrecent.json");
1041 File file = path.toFile();
1042 file.getParentFile().setWritable(true);
1043
1044 if(!file.getParentFile().exists()) {
1045 try {
1046 file.getParentFile().mkdirs();
1047 } catch (SecurityException e) {
1048 System.out.println("Error while creating recent item history directory");
1049 }
1050 }
1051 try(FileWriter writer = new FileWriter(file)) {
1052 Gson builder = new GsonBuilder().setPrettyPrinting().create();
1053 JsonObject object = new JsonObject();
1054 object.add("recentitemhistory",builder.toJsonTree(recentlySoldItems));
1055 writer.write(builder.toJson(object));
1056 } catch (Exception e) {
1057 e.printStackTrace();
1058 }
1059 }
1060
1061 public static void loadItemHistory() {
1062 Path path = Paths.get("./data/profile/save/tradingpost/itemhistory.json");
1063 File file = path.toFile();
1064
1065 createFileAndDirIfNotExists(file);
1066 if(file.length() == 0) return;
1067
1068 try (FileReader fileReader = new FileReader(file)) {
1069 JsonParser fileParser = new JsonParser();
1070 Gson builder = new GsonBuilder()
1071 .create();
1072 JsonObject reader = (JsonObject) fileParser.parse(fileReader);
1073
1074 HashMap<String, List<ItemHistory>> itemHistory = builder.fromJson(reader.get("itemhistory"),
1075 new TypeToken<HashMap<String, List<ItemHistory>>>() {
1076 }.getType());
1077 itemHistories.putAll(itemHistory);
1078 } catch (Exception e) {
1079 e.printStackTrace();
1080 }
1081 }
1082
1083 public static void loadRecentItemHistory() {
1084 Path path = Paths.get("./data/profile/save/tradingpost/itemhistoryrecent.json");
1085 File file = path.toFile();
1086
1087 createFileAndDirIfNotExists(file);
1088 if(file.length() == 0) return;
1089
1090 try (FileReader fileReader = new FileReader(file)) {
1091 JsonParser fileParser = new JsonParser();
1092 Gson builder = new GsonBuilder()
1093 .create();
1094 JsonObject reader = (JsonObject) fileParser.parse(fileReader);
1095 ItemHistory[] temp = builder.fromJson(reader.get("recentitemhistory").getAsJsonArray(), ItemHistory[].class);
1096 recentlySoldItems.addAll(Arrays.asList(temp));
1097 } catch (Exception e) {
1098 e.printStackTrace();
1099 }
1100 }
1101
1102 public static void createFileAndDirIfNotExists(File file) {
1103 if (!file.getParentFile().exists() || !file.exists()) {
1104 try {
1105 file.getParentFile().mkdirs();
1106 file.createNewFile();
1107 } catch (SecurityException e) {
1108 System.out.println("Unable to create directory");
1109 } catch (IOException e) {
1110 throw new RuntimeException(e);
1111 }
1112 }
1113 }
1114
1115 public List<ItemHistory> getPlayersItemHistory() {
1116 return playersItemHistory;
1117 }
1118
1119
1120 static class TradingPostActiveListingSearch {
1121 private final TradingPostActiveListingSearchType searchType;
1122 private final String text;
1123 private final AhoCorasickDoubleArrayTrie<String> acdat = new AhoCorasickDoubleArrayTrie<>();
1124
1125 TradingPostActiveListingSearch(TradingPostActiveListingSearchType searchType, String text) {
1126 this.searchType = searchType;
1127 this.text = text.toLowerCase();
1128 }
1129 }
1130
1131enum TradingPostActiveListingSearchType {
1132 PLAYER,
1133 ITEM,
1134 MOST_RECENT;
1135 }
1136
1137 static class TradingPostActiveListingSort {
1138 TradingPostSortType postSortType;
1139
1140 public TradingPostActiveListingSort(TradingPostSortType postSortType) {
1141 this.postSortType = postSortType;
1142 }
1143
1144 public TradingPostSortType getPostSortType() {
1145 return postSortType;
1146 }
1147
1148 public TradingPostActiveListingSort setPostSortType(TradingPostSortType postSortType) {
1149 this.postSortType = postSortType;
1150 return this;
1151 }
1152 }
1153
1154 enum TradingPostSortType {
1155 HIGHEST_QUANTITY,
1156 LOWEST_QUANTITY,
1157 HIGHEST_PRICE,
1158 LOWEST_PRICE;
1159 }
1160}
static Optional< Player > search(String name)
Definition World.java:184
static void schedule(Task task)
Definition World.java:284
static String formatDigits(final int amount)
Definition Utility.java:78