RuneHive-Game
Loading...
Searching...
No Matches
TradingPost.java
Go to the documentation of this file.
1package com.runehive.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.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;
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
33public class TradingPost {
34
35 private static final Logger logger = LoggerFactory.getLogger(TradingPost.class);
36
37 private static final int OVERVIEW_INTERFACE_ID = 80000;
38 public static final int BUYING_PAGE_INTERFACE_ID = 80174;
39 private static final int HISTORY_PAGE_INTERFACE_ID = 80616;
40 private static final int MAX_LISTINGS_SHOWN = 50;
41 private static final int MAX_LISTING_SIZE = 20;
42 private static final int CURRENCY_ID = 995;
43 private static final int TAX_RATE = 10;
44
45 private final AhoCorasickDoubleArrayTrie<String> acdat = new AhoCorasickDoubleArrayTrie<>();
46
47 /**
48 * A list containing all the trading post listings
49 */
50 private static final List<TradingPostListing> allListings = new ArrayList<>();
51
52 /**
53 * Hashmap containing the coffers <PlayerName, Coffer>
54 */
55 private static final Map<String, Coffer> allCoffers = Collections.synchronizedMap(new HashMap<>());
56
57 /**
58 * List containing the players existing listings/offers
59 */
60 private List<TradingPostListing> myListings = new ArrayList<>();
61
62 /**
63 * The listing that the player will add to their existing listings
64 */
66
67 /**
68 * The listing that the player selects in the buying page
69 */
71
72 /**
73 * The current item or player search
74 */
76
77 /**
78 * The current sort of the buyable listings
79 */
81
82 /**
83 * The current searched listings
84 */
85 private List<TradingPostListing> searchedListings = new ArrayList<>();
86
87 /**
88 * The displayed listings on the buying page
89 */
90 private List<TradingPostListing> displayedPageListings = new ArrayList<>();
91
92 /**
93 * Contains a list of the last 50 sold of each item
94 */
95 private static final Map<String, List<ItemHistory>> itemHistories = new HashMap<>();
96
97 /**
98 * contains the last 50 sold items on the trading post
99 */
100 private static final List<ItemHistory> recentlySoldItems = new ArrayList<>();
101
102 /**
103 * contains last 50 players buying history
104 */
105 private final List<ItemHistory> playersItemHistory = new ArrayList<>();
106
107 /**
108 * The page # of the buying interface
109 */
110 private int page;
111
112 private final Player player;
113
115 this.player = player;
116 }
117
126
128 getOrMakeNewCofferIfNotExists(player.getName(), coffer -> {
129 player.send(new SendString(Utility.formatDigits(coffer.getAmount()), 80005));
130 });
131 }
132
133 public boolean handleButtonClick(int btnId) {
135 return false;
136 }
137
138 switch (btnId) {
139 case 14650:
140 case 15418:
142 return true;
143 case 15091:
144 case 14597:
146 return true;
147 case 14600:
149 return true;
150 case 14603:
152 return true;
153 case 14662:
154 player.dialogueFactory.sendOption("Search item", this::searchExistingItemInput, "Search player", this::searchPlayerInput, "Nevermind", () -> player.dialogueFactory.clear()).execute();
155 return true;
156 case 14474:
158 return true;
159 case 14477:
161 return true;
162 case 14484:
164 return true;
165 case 14487:
167 return true;
168 case 14614:
170 return true;
171 case 14659:
172 nextPage();
173 return true;
174 case 14653:
175 previousPage();
176 return true;
177 case 14656:
178 refresh();
179 return true;
180 case 14466:
182 return true;
183 case 14649:
184 sortPrice();
185 return true;
186 case 14648:
187 sortQuantity();
188 return true;
189 default: {
190 /*
191 * Returns true or false based on button ids
192 */
194 return true;
195 }
196 }
197 }
198
199 return false;
200 }
201
202 private void searchItemHistoryInput() {
203 player.send(new SendInputMessage("Enter item name to search history for:", 16, input -> {
204
205 if(Objects.equals(input, "")) {
207 return;
208 }
209
210 List<ItemHistory> historyItems = new ArrayList<>();
211 TreeMap<String, String> map = new TreeMap<>();
212 map.put(input,input);
213 acdat.build(map);
214
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();
218
219 for(ItemHistory h : temp) {
220 if(!historyItems.contains(h)) {
221 historyItems.add(h);
222 }
223
224 if(historyItems.size() == 50) {
225 break;
226 }
227
228 }
229 });
230 }
231
232 sendItemHistoryData(historyItems);
233 }));
234 }
235
236 public void sendItemHistoryData(List<ItemHistory> itemHistories) {
237 for(int i = 0; i < MAX_LISTINGS_SHOWN; i++) {
238 if(itemHistories.size() > i) {
239 ItemHistory ih = itemHistories.get(i);
240 player.send(new SendString(ItemDefinition.get(ih.getItemId()).getName(),80753+i));
241 player.send(new SendString(Utility.formatDigits(ih.getPrice()),80803+i));
242 player.send(new SendString(ih.getSeller(),80853+i));
243 player.send(new SendString(ih.getBuyer(),80903+i));
244 } else {
245 player.send(new SendString("",80753+i));
246 player.send(new SendString("",80803+i));
247 player.send(new SendString("",80853+i));
248 player.send(new SendString("",80903+i));
249 }
250 }
251
253 player.interfaceManager.open(HISTORY_PAGE_INTERFACE_ID);
254 }
255
256 private void searchExistingItemInput() {
257 World.schedule(new Task(1) { // task added so input prompt shows after dialogue
258 @Override
259 protected void execute() {
260 player.send(new SendInputMessage("Enter item name to search for:", 16, input -> {
262 player.interfaceManager.open(BUYING_PAGE_INTERFACE_ID);
263 }));
264 this.cancel();
265 }
266 });
267 }
268
269 private void searchPlayerInput() {
270 World.schedule(new Task(1) { // task added so input prompt shows after dialogue
271 @Override
272 protected void execute() {
273 player.send(new SendInputMessage("Enter player name to search for:", 16, input -> {
275 player.interfaceManager.open(BUYING_PAGE_INTERFACE_ID);
276 }));
277 this.cancel();
278 }
279 });
280 }
281
282 private void sort() {
283 if(displayedPageListings == null) return;
284 if(tradingPostSort == null) return;
285
286 page = 0;
287
289 .stream()
290 .sorted(Comparator.comparingInt(
294 .collect(Collectors.toList());
295
296
298 Collections.reverse(searchedListings);
299 }
300
302 }
303
313
323
324
325 private void withdrawFromCoffer() {
326 getOrMakeNewCofferIfNotExists(player.getName(), coffer -> {
327 if (coffer.getAmount() == 0) {
328 return;
329 }
330
331 player.send(new SendInputAmount("How much gold would you like to withdraw?", 10, input -> {
332 int amount = Integer.parseInt(input);
333
334 if (amount > coffer.getAmount()) {
335 amount = (int) coffer.getAmount();
336 }
337
338 if (amount > coffer.getAmount()) {
339 return;
340 }
341
342 if (player.inventory.add(CURRENCY_ID, amount)) {
343 coffer.subtractAmount(amount);
345 saveCoffer(coffer);
346 } else {
347 player.send(new SendMessage("You do not have enough inventory spaces to withdraw this gold"));
348 }
349
350 }));
351 });
352 }
353
354 private void refresh() {
355 if(tradingPostSearch != null) {
357 } else {
359 }
360 }
361
362 private void nextPage() {
363 page++;
364 List<TradingPostListing> temp;
366 if(temp.isEmpty()) {
367 page--;
368 player.send(new SendMessage("No more results on the next page"));
369 } else {
371 }
372 }
373
374 private void previousPage() {
375 if(page == 0) return;
376 page--;
378 }
379
380 private boolean handleSellingOverlayButtons(int btnId) {
381 if(listingToAdd == null) return false;
382
383 switch (btnId) {
384 case 14618:
385 listingToAdd.addQuantity(player,1);
386 return true;
387 case 14621:
388 listingToAdd.removeQuantity(player,1);
389 return true;
390 case 14636:
392 return true;
393 case 14628:
395 return true;
396 case 14631:
398 return true;
399 }
400
401 return false;
402 }
403
405 if(!player.interfaceManager.isInterfaceOpen(BUYING_PAGE_INTERFACE_ID)) {
406 player.interfaceManager.open(BUYING_PAGE_INTERFACE_ID);
407 }
408 player.send(new SendString("Search: Most recent",80176));
409
412 }
413
415 if(search.text.length() == 0) {
416 cleanUp();
418 return;
419 }
420 tradingPostSearch = search;
421 this.page = 0;
423 player.send(new SendString("Search: " + tradingPostSearch.text,80176));
424 }
425
431
432 private List<TradingPostListing> getSearchResults(TradingPostActiveListingSearchType type, String search) {
434 return Lists.reverse(allListings);
435 }
436 List<TradingPostListing> temp = new ArrayList<>();
437 TreeMap<String, String> map = new TreeMap<>();
438 map.put(search,search);
439 tradingPostSearch.acdat.build(map);
440 for(TradingPostListing listing : allListings) {
441 tradingPostSearch.acdat.parseText(type == TradingPostActiveListingSearchType.ITEM ? ItemDefinition.get(listing.getItemId()).getName().toLowerCase() : listing.getSeller().toLowerCase(), (begin, end, value) -> {
442 if(!temp.contains(listing)) {
443 temp.add(listing);
444 }
445 });
446 }
447 return temp;
448 }
449
450 private List<TradingPostListing> getPageListings(List<TradingPostListing> listings) {
451 return listings.subList(
452 Math.min(listings.size(), page * MAX_LISTINGS_SHOWN),
453 Math.min(listings.size(), (page * MAX_LISTINGS_SHOWN + MAX_LISTINGS_SHOWN))).
454 stream()
455 .filter(tradingPostListing -> !tradingPostListing.getSeller().equalsIgnoreCase(player.getName()))
456 .limit(MAX_LISTINGS_SHOWN)
457 .collect(Collectors.toList());
458 }
459
460 private void updateBuyingPageWidgets(List<TradingPostListing> listings) {
461 displayedPageListings = listings;
462
463 for(int i = 0; i < MAX_LISTINGS_SHOWN; i++) {
464 if(displayedPageListings.size() > i) {
466 player.send(new SendString(ItemDefinition.get(listing.getItemId()).getName(),80214+i));
467 player.send(new SendString(Utility.formatDigits(listing.getAmountLeft()*listing.getPrice()),80264+i));
468 player.send(new SendString("= " + Utility.formatDigits(listing.getPrice()) + " (ea)",80314+i));
469 player.send(new SendString(listing.getSeller(),80364+i));
471 } else {
473 }
474 }
475
476 player.send(new SendString("Page: " + (page+1),80615));
478 player.send(new SendScrollbar(80211, Math.max(238, displayedPageListings.size()*34)));
479 }
480
481 private void toggleBuyingPageWidgetVisibility(int i, boolean isHidden) {
482 player.send(new SendInterfaceVisibility(80214+i, isHidden));
483 player.send(new SendInterfaceVisibility(80264+i, isHidden));
484 player.send(new SendInterfaceVisibility(80314+i, isHidden));
485 player.send(new SendInterfaceVisibility(80364+i, isHidden));
486 player.send(new SendInterfaceVisibility(80414+i, isHidden));
487 player.send(new SendInterfaceVisibility(80464+i, isHidden));
488 player.send(new SendInterfaceVisibility(80564+i, isHidden));
489 }
490
491 private void toggleSellingOverlayVisibilityAndOpen(boolean isHidden) {
492 if(isHidden) {
493 player.interfaceManager.close();
494 }
495 player.interfaceManager.open(OVERVIEW_INTERFACE_ID);
496 player.send(new SendInterfaceVisibility(80146, isHidden));
497 }
498
500 switch (player.right) {
501 case KING_DONATOR -> {
502 return MAX_LISTING_SIZE;
503 }
504 case ELITE_DONATOR -> {
505 return 18;
506
507 }
508 case EXTREME_DONATOR -> {
509 return 15;
510 }
511 case SUPER_DONATOR -> {
512 return 12;
513 }
514 case DONATOR -> {
515 return 10;
516 }
517 default -> {
518 return 5;
519 }
520 }
521 }
522
523 private void openSellingOverlay() {
526 listingToAdd = null;
528 player.send(new SendItemOnInterface(65301, player.inventory.toArray()));
529 }
530
531 public void selectItemToList(Item item) {
532 if(!item.isTradeable() || item.getId() == 995) {
533 player.send(new SendMessage("You cannot list this item."));
534 return;
535 }
536 listingToAdd = new TradingPostListing(item.getId(), player.getName());
537 player.send(new SendItemOnInterface(80153,item));
538 player.send(new SendString(item.getDefinition().getName(),80160));
541 }
542
543 public void updatePriceStrings() {
544 if(listingToAdd == null) return;
545 player.send(new SendString("Price: @gre@" + Utility.formatDigits(listingToAdd.getPrice()), 80161));
546 player.send(new SendString("Total: @gre@" + Utility.formatDigits(listingToAdd.getPrice()*listingToAdd.getInitialQuantity()), 80163));
547 }
548
549 public void updateQuantityString() {
550 if(listingToAdd == null) return;
551 player.send(new SendString("Quantity: @gre@" + listingToAdd.getInitialQuantity(), 80162));
552 player.send(new SendString(listingToAdd.getInitialQuantity(), 80173));
553 }
554
555 private void clearSellingOverlay() {
556 player.send(new SendItemOnInterface(80153, new Item(-1,0)));
557 player.send(new SendString("",80160));
558 player.send(new SendString("",80161));
559 player.send(new SendString("",80162));
560 player.send(new SendString("",80163));
561 player.send(new SendString("",80173));
562 }
563
564 private void editQuantityInputPrompt() {
565 player.send(new SendInputAmount("How many would you like to sell?", 10, input -> listingToAdd.setQuantity(player,Integer.parseInt(input))));
566 }
567
568 private void editPriceInputPrompt() {
569 player.send(new SendInputAmount("How much would you like sell this for?", 10, input -> listingToAdd.setPrice(player, Integer.parseInt(input))));
570 }
571
572 private void confirmToAddListing() {
573 if(listingToAdd.getPrice() == 0) {
574 player.send(new SendMessage("Unable to add listing with a price of zero."));
575 return;
576 }
577 if(myListings.size() > getSlotSizeByDonatorRank()) {
578 player.send(new SendMessage("Unable to add listing with max number listings listed."));
579 return;
580 }
581
582 if(player.inventory.remove(listingToAdd.getItemId(), listingToAdd.getInitialQuantity())) {
587 saveListings(player.getName(),getListingsByName(player.getName()));
588 } else {
589 player.send(new SendMessage("Unable to list this item. Please try again."));
590 }
591 }
592
595 }
596
597 private List<TradingPostListing> getListingsByName(String name) {
598 return allListings
599 .stream()
600 .filter(tradingPostListing -> tradingPostListing.getSeller().equals(name))
601 .collect(Collectors.toList());
602 }
603
604 private Item[] getItemArrayFromActiveListings(List<TradingPostListing> list) {
605 return list
606 .stream()
607 .map(tradingPostListing -> new Item(ItemDefinition.get(tradingPostListing.getItemId()).isNoted()
608 ? ItemDefinition.get(tradingPostListing.getItemId()).getUnnotedId()
609 : tradingPostListing.getItemId(), tradingPostListing.getAmountLeft()))
610 .toArray(Item[]::new);
611 }
612
613 private Item[] getItemArrayFromItemHistory(List<ItemHistory> list) {
614 return list
615 .stream()
616 .map(ih -> new Item(ItemDefinition.get(ih.getItemId()).isNoted()
617 ? ItemDefinition.get(ih.getItemId()).getUnnotedId()
618 : ih.getItemId(), ih.getQuantity()))
619 .toArray(Item[]::new);
620 }
621
624
625 for(int i = 0; i < MAX_LISTING_SIZE; i++) {
626 if(myListings.size() > i) {
627 TradingPostListing tradingPostListing = myListings.get(i);
629 player.send(new SendString(tradingPostListing.getAmountSold() + "/" + tradingPostListing.getInitialQuantity(), 80112+i));
630 player.send(new SendProgressBar(80032+i, ((tradingPostListing.getAmountSold() * 100) / tradingPostListing.getInitialQuantity())));
631 } else {
633 }
634 }
635
636 player.send(new SendScrollbar(80029, Math.max(38*myListings.size(), 157)));
637 }
638
639 private void sendOverviewWidgetVisibility(int i, boolean isHidden) {
640 player.send(new SendInterfaceVisibility(80032+i,isHidden));
641 player.send(new SendInterfaceVisibility(80052+i,isHidden));
642 player.send(new SendInterfaceVisibility(80072+i,isHidden));
643 player.send(new SendInterfaceVisibility(80112+i,isHidden));
644 }
645
646
647 private boolean handleBuyingButton(int btnId) {
648 if(btnId >= 14878 && btnId <= 14927) {
649 int index = btnId - 14878;
650
651 if(displayedPageListings.size() < index) {
652 return true;
653 }
654
656
657 if(temp == null) {
658 return true;
659 }
660
661 if(!allListings.contains(temp)) {
662 return true;
663 }
664
665 listingToBuy = temp;
666
667 if(listingToBuy.getAmountLeft() > 1) {
668 player.send(new SendInputAmount("How many of " + ItemDefinition.get(listingToBuy.getItemId()).getName() + "?", 10, input -> buyingDialogueOptions(Integer.parseInt(input))));
669 } else {
671 }
672 }
673
674 return false;
675 }
676
677 public void buyingDialogueOptions(int amount) {
678 if((long)amount + listingToBuy.getPrice() > Integer.MAX_VALUE) {
679 player.send(new SendMessage("Cannot buy this quantity for this price."));
680 return;
681 }
682
683 if(amount > listingToBuy.getAmountLeft()) {
684 amount = listingToBuy.getAmountLeft();
685 }
686
687 int finalAmount = amount;
688 player.dialogueFactory.sendStatement("Purchase " + amount + " of " + ItemDefinition.get(listingToBuy.getItemId()).getName(),
689 "for <col=A52929>" + Utility.formatDigits(listingToBuy.getPrice()*amount) + " " + ItemDefinition.get(CURRENCY_ID).getName() + "?")
690 .sendOption("Yes", () -> purchase(finalAmount), "Nevermind", () -> player.dialogueFactory.clear()).execute();
691 }
692
693 private void alertSeller(String sellerName, String item, int amount) {
694 Optional<Player> playerOptional = World.search(sellerName);
695
696 if(playerOptional.isPresent()) {
697 Player player = playerOptional.get();
698
699 player.send(new SendMessage("@red@Trading post: " + amount + " of your " + item + "s have been sold"));
700
701 player.tradingPost.updateExistingListingsList();
702
703 if(player.interfaceManager.hasAnyOpen(OVERVIEW_INTERFACE_ID)) {
704 player.tradingPost.openOverviewInterface();
705 }
706 }
707 }
708
709 private void purchase(int amount) {
710 if(listingToBuy == null) {
711 return;
712 }
713
714 int getPlayerCurrencyAmount = player.inventory.computeAmountForId(CURRENCY_ID);
715 int totalPrice = listingToBuy.getPrice()*amount;
716
717 if(totalPrice > getPlayerCurrencyAmount) {
718 player.send(new SendMessage("Insufficient " + ItemDefinition.get(CURRENCY_ID).getName() + " to complete your transaction."));
719 return;
720 }
721
722 if(amount > listingToBuy.getAmountLeft()) {
723 amount = listingToBuy.getAmountLeft();
724 player.send(new SendMessage("Your purchase amount has been lowered to " + amount));
725 }
726
727 if(!allListings.contains(listingToBuy)) {
728 player.send(new SendMessage("This item does not exist in the trading post anymore."));
729 return;
730 }
731
732 if(player.inventory.add(listingToBuy.getItemId(), amount)) {
733 player.inventory.remove(CURRENCY_ID,totalPrice);
734 listingToBuy.addToAmountSold(amount);
735
736 if(listingToBuy.getAmountLeft() == 0) {
740 }
741
742 if(tradingPostSearch != null) {
744 } else {
746 }
747
748 addToCoffer(listingToBuy.getSeller(),listingToBuy.getPrice()*amount);
749 alertSeller(listingToBuy.getSeller(),ItemDefinition.get(listingToBuy.getItemId()).getName(), amount);
750
751 String seller = listingToBuy.getSeller();
752 saveListings(seller,getListingsByName(seller));
753
754 addToItemHistory(new ItemHistory(amount, listingToBuy.getItemId(), listingToBuy.getPrice()*amount, listingToBuy.getSeller(), player.getName()));
755
756 } else {
757 player.send(new SendMessage("You do not have enough inventory spaces to complete this transaction"));
758 }
759
760 }
761
762 public void addToItemHistory(ItemHistory itemHistory) {
763 List<ItemHistory> itemHistoryList = itemHistories.computeIfAbsent(ItemDefinition.get(itemHistory.getItemId()).getName(), x -> new ArrayList<>());
764
765 if(itemHistoryList.size() == 50) {
766 itemHistoryList.remove(49);
767 }
768
769 itemHistoryList.add(itemHistory);
770
771 if(recentlySoldItems.size() == 50) {
772 recentlySoldItems.remove(49);
773 }
774
775 recentlySoldItems.add(itemHistory);
776
777 if(playersItemHistory.size() == 50) {
778 playersItemHistory.remove(49);
779 }
780
781 playersItemHistory.add(itemHistory);
782 }
783
784 public void addToCoffer(String owner, int amount) {
785 getOrMakeNewCofferIfNotExists(owner, coffer -> {
786 coffer.addAmount((int) (amount - (TAX_RATE/100.0*amount)));
787 saveCoffer(coffer);
788 });
789 }
790
791 public void getOrMakeNewCofferIfNotExists(final String owner,
792 final Consumer<Coffer> useCoffer) {
793 getCoffer(owner).thenAccept(coffer -> {
794 if (coffer == null) {
795 coffer = allCoffers.compute(owner, (k, v) -> new Coffer(owner));
796 saveCoffer(coffer);
797 }
798
799 useCoffer.accept(coffer);
800 });
801 }
802
803 private boolean handleDismissListingButton(int btnId) {
804 if(btnId >= 14516 && btnId <= 14535) {
805 int index = btnId - 14516;
806
807 if(myListings.isEmpty()) {
808 return true;
809 }
810
811 if(myListings.size() < index) {
812 return true;
813 }
814
815 TradingPostListing listing = myListings.get(index);
816
817 if(!delist(listing)) {
818 player.send(new SendMessage("Unable to delist this item. Please try again."));
819 } else {
820 saveListings(player.getName(),getListingsByName(player.getName()));
821 player.inventory.addOrBank(new Item(listing.getItemId(),listing.getAmountLeft()));
823 }
824
825 return true;
826 }
827
828 return false;
829 }
830
831 public boolean delist(TradingPostListing listing) {
832 if(myListings.isEmpty()) {
833 return false;
834 }
835
836 if(!myListings.contains(listing)) {
837 return false;
838 }
839
840 if(!allListings.contains(listing)) {
841 return false;
842 }
843
844 return allListings.remove(listing) && myListings.remove(listing);
845 }
846
847 public void cleanUp() {
848 displayedPageListings.clear();
849 searchedListings.clear();
850 tradingPostSearch = null;
851 tradingPostSort = null;
852 listingToAdd = null;
853 listingToBuy = null;
854 page = 0;
855 }
856
857 public void testData() {
858 int addCounter = 0;
859 for(int i = 0; i < 1000; i++) {
860 ItemDefinition def = ItemDefinition.DEFINITIONS[new Random().nextInt(ItemDefinition.DEFINITIONS.length)];
861 if(def != null) {
862 TradingPostListing listing = new TradingPostListing(def.getId(), "John"+i);
863 listing.setInitialQuantity(i+1);
864 listing.setPrice(i+1);
865 allListings.add(listing);
866 addCounter++;
867 System.out.println("Adding listing: " + addCounter);
868 }
869 }
870 }
871
872 public void saveCoffer(final Coffer coffer) {
873 Thread.startVirtualThread(() -> {
874 try {
875 final String json = GsonUtils.JSON_PRETTY_ALLOW_NULL.get().toJson(coffer);
876
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);
881 }
882 if (!Files.exists(parent)) {
883 parent = Files.createDirectories(parent);
884 }
885
886 final Path tempFile = Files.createTempFile(parent, path.getFileName().toString(), ".tmp");
887 Files.writeString(tempFile, json, StandardCharsets.UTF_8);
888
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);
892 }
893 });
894 }
895
896 public CompletableFuture<Coffer> getCoffer(final String owner) {
897 final Coffer cachedCoffer = allCoffers.get(owner);
898 if (cachedCoffer != null) {
899 return CompletableFuture.completedFuture(cachedCoffer);
900 }
901
902 // retrieve coffer if coffer hasn't been retrieved since server start
903 return CompletableFuture.supplyAsync(() -> {
904 final Path path = Path.of("./data/profile/save/tradingpost/coffers/", owner, ".json");
905 if (!Files.exists(path)) {
906 return null;
907 }
908
909 final Gson gson = GsonUtils.JSON_ALLOW_NULL.get();
910 try (final BufferedReader reader = Files.newBufferedReader(path)) {
911 final Coffer coffer = gson.fromJson(reader, Coffer.class);
912 if (coffer != null) {
913 allCoffers.put(coffer.getOwner(), coffer);
914 return coffer;
915 }
916 } catch (final IOException e) {
917 logger.error("Failed reading coffer for \"" + owner + "\"", e);
918 }
919 return null;
920 });
921 }
922
923 public void saveListings(final String owner, final List<TradingPostListing> listings) {
924 Thread.startVirtualThread(() -> {
925 try {
926 final String json = GsonUtils.JSON_PRETTY_ALLOW_NULL.get().toJson(listings);
927
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);
932 }
933 if (!Files.exists(parent)) {
934 parent = Files.createDirectories(parent);
935 }
936
937 final Path tempFile = Files.createTempFile(parent, path.getFileName().toString(), ".tmp");
938 Files.writeString(tempFile, json, StandardCharsets.UTF_8);
939
940 Files.move(tempFile, path, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
941
942 } catch (final Exception ex) {
943 logger.error(String.format("Error while saving player=%s", player.getName()), ex);
944 }
945 });
946 }
947
948 public static void loadAllListings() {
949 try {
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) {
954 File toRead = null;
955 if(f.isDirectory()) {
956 toRead = Objects.requireNonNull(f.getAbsoluteFile().listFiles())[0];
957 }
958 try {
959 assert toRead != null;
960 final Gson gson = GsonUtils.JSON_ALLOW_NULL.get();
961 try (final BufferedReader reader = Files.newBufferedReader(Path.of(toRead.getPath()))) {
962 final List<TradingPostListing> listings = List.of(gson.fromJson(reader, TradingPostListing[].class));
963 allListings.addAll(listings);
964 } catch (final IOException e) {
965 logger.error("Failed reading listings", e);
966 }
967 } catch (Exception e) {
968 e.printStackTrace();
969 }
970 }
971 } catch (Exception e) {
972 e.printStackTrace();
973 }
974 }
975
976 public static void saveAllItemHistory() {
977 Path path = Paths.get("./data/profile/save/tradingpost/itemhistory.json");
978 File file = path.toFile();
979 file.getParentFile().setWritable(true);
980
981 if(!file.getParentFile().exists()) {
982 try {
983 file.getParentFile().mkdirs();
984 } catch (SecurityException e) {
985 System.out.println("Error while creating item history directory");
986 }
987 }
988 try(FileWriter writer = new FileWriter(file)) {
989 Gson builder = new GsonBuilder().setPrettyPrinting().create();
990 JsonObject object = new JsonObject();
991 object.add("itemhistory",builder.toJsonTree(itemHistories));
992 writer.write(builder.toJson(object));
993 } catch (Exception e) {
994 e.printStackTrace();
995 }
996 }
997
998 public static void saveRecentHistory() {
999 Path path = Paths.get("./data/profile/save/tradingpost/itemhistoryrecent.json");
1000 File file = path.toFile();
1001 file.getParentFile().setWritable(true);
1002
1003 if(!file.getParentFile().exists()) {
1004 try {
1005 file.getParentFile().mkdirs();
1006 } catch (SecurityException e) {
1007 System.out.println("Error while creating recent item history directory");
1008 }
1009 }
1010 try(FileWriter writer = new FileWriter(file)) {
1011 Gson builder = new GsonBuilder().setPrettyPrinting().create();
1012 JsonObject object = new JsonObject();
1013 object.add("recentitemhistory",builder.toJsonTree(recentlySoldItems));
1014 writer.write(builder.toJson(object));
1015 } catch (Exception e) {
1016 e.printStackTrace();
1017 }
1018 }
1019
1020 public static void loadItemHistory() {
1021 Path path = Paths.get("./data/profile/save/tradingpost/itemhistory.json");
1022 File file = path.toFile();
1023
1025 if(file.length() == 0) return;
1026
1027 try (FileReader fileReader = new FileReader(file)) {
1028 JsonParser fileParser = new JsonParser();
1029 Gson builder = new GsonBuilder()
1030 .create();
1031 JsonObject reader = (JsonObject) fileParser.parse(fileReader);
1032
1033 HashMap<String, List<ItemHistory>> itemHistory = builder.fromJson(reader.get("itemhistory"),
1034 new TypeToken<HashMap<String, List<ItemHistory>>>() {
1035 }.getType());
1036 itemHistories.putAll(itemHistory);
1037 } catch (Exception e) {
1038 e.printStackTrace();
1039 }
1040 }
1041
1042 public static void loadRecentItemHistory() {
1043 Path path = Paths.get("./data/profile/save/tradingpost/itemhistoryrecent.json");
1044 File file = path.toFile();
1045
1047 if(file.length() == 0) return;
1048
1049 try (FileReader fileReader = new FileReader(file)) {
1050 JsonParser fileParser = new JsonParser();
1051 Gson builder = new GsonBuilder()
1052 .create();
1053 JsonObject reader = (JsonObject) fileParser.parse(fileReader);
1054 ItemHistory[] temp = builder.fromJson(reader.get("recentitemhistory").getAsJsonArray(), ItemHistory[].class);
1055 recentlySoldItems.addAll(Arrays.asList(temp));
1056 } catch (Exception e) {
1057 e.printStackTrace();
1058 }
1059 }
1060
1061 public static void createFileAndDirIfNotExists(File file) {
1062 if (!file.getParentFile().exists() || !file.exists()) {
1063 try {
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);
1070 }
1071 }
1072 }
1073
1074 public List<ItemHistory> getPlayersItemHistory() {
1075 return playersItemHistory;
1076 }
1077
1078
1081 private final String text;
1082 private final AhoCorasickDoubleArrayTrie<String> acdat = new AhoCorasickDoubleArrayTrie<>();
1083
1085 this.searchType = searchType;
1086 this.text = text.toLowerCase();
1087 }
1088 }
1089
1095
1112
1119}
TradingPostActiveListingSearch(TradingPostActiveListingSearchType searchType, String text)
final AhoCorasickDoubleArrayTrie< String > acdat
final TradingPostActiveListingSearchType searchType
final String text
TradingPostActiveListingSort(TradingPostSortType postSortType)
TradingPostSortType postSortType
TradingPostActiveListingSort setPostSortType(TradingPostSortType postSortType)
TradingPostSortType getPostSortType()
List< TradingPostListing > searchedListings
The current searched listings.
void testData()
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 searchPlayerInput()
void toggleSellingOverlayVisibilityAndOpen(boolean isHidden)
void cleanUp()
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()
void previousPage()
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
final Player player
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 sort()
void sendOverviewWidgetVisibility(int i, boolean isHidden)
void toggleBuyingPageWidgetVisibility(int i, boolean isHidden)
void sortQuantity()
void nextPage()
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()
void sortPrice()
void refresh()
CompletableFuture< Coffer > getCoffer(final String owner)
int getAmountLeft()
int getPrice()
int getInitialQuantity()
void setPrice(Player player, int price)
void setInitialQuantity(int initialQuantity)
int getItemId()
int getAmountSold()
String getSeller()
A game representing a cyclic unit of work.
Definition Task.java:11
Represents the game world.
Definition World.java:46
static void schedule(Task task)
Submits a new event.
Definition World.java:247
static Optional< Player > search(String name)
Gets a player by name.
Definition World.java:147
This class represents a character controlled by a player.
Definition Player.java:125
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.
static ItemDefinition[] DEFINITIONS
An array of item definitions.
The container class that represents an item that can be interacted with.
Definition Item.java:21
final int getId()
Gets the identification of this item.
Definition Item.java:324
ItemDefinition getDefinition()
Gets the item definition for the item identifier.
Definition Item.java:315
Sends a chatbox input prompt that accepts full text with spaces.
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.
Definition Utility.java:27
static String formatDigits(final int amount)
Formats digits for integers.
Definition Utility.java:41
ITEM
MOST_RECENT
PLAYER
HIGHEST_QUANTITY
HIGHEST_PRICE
LOWEST_QUANTITY
LOWEST_PRICE
static final ThreadLocal< Gson > JSON_PRETTY_ALLOW_NULL
static final ThreadLocal< Gson > JSON_ALLOW_NULL