From 713f66b8c5802d543d965e70fa83a0ca3499cfd0 Mon Sep 17 00:00:00 2001 From: Kiran Hart Date: Wed, 1 Sep 2021 16:39:10 -0400 Subject: [PATCH] 2.32.0 --- pom.xml | 14 +++- .../ca/tweetzy/auctionhouse/AuctionHouse.java | 11 ++- .../auctionhouse/auction/AuctionStat.java | 23 ++++++ .../auctionhouse/auction/AuctionedItem.java | 25 ++++++ .../auctionhouse/commands/CommandStatus.java | 9 ++- .../auctionhouse/database/DataManager.java | 71 ++++++++++++++-- .../migrations/_9_StatsMigration.java | 35 ++++++++ .../auctionhouse/guis/GUIAuctionHouse.java | 6 ++ .../ca/tweetzy/auctionhouse/guis/GUIBid.java | 6 ++ .../auctionhouse/guis/GUISellItem.java | 49 +++++++++++- .../tweetzy/auctionhouse/guis/GUIStats.java | 56 +++++++++++++ .../guis/confirmation/GUIConfirmBid.java | 42 ++++++++-- .../listeners/AuctionListeners.java | 21 +++++ .../managers/AuctionStatManager.java | 80 +++++++++++++++++++ .../auctionhouse/settings/LocaleSettings.java | 1 + .../auctionhouse/settings/Settings.java | 34 ++++++++ .../auctionhouse/tasks/AutoSaveTask.java | 1 + .../auctionhouse/tasks/TickAuctionsTask.java | 8 +- src/main/test/Test.java | 17 +++- 19 files changed, 478 insertions(+), 31 deletions(-) create mode 100644 src/main/java/ca/tweetzy/auctionhouse/auction/AuctionStat.java create mode 100644 src/main/java/ca/tweetzy/auctionhouse/database/migrations/_9_StatsMigration.java create mode 100644 src/main/java/ca/tweetzy/auctionhouse/guis/GUIStats.java create mode 100644 src/main/java/ca/tweetzy/auctionhouse/managers/AuctionStatManager.java diff --git a/pom.xml b/pom.xml index 9554169..3943c2d 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ 4.0.0 ca.tweetzy auctionhouse - 2.31.0 + 2.32.0 UTF-8 @@ -99,9 +99,17 @@ ca.tweetzy.core ${project.groupId}.${project.artifactId}.core + + com.zaxxer.hikari + ${project.groupId}.${project.artifactId}.lib.hikari + + + org.slf4j + ${project.groupId}.${project.artifactId}.lib.slf4j + co.aikar.taskchain - ${project.groupId}.${project.artifactId}.taskchain + ${project.groupId}.${project.artifactId}.lib.taskchain @@ -154,7 +162,7 @@ ca.tweetzy tweetycore - 2.8.0 + 2.9.0 org.projectlombok diff --git a/src/main/java/ca/tweetzy/auctionhouse/AuctionHouse.java b/src/main/java/ca/tweetzy/auctionhouse/AuctionHouse.java index 00323a4..3ae27ca 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/AuctionHouse.java +++ b/src/main/java/ca/tweetzy/auctionhouse/AuctionHouse.java @@ -3,6 +3,7 @@ package ca.tweetzy.auctionhouse; import ca.tweetzy.auctionhouse.api.UpdateChecker; import ca.tweetzy.auctionhouse.api.hook.PlaceholderAPI; import ca.tweetzy.auctionhouse.auction.AuctionPlayer; +import ca.tweetzy.auctionhouse.auction.AuctionStat; import ca.tweetzy.auctionhouse.commands.*; import ca.tweetzy.auctionhouse.database.DataManager; import ca.tweetzy.auctionhouse.database.migrations.*; @@ -80,6 +81,9 @@ public class AuctionHouse extends TweetyPlugin { @Getter private AuctionBanManager auctionBanManager; + @Getter + private AuctionStatManager auctionStatManager; + @Getter private DatabaseConnector databaseConnector; @@ -146,7 +150,8 @@ public class AuctionHouse extends TweetyPlugin { new _5_TransactionChangeMigration(), new _6_BigIntMigration(), new _7_TransactionBigIntMigration(), - new _8_ItemPerWorldMigration() + new _8_ItemPerWorldMigration(), + new _9_StatsMigration() ); dataMigrationManager.runMigrations(); @@ -167,6 +172,9 @@ public class AuctionHouse extends TweetyPlugin { this.auctionBanManager = new AuctionBanManager(); this.auctionBanManager.loadBans(); + this.auctionStatManager = new AuctionStatManager(); + this.auctionStatManager.loadStats(); + // gui manager this.guiManager.init(); @@ -241,6 +249,7 @@ public class AuctionHouse extends TweetyPlugin { this.auctionItemManager.end(); this.filterManager.saveFilterWhitelist(false); this.auctionBanManager.saveBans(false); + this.auctionStatManager.saveStats(); this.dataManager.close(); } diff --git a/src/main/java/ca/tweetzy/auctionhouse/auction/AuctionStat.java b/src/main/java/ca/tweetzy/auctionhouse/auction/AuctionStat.java new file mode 100644 index 0000000..0e06890 --- /dev/null +++ b/src/main/java/ca/tweetzy/auctionhouse/auction/AuctionStat.java @@ -0,0 +1,23 @@ +package ca.tweetzy.auctionhouse.auction; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +/** + * The current file has been created by Kiran Hart + * Date Created: September 01 2021 + * Time Created: 3:23 p.m. + * Usage of any code found within this class is prohibited unless given explicit permission otherwise + */ +@AllArgsConstructor +@Getter +@Setter +public final class AuctionStat { + + private Created created; + private Sold sold; + private Expired expired; + private Earned earned; + private Spent spent; +} diff --git a/src/main/java/ca/tweetzy/auctionhouse/auction/AuctionedItem.java b/src/main/java/ca/tweetzy/auctionhouse/auction/AuctionedItem.java index a8054e6..2f21dfb 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/auction/AuctionedItem.java +++ b/src/main/java/ca/tweetzy/auctionhouse/auction/AuctionedItem.java @@ -83,6 +83,31 @@ public class AuctionedItem { this.expiresAt = expiresAt; } + public ItemStack getBidStack() { + ItemStack itemStack = this.item.clone(); + itemStack.setAmount(Math.max(this.item.getAmount(), 1)); + ItemMeta meta = itemStack.hasItemMeta() ? itemStack.getItemMeta() : Bukkit.getItemFactory().getItemMeta(itemStack.getType()); + List lore = (meta.hasLore()) ? meta.getLore() : new ArrayList<>(); + lore.addAll(TextUtils.formatText(Settings.AUCTION_STACK_DETAILS_HEADER.getStringList())); + lore.addAll(TextUtils.formatText(Settings.AUCTION_STACK_DETAILS_SELLER.getStringList().stream().map(s -> s.replace("%seller%", this.ownerName)).collect(Collectors.toList()))); + lore.addAll(TextUtils.formatText(Settings.AUCTION_STACK_DETAILS_CURRENT_PRICE.getStringList().stream().map(s -> s.replace("%currentprice%", Settings.USE_SHORT_NUMBERS_ON_ITEMS.getBoolean() ? AuctionAPI.getInstance().getFriendlyNumber(this.currentPrice) : AuctionAPI.getInstance().formatNumber(this.currentPrice))).collect(Collectors.toList()))); + lore.addAll(TextUtils.formatText(Settings.AUCTION_STACK_DETAILS_HIGHEST_BIDDER.getStringList().stream().map(s -> s.replace("%highestbidder%", this.highestBidder.equals(this.owner) ? AuctionHouse.getInstance().getLocale().getMessage("auction.nobids").getMessage() : this.highestBidderName)).collect(Collectors.toList()))); + + long[] times = AuctionAPI.getInstance().getRemainingTimeValues((this.expiresAt - System.currentTimeMillis()) / 1000); + lore.addAll(TextUtils.formatText(Settings.AUCTION_STACK_DETAILS_TIME_LEFT.getStringList().stream().map(s -> s + .replace("%remaining_days%", String.valueOf(times[0])) + .replace("%remaining_hours%", String.valueOf(times[1])) + .replace("%remaining_minutes%", String.valueOf(times[2])) + .replace("%remaining_seconds%", String.valueOf(times[3])) + ).collect(Collectors.toList()))); + + lore.addAll(TextUtils.formatText(Settings.AUCTION_STACK_PURCHASE_CONTROL_FOOTER.getStringList())); + + meta.setLore(lore); + itemStack.setItemMeta(meta); + return itemStack; + } + public ItemStack getDisplayStack(AuctionStackType type) { ItemStack itemStack = this.item.clone(); itemStack.setAmount(Math.max(this.item.getAmount(), 1)); diff --git a/src/main/java/ca/tweetzy/auctionhouse/commands/CommandStatus.java b/src/main/java/ca/tweetzy/auctionhouse/commands/CommandStatus.java index 80ad387..054a761 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/commands/CommandStatus.java +++ b/src/main/java/ca/tweetzy/auctionhouse/commands/CommandStatus.java @@ -1,6 +1,8 @@ package ca.tweetzy.auctionhouse.commands; +import ca.tweetzy.auctionhouse.AuctionHouse; import ca.tweetzy.auctionhouse.api.AuctionAPI; +import ca.tweetzy.auctionhouse.guis.GUIStats; import ca.tweetzy.core.commands.AbstractCommand; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -16,15 +18,14 @@ import java.util.List; public class CommandStatus extends AbstractCommand { public CommandStatus() { - super(CommandType.CONSOLE_OK, "status"); + super(CommandType.PLAYER_ONLY, "status", "stats"); } @Override protected ReturnType runCommand(CommandSender sender, String... args) { if (AuctionAPI.tellMigrationStatus(sender)) return ReturnType.FAILURE; Player player = (Player) sender; - - + AuctionHouse.getInstance().getGuiManager().showGUI(player, new GUIStats(player)); return ReturnType.SUCCESS; } @@ -40,7 +41,7 @@ public class CommandStatus extends AbstractCommand { @Override public String getDescription() { - return "Return plugin statistics"; + return "Open the auction house statistics"; } @Override diff --git a/src/main/java/ca/tweetzy/auctionhouse/database/DataManager.java b/src/main/java/ca/tweetzy/auctionhouse/database/DataManager.java index e846f32..7c588f3 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/database/DataManager.java +++ b/src/main/java/ca/tweetzy/auctionhouse/database/DataManager.java @@ -18,10 +18,8 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -182,7 +180,7 @@ public class DataManager extends DataManagerAbstract { public void getItems(Callback> callback) { ArrayList items = new ArrayList<>(); - this.databaseConnector.connect(connection -> { + this.async(() -> this.databaseConnector.connect(connection -> { try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + this.getTablePrefix() + "auctions")) { ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { @@ -193,12 +191,12 @@ public class DataManager extends DataManagerAbstract { } catch (Exception e) { resolveCallback(callback, e); } - }); + })); } public void getTransactions(Callback> callback) { ArrayList transactions = new ArrayList<>(); - this.databaseConnector.connect(connection -> { + this.async(() -> this.databaseConnector.connect(connection -> { try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + this.getTablePrefix() + "transactions")) { ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { @@ -209,7 +207,7 @@ public class DataManager extends DataManagerAbstract { } catch (Exception e) { resolveCallback(callback, e); } - }); + })); } public void insertTransaction(Transaction transaction, Callback callback) { @@ -285,6 +283,63 @@ public class DataManager extends DataManagerAbstract { }); } + public void getStats(Callback>> callback) { + Map> stats = new HashMap<>(); + this.async(() -> this.databaseConnector.connect(connection -> { + try (PreparedStatement statement = connection.prepareStatement("SELECT * FROM " + this.getTablePrefix() + "stats")) { + ResultSet resultSet = statement.executeQuery(); + while (resultSet.next()) { + stats.put(UUID.fromString(resultSet.getString("id")), new AuctionStat<>( + resultSet.getInt("auctions_created"), + resultSet.getInt("auctions_sold"), + resultSet.getInt("auctions_expired"), + resultSet.getDouble("money_earned"), + resultSet.getDouble("money_spent") + )); + } + + callback.accept(null, stats); + } catch (Exception e) { + resolveCallback(callback, e); + } + })); + } + + public void updateStats(Map> stats, UpdateCallback callback) { + this.databaseConnector.connect(connection -> { + connection.setAutoCommit(false); + SQLException error = null; + + PreparedStatement statement = connection.prepareStatement("REPLACE into " + this.getTablePrefix() + "stats (id, auctions_created, auctions_sold, auctions_expired, money_earned, money_spent) VALUES(?,?,?,?,?,?)"); + for (Map.Entry> value: stats.entrySet()) { + try { + statement.setString(1, value.getKey().toString()); + statement.setInt(2, value.getValue().getCreated()); + statement.setInt(3, value.getValue().getSold()); + statement.setInt(4, value.getValue().getExpired()); + statement.setDouble(5, value.getValue().getEarned()); + statement.setDouble(6, value.getValue().getSpent()); + statement.addBatch(); + } catch (SQLException e) { + error = e; + break; + } + } + + statement.executeBatch(); + + if (error == null) { + connection.commit(); + resolveUpdateCallback(callback, null); + } else { + connection.rollback(); + resolveUpdateCallback(callback, error); + } + + connection.setAutoCommit(true); + }); + } + public void insertAuctionAsync(AuctionedItem item, Callback callback) { this.thread.execute(() -> insertAuction(item, callback)); } diff --git a/src/main/java/ca/tweetzy/auctionhouse/database/migrations/_9_StatsMigration.java b/src/main/java/ca/tweetzy/auctionhouse/database/migrations/_9_StatsMigration.java new file mode 100644 index 0000000..c281373 --- /dev/null +++ b/src/main/java/ca/tweetzy/auctionhouse/database/migrations/_9_StatsMigration.java @@ -0,0 +1,35 @@ +package ca.tweetzy.auctionhouse.database.migrations; + +import ca.tweetzy.auctionhouse.AuctionHouse; +import ca.tweetzy.core.database.DataMigration; +import ca.tweetzy.core.database.MySQLConnector; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * The current file has been created by Kiran Hart + * Date Created: August 24 2021 + * Time Created: 4:04 p.m. + * Usage of any code found within this class is prohibited unless given explicit permission otherwise + */ +public final class _9_StatsMigration extends DataMigration { + + public _9_StatsMigration() { + super(9); + } + + @Override + public void migrate(Connection connection, String tablePrefix) throws SQLException { + try (Statement statement = connection.createStatement()) { + statement.execute("CREATE TABLE " + tablePrefix + "stats (" + + "id VARCHAR(36) PRIMARY KEY, " + + "auctions_created INT NOT NULL, " + + "auctions_sold INT NOT NULL, " + + "auctions_expired INT NOT NULL, " + + "money_earned DOUBLE NOT NULL, " + + "money_spent DOUBLE NOT NULL )"); + } + } +} diff --git a/src/main/java/ca/tweetzy/auctionhouse/guis/GUIAuctionHouse.java b/src/main/java/ca/tweetzy/auctionhouse/guis/GUIAuctionHouse.java index ca8fff0..033a19b 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/guis/GUIAuctionHouse.java +++ b/src/main/java/ca/tweetzy/auctionhouse/guis/GUIAuctionHouse.java @@ -228,6 +228,12 @@ public class GUIAuctionHouse extends Gui { if (value > auctionItem.getCurrentPrice()) { newBiddingAmount = value; } else { + if (Settings.BID_MUST_BE_HIGHER_THAN_PREVIOUS.getBoolean()) { + e.manager.showGUI(e.player, new GUIAuctionHouse(this.auctionPlayer)); + AuctionHouse.getInstance().getLocale().getMessage("pricing.bidmusthigherthanprevious").processPlaceholder("current_bid", AuctionAPI.getInstance().formatNumber(auctionItem.getCurrentPrice())).sendPrefixedMessage(e.player); + return; + } + newBiddingAmount = auctionItem.getCurrentPrice() + value; } } else { diff --git a/src/main/java/ca/tweetzy/auctionhouse/guis/GUIBid.java b/src/main/java/ca/tweetzy/auctionhouse/guis/GUIBid.java index d32eb30..2c8ebaa 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/guis/GUIBid.java +++ b/src/main/java/ca/tweetzy/auctionhouse/guis/GUIBid.java @@ -73,6 +73,12 @@ public class GUIBid extends Gui { if (value > this.auctionItem.getCurrentPrice()) { newBiddingAmount = value; } else { + if (Settings.BID_MUST_BE_HIGHER_THAN_PREVIOUS.getBoolean()) { + e.manager.showGUI(e.player, new GUIAuctionHouse(this.auctionPlayer)); + AuctionHouse.getInstance().getLocale().getMessage("pricing.bidmusthigherthanprevious").processPlaceholder("current_bid", AuctionAPI.getInstance().formatNumber(auctionItem.getCurrentPrice())).sendPrefixedMessage(e.player); + return; + } + newBiddingAmount = this.auctionItem.getCurrentPrice() + value; } } else { diff --git a/src/main/java/ca/tweetzy/auctionhouse/guis/GUISellItem.java b/src/main/java/ca/tweetzy/auctionhouse/guis/GUISellItem.java index c7070f2..237b7ea 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/guis/GUISellItem.java +++ b/src/main/java/ca/tweetzy/auctionhouse/guis/GUISellItem.java @@ -13,11 +13,14 @@ import ca.tweetzy.core.input.PlayerChatInput; import ca.tweetzy.core.utils.NumberUtils; import ca.tweetzy.core.utils.PlayerUtils; import ca.tweetzy.core.utils.TextUtils; +import org.bukkit.ChatColor; import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; import java.util.Arrays; import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; /** * The current file has been created by Kiran Hart @@ -246,6 +249,48 @@ public class GUISellItem extends Gui { } setButton(3, 7, ConfigurationItemHelper.createConfigurationItem(Settings.GUI_SELL_ITEMS_CONFIRM_LISTING_ITEM.getString(), Settings.GUI_SELL_ITEMS_CONFIRM_LISTING_NAME.getString(), Settings.GUI_SELL_ITEMS_CONFIRM_LISTING_LORE.getStringList(), null), e -> { + // if the item in the sell slot is null then stop the listing + if (getItem(1, 4) == null || getItem(1, 4).getType() == XMaterial.AIR.parseMaterial()) return; + setTheItemToBeListed(); + + if (Settings.MAKE_BLOCKED_ITEMS_A_WHITELIST.getBoolean()) { + if (!Settings.BLOCKED_ITEMS.getStringList().contains(this.itemToBeListed.getType().name())) { + AuctionHouse.getInstance().getLocale().getMessage("general.blockeditem").processPlaceholder("item", this.itemToBeListed.getType().name()).sendPrefixedMessage(e.player); + return; + } + } else { + if (Settings.BLOCKED_ITEMS.getStringList().contains(this.itemToBeListed.getType().name())) { + AuctionHouse.getInstance().getLocale().getMessage("general.blockeditem").processPlaceholder("item", this.itemToBeListed.getType().name()).sendPrefixedMessage(e.player); + return; + } + } + + boolean blocked = false; + + String itemName = ChatColor.stripColor(AuctionAPI.getInstance().getItemName(this.itemToBeListed).toLowerCase()); + List itemLore = AuctionAPI.getInstance().getItemLore(this.itemToBeListed).stream().map(line -> ChatColor.stripColor(line.toLowerCase())).collect(Collectors.toList()); + + // Check for blocked names and lore + for (String s : Settings.BLOCKED_ITEM_NAMES.getStringList()) { + if (AuctionAPI.getInstance().match(s, itemName)) { + AuctionHouse.getInstance().getLocale().getMessage("general.blockedname").sendPrefixedMessage(e.player); + blocked = true; + } + } + + if (!itemLore.isEmpty() && !blocked) { + for (String s : Settings.BLOCKED_ITEM_LORES.getStringList()) { + for (String line : itemLore) { + if (AuctionAPI.getInstance().match(s, line)) { + AuctionHouse.getInstance().getLocale().getMessage("general.blockedlore").sendPrefixedMessage(e.player); + blocked = true; + } + } + } + } + + if (blocked) return; + // are they even allowed to sell more items if (this.auctionPlayer.isAtSellLimit()) { AuctionHouse.getInstance().getLocale().getMessage("general.sellinglimit").sendPrefixedMessage(e.player); @@ -257,10 +302,6 @@ public class GUISellItem extends Gui { return; } - // if the item in the sell slot is null then stop the listing - if (getItem(1, 4) == null || getItem(1, 4).getType() == XMaterial.AIR.parseMaterial()) return; - setTheItemToBeListed(); - AuctionAPI.getInstance().listAuction( e.player, this.itemToBeListed.clone(), diff --git a/src/main/java/ca/tweetzy/auctionhouse/guis/GUIStats.java b/src/main/java/ca/tweetzy/auctionhouse/guis/GUIStats.java new file mode 100644 index 0000000..bbfc521 --- /dev/null +++ b/src/main/java/ca/tweetzy/auctionhouse/guis/GUIStats.java @@ -0,0 +1,56 @@ +package ca.tweetzy.auctionhouse.guis; + +import ca.tweetzy.auctionhouse.AuctionHouse; +import ca.tweetzy.auctionhouse.api.AuctionAPI; +import ca.tweetzy.auctionhouse.auction.AuctionStat; +import ca.tweetzy.auctionhouse.helpers.ConfigurationItemHelper; +import ca.tweetzy.auctionhouse.managers.AuctionStatManager; +import ca.tweetzy.auctionhouse.settings.Settings; +import ca.tweetzy.core.gui.Gui; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The current file has been created by Kiran Hart + * Date Created: September 01 2021 + * Time Created: 2:57 p.m. + * Usage of any code found within this class is prohibited unless given explicit permission otherwise + */ +public final class GUIStats extends Gui { + + private final Player player; + + public GUIStats(final Player player) { + this.player = player; + setTitle(Settings.GUI_STATS_TITLE.getString()); + setDefaultItem(Settings.GUI_STATS_BG_ITEM.getMaterial().parseItem()); + setUseLockedCells(true); + setAcceptsItems(false); + setAllowDrops(false); + setRows(3); + draw(); + } + + private void draw() { + + final AuctionStat playerStats = AuctionHouse.getInstance().getAuctionStatManager().getPlayerStats(this.player); + + setItem(1, 3, ConfigurationItemHelper.createConfigurationItem(Settings.GUI_STATS_ITEMS_PERSONAL_USE_HEAD.getBoolean() ? AuctionAPI.getInstance().getPlayerHead(player.getName()) : Settings.GUI_STATS_ITEMS_PERSONAL_ITEM.getMaterial().parseItem(), Settings.GUI_STATS_ITEMS_PERSONAL_NAME.getString(), Settings.GUI_STATS_ITEMS_PERSONAL_LORE.getStringList(), new HashMap() {{ + put("%auctions_created%", playerStats.getCreated()); + put("%auctions_sold%", playerStats.getSold()); + put("%auctions_expired%", playerStats.getExpired()); + put("%auctions_money_spent%", AuctionAPI.getInstance().formatNumber(playerStats.getSpent())); + put("%auctions_money_earned%", AuctionAPI.getInstance().formatNumber(playerStats.getEarned())); + }})); + + setItem(1, 5, ConfigurationItemHelper.createConfigurationItem(Settings.GUI_STATS_ITEMS_GLOBAL_ITEM.getString(), Settings.GUI_STATS_ITEMS_GLOBAL_NAME.getString(), Settings.GUI_STATS_ITEMS_GLOBAL_LORE.getStringList(), new HashMap() {{ + put("%auctions_created%", (int) AuctionHouse.getInstance().getAuctionStatManager().getGlobalStat(AuctionStatManager.GlobalAuctionStatType.CREATED)); + put("%auctions_sold%", (int) AuctionHouse.getInstance().getAuctionStatManager().getGlobalStat(AuctionStatManager.GlobalAuctionStatType.SOLD)); + put("%auctions_expired%", (int) AuctionHouse.getInstance().getAuctionStatManager().getGlobalStat(AuctionStatManager.GlobalAuctionStatType.EXPIRED)); + put("%auctions_money_spent%", AuctionAPI.getInstance().formatNumber(AuctionHouse.getInstance().getAuctionStatManager().getGlobalStat(AuctionStatManager.GlobalAuctionStatType.SPENT))); + }})); + } +} diff --git a/src/main/java/ca/tweetzy/auctionhouse/guis/confirmation/GUIConfirmBid.java b/src/main/java/ca/tweetzy/auctionhouse/guis/confirmation/GUIConfirmBid.java index bd4ab58..2621097 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/guis/confirmation/GUIConfirmBid.java +++ b/src/main/java/ca/tweetzy/auctionhouse/guis/confirmation/GUIConfirmBid.java @@ -14,6 +14,7 @@ import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.event.inventory.ClickType; import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitTask; import java.util.Objects; @@ -25,9 +26,10 @@ import java.util.Objects; */ public class GUIConfirmBid extends Gui { - final AuctionPlayer auctionPlayer; - final AuctionedItem auctionItem; - final double bidAmount; + private final AuctionPlayer auctionPlayer; + private final AuctionedItem auctionItem; + private final double bidAmount; + private BukkitTask bukkitTask; public GUIConfirmBid(AuctionPlayer auctionPlayer, AuctionedItem auctionItem) { this(auctionPlayer, auctionItem, -1); @@ -41,30 +43,57 @@ public class GUIConfirmBid extends Gui { setAcceptsItems(false); setRows(1); draw(); + + setOnOpen(open -> { + if (Settings.USE_LIVE_BID_NUMBER_IN_CONFIRM_GUI.getBoolean()) { + this.bukkitTask = Bukkit.getServer().getScheduler().runTaskTimerAsynchronously(AuctionHouse.getInstance(), this::placeAuctionItem, 0L, (long) 20 * Settings.LIVE_BID_NUMBER_IN_CONFIRM_GUI_RATE.getInt()); + } + }); + + setOnClose(close -> cleanup()); + } + + private void placeAuctionItem() { + setItem(0, 4, this.auctionItem.getBidStack()); + } + + private void cleanup() { + if (bukkitTask != null) { + bukkitTask.cancel(); + } } private void draw() { setItems(0, 3, new TItemBuilder(Objects.requireNonNull(Settings.GUI_CONFIRM_BID_YES_ITEM.getMaterial().parseMaterial())).setName(Settings.GUI_CONFIRM_BID_YES_NAME.getString()).setLore(Settings.GUI_CONFIRM_BID_YES_LORE.getStringList()).toItemStack()); - setItem(0, 4, this.auctionItem.getItem()); + placeAuctionItem(); setItems(5, 8, new TItemBuilder(Objects.requireNonNull(Settings.GUI_CONFIRM_BID_NO_ITEM.getMaterial().parseMaterial())).setName(Settings.GUI_CONFIRM_BID_NO_NAME.getString()).setLore(Settings.GUI_CONFIRM_BID_NO_LORE.getStringList()).toItemStack()); - setActionForRange(5, 8, ClickType.LEFT, e -> e.manager.showGUI(e.player, new GUIAuctionHouse(this.auctionPlayer))); + setActionForRange(5, 8, ClickType.LEFT, e -> { + cleanup(); + e.manager.showGUI(e.player, new GUIAuctionHouse(this.auctionPlayer)); + }); setActionForRange(0, 3, ClickType.LEFT, e -> { // Re-select the item to ensure that it's available AuctionedItem located = AuctionHouse.getInstance().getAuctionItemManager().getItem(this.auctionItem.getId()); if (located == null) { + cleanup(); e.manager.showGUI(e.player, new GUIAuctionHouse(this.auctionPlayer)); return; } double toIncrementBy = this.bidAmount == -1 ? auctionItem.getBidIncrementPrice() : this.bidAmount; - double newBiddingAmount = 0; if (Settings.USE_REALISTIC_BIDDING.getBoolean()) { if (toIncrementBy > this.auctionItem.getCurrentPrice()) { newBiddingAmount = toIncrementBy; } else { + if (Settings.BID_MUST_BE_HIGHER_THAN_PREVIOUS.getBoolean()) { + e.manager.showGUI(e.player, new GUIAuctionHouse(this.auctionPlayer)); + AuctionHouse.getInstance().getLocale().getMessage("pricing.bidmusthigherthanprevious").processPlaceholder("current_bid", AuctionAPI.getInstance().formatNumber(auctionItem.getCurrentPrice())).sendPrefixedMessage(e.player); + return; + } + newBiddingAmount = this.auctionItem.getCurrentPrice() + toIncrementBy; } } else { @@ -115,6 +144,7 @@ public class GUIConfirmBid extends Gui { .sendPrefixedMessage(owner.getPlayer()); } + cleanup(); e.manager.showGUI(e.player, new GUIAuctionHouse(this.auctionPlayer)); }); } diff --git a/src/main/java/ca/tweetzy/auctionhouse/listeners/AuctionListeners.java b/src/main/java/ca/tweetzy/auctionhouse/listeners/AuctionListeners.java index 4e38743..a0e210c 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/listeners/AuctionListeners.java +++ b/src/main/java/ca/tweetzy/auctionhouse/listeners/AuctionListeners.java @@ -5,6 +5,7 @@ import ca.tweetzy.auctionhouse.api.AuctionAPI; import ca.tweetzy.auctionhouse.api.events.AuctionEndEvent; import ca.tweetzy.auctionhouse.api.events.AuctionStartEvent; import ca.tweetzy.auctionhouse.auction.AuctionSaleType; +import ca.tweetzy.auctionhouse.auction.AuctionStat; import ca.tweetzy.auctionhouse.settings.Settings; import ca.tweetzy.auctionhouse.transaction.Transaction; import org.bukkit.Bukkit; @@ -23,6 +24,10 @@ public class AuctionListeners implements Listener { @EventHandler public void onAuctionStart(AuctionStartEvent e) { + AuctionHouse.getInstance().getAuctionStatManager().insertOrUpdate(e.getSeller(), new AuctionStat<>( + 1, 0, 0, 0D, 0D + )); + if (Settings.DISCORD_ENABLED.getBoolean() && Settings.DISCORD_ALERT_ON_AUCTION_START.getBoolean()) { Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(AuctionHouse.getInstance(), () -> { Settings.DISCORD_WEBHOOKS.getStringList().forEach(hook -> { @@ -42,6 +47,22 @@ public class AuctionListeners implements Listener { @EventHandler public void onAuctionEnd(AuctionEndEvent e) { + AuctionHouse.getInstance().getAuctionStatManager().insertOrUpdate(e.getOriginalOwner(), new AuctionStat<>( + 0, + 1, + 0, + e.getSaleType() == AuctionSaleType.USED_BIDDING_SYSTEM ? e.getAuctionItem().getCurrentPrice() : e.getAuctionItem().getBasePrice(), + 0D + )); + + AuctionHouse.getInstance().getAuctionStatManager().insertOrUpdate(e.getBuyer(), new AuctionStat<>( + 0, + 0, + 0, + 0D, + e.getSaleType() == AuctionSaleType.USED_BIDDING_SYSTEM ? e.getAuctionItem().getCurrentPrice() : e.getAuctionItem().getBasePrice() + )); + Bukkit.getServer().getScheduler().runTaskLaterAsynchronously(AuctionHouse.getInstance(), () -> { if (Settings.RECORD_TRANSACTIONS.getBoolean()) { diff --git a/src/main/java/ca/tweetzy/auctionhouse/managers/AuctionStatManager.java b/src/main/java/ca/tweetzy/auctionhouse/managers/AuctionStatManager.java new file mode 100644 index 0000000..074c8f5 --- /dev/null +++ b/src/main/java/ca/tweetzy/auctionhouse/managers/AuctionStatManager.java @@ -0,0 +1,80 @@ +package ca.tweetzy.auctionhouse.managers; + +import ca.tweetzy.auctionhouse.AuctionHouse; +import ca.tweetzy.auctionhouse.auction.AuctionStat; +import lombok.Getter; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The current file has been created by Kiran Hart + * Date Created: September 01 2021 + * Time Created: 2:58 p.m. + * Usage of any code found within this class is prohibited unless given explicit permission otherwise + */ +public final class AuctionStatManager { + + public static enum GlobalAuctionStatType { + CREATED, + EXPIRED, + SOLD, + SPENT + } + + @Getter + private final ConcurrentHashMap> stats = new ConcurrentHashMap<>(); + + public void loadStats() { + AuctionHouse.getInstance().getDataManager().getStats((error, stats) -> { + if (error == null) { + this.stats.putAll(stats); + } + }); + } + + public AuctionStat getPlayerStats(final Player player) { + return this.stats.getOrDefault(player.getUniqueId(), new AuctionStat<>(0, 0, 0, 0D, 0D)); + } + + public double getGlobalStat(final GlobalAuctionStatType globalAuctionStatType) { + double total = 0D; + switch (globalAuctionStatType) { + case CREATED: + total += stats.values().stream().mapToInt(AuctionStat::getCreated).sum(); + break; + case EXPIRED: + total += stats.values().stream().mapToInt(AuctionStat::getExpired).sum(); + break; + case SOLD: + total += stats.values().stream().mapToInt(AuctionStat::getSold).sum(); + break; + case SPENT: + total += stats.values().stream().mapToDouble(AuctionStat::getSpent).sum(); + break; + } + return total; + } + + public void saveStats() { + AuctionHouse.getInstance().getDataManager().updateStats(this.stats, null); + } + + public void insertOrUpdate(final OfflinePlayer player, final AuctionStat stats) { + if (!this.stats.containsKey(player.getUniqueId())) { + this.stats.put(player.getUniqueId(), stats); + return; + } + + final AuctionStat foundStats = this.stats.get(player.getUniqueId()); + foundStats.setCreated(foundStats.getCreated() + stats.getCreated()); + foundStats.setSold(foundStats.getSold() + stats.getSold()); + foundStats.setExpired(foundStats.getExpired() + stats.getExpired()); + foundStats.setEarned(foundStats.getEarned() + stats.getEarned()); + foundStats.setSpent(foundStats.getSpent() + stats.getSpent()); + + this.stats.put(player.getUniqueId(), foundStats); + } +} diff --git a/src/main/java/ca/tweetzy/auctionhouse/settings/LocaleSettings.java b/src/main/java/ca/tweetzy/auctionhouse/settings/LocaleSettings.java index 0f42adc..2803976 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/settings/LocaleSettings.java +++ b/src/main/java/ca/tweetzy/auctionhouse/settings/LocaleSettings.java @@ -57,6 +57,7 @@ public class LocaleSettings { languageNodes.put("pricing.basepricetoolow", "&cThe buy now price must be higher than the starting bid."); languageNodes.put("pricing.moneyremove", "&c&l- $%price% &7(%player_balance%)"); languageNodes.put("pricing.moneyadd", "&a&l+ $%price% &7(%player_balance%)"); + languageNodes.put("pricing.bidmusthigherthanprevious", "&cYour bid must be higher than &4%current_bid%"); languageNodes.put("prompts.enter new buy now price", "&aPlease enter the new buy now price in chat:"); languageNodes.put("prompts.enter new starting bid", "&aPlease enter the new starting bid in chat:"); diff --git a/src/main/java/ca/tweetzy/auctionhouse/settings/Settings.java b/src/main/java/ca/tweetzy/auctionhouse/settings/Settings.java index 9bbf891..183a8c9 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/settings/Settings.java +++ b/src/main/java/ca/tweetzy/auctionhouse/settings/Settings.java @@ -55,6 +55,10 @@ public class Settings { public static final ConfigSetting BROADCAST_AUCTION_ENDING_AT_TIME = new ConfigSetting(config, "auction setting.broadcast auction ending at time", 20, "When the time on the auction item reaches this amount of seconds left, the broadcast ending will take affect "); public static final ConfigSetting USE_REALISTIC_BIDDING = new ConfigSetting(config, "auction setting.use realistic bidding", false, "If true auction house will use a more realistic bidding approach. Ex. the previous bid is 400, and if a player bids 500, rather than making the new bid 900, it will be set to 500."); + public static final ConfigSetting BID_MUST_BE_HIGHER_THAN_PREVIOUS = new ConfigSetting(config, "auction setting.bid must be higher than previous", true, "Only applies if use realistic bidding is true, this will make it so that they must bid higher than the current bid."); + public static final ConfigSetting USE_LIVE_BID_NUMBER_IN_CONFIRM_GUI = new ConfigSetting(config, "auction setting.live bid number in confirm gui.use", true, "If true, the bid confirmation menu will auto update every 1 second by default"); + public static final ConfigSetting LIVE_BID_NUMBER_IN_CONFIRM_GUI_RATE = new ConfigSetting(config, "auction setting.live bid number in confirm gui.rate", 1, "How often the confirm gui for bids will update"); + public static final ConfigSetting PLAYER_NEEDS_TOTAL_PRICE_TO_BID = new ConfigSetting(config, "auction setting.bidder must have funds in account", false, "Should the player who is placing a bid on an item have the money in their account to cover the cost?"); public static final ConfigSetting ALLOW_USAGE_OF_BID_SYSTEM = new ConfigSetting(config, "auction setting.allow bid system usage", true, "Should players be allowed to use the bid option cmd params?"); public static final ConfigSetting ALLOW_USAGE_OF_BUY_NOW_SYSTEM = new ConfigSetting(config, "auction setting.allow buy now system usage", true, "Should players be allowed to use the right-click buy now feature on biddable items?"); @@ -705,6 +709,36 @@ public class Settings { public static final ConfigSetting GUI_SELL_ITEMS_BUY_NOW_DISABLED_NAME = new ConfigSetting(config, "gui.sell.items.buy now disabled.name", "&c&lBuy Now Disabled"); public static final ConfigSetting GUI_SELL_ITEMS_BUY_NOW_DISABLED_LORE = new ConfigSetting(config, "gui.sell.items.buy now disabled.lore", Collections.singletonList("&7Click to &aEnable &7buy now")); + /* =============================== + * AH STATS GUI + * ===============================*/ + public static final ConfigSetting GUI_STATS_TITLE = new ConfigSetting(config, "gui.stats.title", "&7Auction House - &eStatistics"); + public static final ConfigSetting GUI_STATS_BG_ITEM = new ConfigSetting(config, "gui.stats.bg item", XMaterial.BLACK_STAINED_GLASS_PANE.name()); + + public static final ConfigSetting GUI_STATS_ITEMS_PERSONAL_USE_HEAD = new ConfigSetting(config, "gui.stats.items.personal.use head", true); + public static final ConfigSetting GUI_STATS_ITEMS_PERSONAL_ITEM = new ConfigSetting(config, "gui.stats.items.personal.item", XMaterial.DIAMOND.name()); + public static final ConfigSetting GUI_STATS_ITEMS_PERSONAL_NAME = new ConfigSetting(config, "gui.stats.items.personal.name", "&9&lPersonal Stats"); + public static final ConfigSetting GUI_STATS_ITEMS_PERSONAL_LORE = new ConfigSetting(config, "gui.stats.items.personal.lore", Arrays.asList( + "", + "&7Auctions Created: &e%auctions_created%", + "&7Auctions Sold: &e%auctions_sold%", + "&7Auctions Expired: &e%auctions_expired%", + "", + "&7Money Earned: &a$%auctions_money_earned%", + "&7Money Spent: &a$%auctions_money_spent%" + )); + + public static final ConfigSetting GUI_STATS_ITEMS_GLOBAL_ITEM = new ConfigSetting(config, "gui.stats.items.global.item", XMaterial.NETHER_STAR.name()); + public static final ConfigSetting GUI_STATS_ITEMS_GLOBAL_NAME = new ConfigSetting(config, "gui.stats.items.global.name", "&6&LGlobal Stats"); + public static final ConfigSetting GUI_STATS_ITEMS_GLOBAL_LORE = new ConfigSetting(config, "gui.stats.items.global.lore", Arrays.asList( + "", + "&7Auctions Created: &e%auctions_created%", + "&7Auctions Sold: &e%auctions_sold%", + "&7Auctions Expired: &e%auctions_expired%", + "", + "&7Money Spent: &a$%auctions_money_spent%" + )); + /* =============================== * ITEM ADMIN GUI diff --git a/src/main/java/ca/tweetzy/auctionhouse/tasks/AutoSaveTask.java b/src/main/java/ca/tweetzy/auctionhouse/tasks/AutoSaveTask.java index e9d9c42..0462887 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/tasks/AutoSaveTask.java +++ b/src/main/java/ca/tweetzy/auctionhouse/tasks/AutoSaveTask.java @@ -27,6 +27,7 @@ public class AutoSaveTask extends BukkitRunnable { @Override public void run() { AuctionHouse.getInstance().getDataManager().updateItems(AuctionHouse.getInstance().getAuctionItemManager().getItems().values(), null); + AuctionHouse.getInstance().getDataManager().updateStats(AuctionHouse.getInstance().getAuctionStatManager().getStats(), null); AuctionHouse.getInstance().getFilterManager().saveFilterWhitelist(true); AuctionHouse.getInstance().getLocale().newMessage(TextUtils.formatText("&aAuto saved auction items & transactions")).sendPrefixedMessage(Bukkit.getConsoleSender()); } diff --git a/src/main/java/ca/tweetzy/auctionhouse/tasks/TickAuctionsTask.java b/src/main/java/ca/tweetzy/auctionhouse/tasks/TickAuctionsTask.java index 5412c9f..1046196 100644 --- a/src/main/java/ca/tweetzy/auctionhouse/tasks/TickAuctionsTask.java +++ b/src/main/java/ca/tweetzy/auctionhouse/tasks/TickAuctionsTask.java @@ -4,6 +4,7 @@ import ca.tweetzy.auctionhouse.AuctionHouse; import ca.tweetzy.auctionhouse.api.AuctionAPI; import ca.tweetzy.auctionhouse.api.events.AuctionEndEvent; import ca.tweetzy.auctionhouse.auction.AuctionSaleType; +import ca.tweetzy.auctionhouse.auction.AuctionStat; import ca.tweetzy.auctionhouse.auction.AuctionedItem; import ca.tweetzy.auctionhouse.settings.Settings; import ca.tweetzy.core.hooks.EconomyManager; @@ -68,9 +69,14 @@ public class TickAuctionsTask extends BukkitRunnable { } } - if (timeRemaining <= 0) { + if (timeRemaining <= 0 && !auctionItem.isExpired()) { if (auctionItem.getHighestBidder().equals(auctionItem.getOwner())) { auctionItem.setExpired(true); + if (auctionItem.isExpired()) { + AuctionHouse.getInstance().getAuctionStatManager().insertOrUpdate(Bukkit.getOfflinePlayer(auctionItem.getOwner()), new AuctionStat<>( + 0, 0, 1, 0D, 0D + )); + } continue; } diff --git a/src/main/test/Test.java b/src/main/test/Test.java index eee4fb5..da058f3 100644 --- a/src/main/test/Test.java +++ b/src/main/test/Test.java @@ -1,6 +1,9 @@ +import ca.tweetzy.auctionhouse.api.AuctionAPI; import org.apache.commons.lang.StringUtils; +import java.rmi.server.UID; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** @@ -20,12 +23,18 @@ public class Test { // System.out.println(StringUtils.join(enchants, ";=;")); - final String uIDPartOne = "%%__US"; - final String uIDPartTwo = "ER__%%"; +// final String uIDPartOne = "%%__US"; +// final String uIDPartTwo = "ER__%%"; +// +// final String UID = "%%__USER_%%"; - final String UID = "%%__USER_%%"; +// System.out.println(UID.contains(uIDPartOne) && UID.contains(uIDPartTwo)); - System.out.println(UID.contains(uIDPartOne) && UID.contains(uIDPartTwo)); +// System.out.println(AuctionAPI.toTicks("1 day")); + + String arguments = "50 -b"; + + Arrays.asList(arguments.split(" ")).forEach(System.out::println); } public static long getSecondsFromString(String time) {