From 1c4794cec506e0af84739163401464bdcbbcb98e Mon Sep 17 00:00:00 2001 From: Garsooon Date: Fri, 11 Jul 2025 18:34:56 -0700 Subject: [PATCH 1/5] Add leaderboard and stat command --- pom.xml | 2 +- .../arenafighter/Commands/FightCommand.java | 53 +++++++++ .../arenafighter/Fight/FightManager.java | 112 ++++++++++++++++++ src/main/resources/plugin.yml | 4 +- 4 files changed, 168 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 2181259..abe2c80 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.retromc arenafighter - 1.0.8 + 1.0.9 jar diff --git a/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java b/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java index 5e120c4..93e5ee9 100644 --- a/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java +++ b/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java @@ -14,6 +14,7 @@ import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; public class FightCommand implements CommandExecutor { @@ -65,6 +66,20 @@ public boolean onCommand(CommandSender sender, Command command, String label, St } return handleCancel(player); + case "leaderboard": + if (!player.hasPermission("arenafighter.fight")) { + player.sendMessage(ChatColor.RED + "You do not have permission to see the leaderboard."); + return true; + } + return handleLeaderboard(player); + + case "stats": + if (!player.hasPermission("arenafighter.fight")) { + player.sendMessage(ChatColor.RED + "You do not have permission to see stats."); + return true; + } + return handleStats(player, args); + case "help": default: sendHelp(player); @@ -264,6 +279,42 @@ private boolean handleAccept(Player accepter, String[] args) { return true; } + private boolean handleLeaderboard(Player player) { + Map topPlayers = fightManager.getTopPlayersByWins(); + + if (topPlayers.isEmpty()) { + player.sendMessage(ChatColor.RED + "No leaderboard data found."); + return true; + } + + player.sendMessage(ChatColor.GOLD + "=== ArenaFighter Leaderboard ==="); + int rank = 1; + for (Map.Entry entry : topPlayers.entrySet()) { + player.sendMessage(ChatColor.YELLOW + "#" + rank + " " + ChatColor.AQUA + entry.getKey() + + ChatColor.WHITE + " - " + ChatColor.GREEN + entry.getValue() + " wins"); + rank++; + } + + return true; + } + + private boolean handleStats(Player player, String[] args) { + String targetName; + + if (args.length == 1) { + // Show own stats + targetName = player.getName(); + } else { + targetName = args[1]; + } + + List statsLines = fightManager.getPlayerStats(targetName); + for (String line : statsLines) { + player.sendMessage(line); + } + return true; + } + private boolean handleCancel(Player player) { if (!fightManager.isInFight(player)) { player.sendMessage(ChatColor.RED + "You're not in a fight."); @@ -280,6 +331,8 @@ private void sendHelp(Player player) { player.sendMessage(ChatColor.YELLOW + "/fight challenge " + ChatColor.WHITE + " - Challenge a player"); player.sendMessage(ChatColor.YELLOW + "/fight accept " + ChatColor.WHITE + " - Accept a challenge"); player.sendMessage(ChatColor.YELLOW + "/fight cancel" + ChatColor.WHITE + " - Cancel current fight"); + player.sendMessage(ChatColor.YELLOW + "/fight leaderboard" + ChatColor.WHITE + " - Show top 10 fighters"); + player.sendMessage(ChatColor.YELLOW + "/fight stats " + ChatColor.WHITE + " - Show player stats"); player.sendMessage(ChatColor.YELLOW + "/fight help" + ChatColor.WHITE + " - Show help"); player.sendMessage(ChatColor.YELLOW + "/spectate " + ChatColor.WHITE + " - Start spectating"); } diff --git a/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java b/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java index f5ed853..2825cf0 100644 --- a/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java +++ b/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java @@ -2,6 +2,7 @@ import net.minecraft.server.EntityHuman; import net.minecraft.server.EntityPlayer; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.inventory.ItemStack; import org.bukkit.Bukkit; @@ -19,7 +20,9 @@ import java.io.File; import java.io.FileInputStream; +import java.io.FileWriter; import java.util.*; +import java.util.stream.Collectors; public class FightManager { @@ -35,6 +38,8 @@ public class FightManager { private final Map punishments = new HashMap<>(); private final long punishmentDurationMillis; private final Method economy; + private final File statsFile; + private Map stats; public FightManager(ArenaFighter plugin, ArenaManager arenaManager, Method economy) { this.plugin = plugin; @@ -45,6 +50,8 @@ public FightManager(ArenaFighter plugin, ArenaManager arenaManager, Method econo this.pendingChallenges = new HashMap<>(); this.spectatorOriginalLocations = new HashMap<>(); this.punishmentDurationMillis = loadPunishmentDuration(plugin.getDataFolder()); + this.statsFile = new File(plugin.getDataFolder(), "stats.yml"); + loadStats(); } private long loadPunishmentDuration(File dataFolder) { @@ -308,6 +315,9 @@ public void endFight(Player winner, Player loser) { fight.resolveBets(winner.getName()); + incrementStat(winner.getName(), "wins"); + incrementStat(loser.getName(), "losses"); + String message = ChatColor.GOLD + winner.getName() + ChatColor.YELLOW + " has defeated " + ChatColor.RED + loser.getName() + @@ -797,4 +807,106 @@ public double getWager() { return wager; } } + + //Stat and leaderboard stuff + private void loadStats() { + stats = new HashMap<>(); + if (!statsFile.exists()) return; + + try (FileInputStream fis = new FileInputStream(statsFile)) { + Yaml yaml = new Yaml(); + Object data = yaml.load(fis); + if (data instanceof Map) { + stats = (Map) data; + } + } catch (Exception e) { + plugin.getServer().getLogger().warning("Failed to load stats.yml: " + e.getMessage()); + } + } + + private void saveStats() { + try (FileWriter writer = new FileWriter(statsFile)) { + Yaml yaml = new Yaml(); + yaml.dump(stats, writer); + } catch (Exception e) { + plugin.getServer().getLogger().warning("Failed to save stats.yml: " + e.getMessage()); + } + } + + private void incrementStat(String playerName, String key) { + Map playerStats = (Map) stats.get(playerName); + if (playerStats == null) { + playerStats = new HashMap<>(); + stats.put(playerName, playerStats); + } + + int current = 0; + Object val = playerStats.get(key); + if (val instanceof Number) { + current = ((Number) val).intValue(); + } + + playerStats.put(key, current + 1); + saveStats(); + } + + public Map getTopPlayersByWins() { + Map winsMap = new HashMap<>(); + + for (Map.Entry entry : stats.entrySet()) { + String playerName = entry.getKey(); + Object value = entry.getValue(); + + if (value instanceof Map) { + Map playerStats = (Map) value; + Object winsObj = playerStats.get("wins"); + + if (winsObj instanceof Number) { + winsMap.put(playerName, ((Number) winsObj).intValue()); + } + } + } + + return winsMap.entrySet().stream() + .sorted(Map.Entry.comparingByValue().reversed()) + .limit(10) + .collect(Collectors.toMap( + Map.Entry::getKey, + Map.Entry::getValue, + (e1, e2) -> e1, + LinkedHashMap::new + )); + } + + public List getPlayerStats(String playerName) { + Object raw = stats.get(playerName); + if (!(raw instanceof Map)) { + return Collections.singletonList(ChatColor.RED + "No stats found for " + playerName + "."); + } + + Map playerStats = (Map) raw; + + int wins = getInt(playerStats.get("wins")); + int losses = getInt(playerStats.get("losses")); + + List lines = new ArrayList<>(); + lines.add(ChatColor.GOLD + "=== Stats for " + ChatColor.AQUA + playerName + ChatColor.GOLD + " ==="); + lines.add(ChatColor.YELLOW + "Wins: " + ChatColor.GREEN + wins); + lines.add(ChatColor.YELLOW + "Losses: " + ChatColor.RED + losses); + + return lines; + } + + // Helper to safely get int from Object, returns 0 if null or not an Integer + private int getInt(Object obj) { + if (obj == null) return 0; + if (obj instanceof Integer) return (Integer) obj; + if (obj instanceof Number) return ((Number) obj).intValue(); + try { + return Integer.parseInt(obj.toString()); + } catch (NumberFormatException e) { + return 0; + } + } + } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 246002e..511775a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: ArenaFighter -version: 1.0.8 +version: 1.0.9 description: A plugin that allows players to fight in arenas and teleports them back after fights conclude author: Garsooon main: org.garsooon.arenafighter.Arena.ArenaFighter @@ -10,7 +10,7 @@ softdepend: [Essentials, Fundamentals, ZCore, MultiCurrency, iCo4, iCo5, iCo6, B commands: fight: description: Challenge other players to arena fights - usage: /fight [player] [wager] + usage: /fight [player] [wager] arena: description: Manage arena configurations usage: /arena [args] From 565ef61d9fb6a0d03724eff62180a9278c681ca0 Mon Sep 17 00:00:00 2001 From: Garsooon Date: Sat, 12 Jul 2025 16:35:10 -0700 Subject: [PATCH 2/5] Add uuid support to stats --- .../arenafighter/Commands/FightCommand.java | 2 +- .../arenafighter/Data/PlayerDataManager.java | 43 +++++++ .../arenafighter/Fight/FightManager.java | 115 ++++++++++++++---- 3 files changed, 136 insertions(+), 24 deletions(-) create mode 100644 src/main/java/org/garsooon/arenafighter/Data/PlayerDataManager.java diff --git a/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java b/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java index 93e5ee9..5a863cf 100644 --- a/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java +++ b/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java @@ -308,7 +308,7 @@ private boolean handleStats(Player player, String[] args) { targetName = args[1]; } - List statsLines = fightManager.getPlayerStats(targetName); + List statsLines = fightManager.getPlayerStatsByName(targetName); for (String line : statsLines) { player.sendMessage(line); } diff --git a/src/main/java/org/garsooon/arenafighter/Data/PlayerDataManager.java b/src/main/java/org/garsooon/arenafighter/Data/PlayerDataManager.java new file mode 100644 index 0000000..b07a2a5 --- /dev/null +++ b/src/main/java/org/garsooon/arenafighter/Data/PlayerDataManager.java @@ -0,0 +1,43 @@ +package org.garsooon.arenafighter.Data; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +// Utility class to try and stop the feature bloat in FightManager +public class PlayerDataManager { + private static final Map uuidToUsername = new HashMap<>(); + private static final Map usernameToUUID = new HashMap<>(); + + public static void setPlayer(UUID uuid, String username) { + if (uuid == null || username == null) return; + + uuidToUsername.put(uuid, username); + usernameToUUID.put(username.toLowerCase(), uuid); + } + + public static String getUsername(UUID uuid) { + return uuidToUsername.get(uuid); + } + + public static UUID getUUID(String username) { + return usernameToUUID.get(username.toLowerCase()); + } + + public static boolean hasPlayer(UUID uuid) { + return uuidToUsername.containsKey(uuid); + } + + public static boolean hasUsername(String name) { + return usernameToUUID.containsKey(name.toLowerCase()); + } + + public static Map getAllUUIDToUsername() { + return new HashMap<>(uuidToUsername); + } + + public static void clear() { + uuidToUsername.clear(); + usernameToUUID.clear(); + } +} diff --git a/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java b/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java index 2825cf0..abc772e 100644 --- a/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java +++ b/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java @@ -2,7 +2,7 @@ import net.minecraft.server.EntityHuman; import net.minecraft.server.EntityPlayer; -import org.bukkit.configuration.ConfigurationSection; +import com.legacyminecraft.poseidon.util.*; import org.bukkit.craftbukkit.inventory.CraftItemStack; import org.bukkit.inventory.ItemStack; import org.bukkit.Bukkit; @@ -14,6 +14,7 @@ import org.garsooon.arenafighter.Arena.ArenaFighter; import org.garsooon.arenafighter.Arena.ArenaManager; import org.garsooon.arenafighter.Data.Challenge; +import org.garsooon.arenafighter.Data.PlayerDataManager; import org.garsooon.arenafighter.Economy.Method; import org.garsooon.arenafighter.Economy.Methods; import org.yaml.snakeyaml.Yaml; @@ -39,7 +40,8 @@ public class FightManager { private final long punishmentDurationMillis; private final Method economy; private final File statsFile; - private Map stats; + private Map stats; // Key: UUID.toString() + private final PlayerDataManager playerDataManager = new PlayerDataManager(); public FightManager(ArenaFighter plugin, ArenaManager arenaManager, Method economy) { this.plugin = plugin; @@ -315,8 +317,8 @@ public void endFight(Player winner, Player loser) { fight.resolveBets(winner.getName()); - incrementStat(winner.getName(), "wins"); - incrementStat(loser.getName(), "losses"); + incrementStat(winner.getUniqueId(), "wins", winner.getName()); + incrementStat(loser.getUniqueId(), "losses", loser.getName()); String message = ChatColor.GOLD + winner.getName() + ChatColor.YELLOW + " has defeated " + @@ -783,7 +785,7 @@ public boolean isSpectating(Player player) { return spectatorOriginalLocations.containsKey(player.getUniqueId()); } - //ECO Wager per Challenge data + //ECO Wager per Challenge data start private static class FightChallenge { private final UUID challengerId; private final UUID targetId; @@ -807,8 +809,20 @@ public double getWager() { return wager; } } + //ECO Wager per Challenge data end + + //Stat and leaderboard functions start + public static int getInt(Object obj) { + if (obj == null) return 0; + if (obj instanceof Integer) return (Integer) obj; + if (obj instanceof Number) return ((Number) obj).intValue(); + try { + return Integer.parseInt(obj.toString()); + } catch (NumberFormatException e) { + return 0; + } + } - //Stat and leaderboard stuff private void loadStats() { stats = new HashMap<>(); if (!statsFile.exists()) return; @@ -833,13 +847,21 @@ private void saveStats() { } } - private void incrementStat(String playerName, String key) { - Map playerStats = (Map) stats.get(playerName); + //I LOVE SUPPRESS WARNINGS I LOVE SUPPRESS WARNINGS I LOVE SUPPRESS WARNINGS, GOD I LOVE SUPPRESS WARNINGS + @SuppressWarnings("unchecked") + public void incrementStat(UUID uuid, String key, String username) { + String uuidKey = uuid.toString(); + + Map playerStats = (Map) stats.get(uuidKey); if (playerStats == null) { playerStats = new HashMap<>(); - stats.put(playerName, playerStats); + stats.put(uuidKey, playerStats); } + playerStats.put("username", username); + + PlayerDataManager.setPlayer(uuid, username); + int current = 0; Object val = playerStats.get(key); if (val instanceof Number) { @@ -850,11 +872,12 @@ private void incrementStat(String playerName, String key) { saveStats(); } + @SuppressWarnings("unchecked") public Map getTopPlayersByWins() { Map winsMap = new HashMap<>(); for (Map.Entry entry : stats.entrySet()) { - String playerName = entry.getKey(); + String uuidStr = entry.getKey(); Object value = entry.getValue(); if (value instanceof Map) { @@ -862,7 +885,18 @@ public Map getTopPlayersByWins() { Object winsObj = playerStats.get("wins"); if (winsObj instanceof Number) { - winsMap.put(playerName, ((Number) winsObj).intValue()); + try { + UUID uuid = UUID.fromString(uuidStr); + + String name = PlayerDataManager.getUsername(uuid); + if (name == null) { + name = uuidStr; // fallback raw UUID + } + + winsMap.put(name, ((Number) winsObj).intValue()); + } catch (IllegalArgumentException ignored) { + + } } } } @@ -878,35 +912,70 @@ public Map getTopPlayersByWins() { )); } - public List getPlayerStats(String playerName) { - Object raw = stats.get(playerName); + @SuppressWarnings("unchecked") + public List getPlayerStats(UUID uuid) { + Object raw = stats.get(uuid.toString()); + if (!(raw instanceof Map)) { - return Collections.singletonList(ChatColor.RED + "No stats found for " + playerName + "."); + return Collections.singletonList(ChatColor.RED + "No stats found for " + uuid + "."); } + // Checked type, safe to cast. In other words I LOVE SUPPRESS WARNING!!!11! Map playerStats = (Map) raw; int wins = getInt(playerStats.get("wins")); int losses = getInt(playerStats.get("losses")); + // Resolve username from PlayerDataManager or fallback to a UUID string + String displayName = PlayerDataManager.getUsername(uuid); + if (displayName == null) { + displayName = uuid.toString(); + } + List lines = new ArrayList<>(); - lines.add(ChatColor.GOLD + "=== Stats for " + ChatColor.AQUA + playerName + ChatColor.GOLD + " ==="); + lines.add(ChatColor.GOLD + "=== Stats for " + ChatColor.AQUA + displayName + ChatColor.GOLD + " ==="); lines.add(ChatColor.YELLOW + "Wins: " + ChatColor.GREEN + wins); lines.add(ChatColor.YELLOW + "Losses: " + ChatColor.RED + losses); return lines; } - // Helper to safely get int from Object, returns 0 if null or not an Integer - private int getInt(Object obj) { - if (obj == null) return 0; - if (obj instanceof Integer) return (Integer) obj; - if (obj instanceof Number) return ((Number) obj).intValue(); + // For /fight stats + @SuppressWarnings("unchecked") + public List getPlayerStatsByName(String name) { + UUID uuid; try { - return Integer.parseInt(obj.toString()); - } catch (NumberFormatException e) { - return 0; + uuid = UUIDFetcher.getUUIDOf(name); + } catch (Exception e) { + return Collections.singletonList(ChatColor.RED + "Could not resolve player: " + name); + } + + if (uuid == null) { + return Collections.singletonList(ChatColor.RED + "Could not resolve player: " + name); + } + + Object raw = stats.get(uuid.toString()); + if (!(raw instanceof Map)) { + return Collections.singletonList(ChatColor.RED + "No stats found for " + name + "."); + } + + Map playerStats = (Map) raw; + int wins = getInt(playerStats.get("wins")); + int losses = getInt(playerStats.get("losses")); + + // Resolve username using PlayerDataManager or fallback to input name + String displayName = PlayerDataManager.getUsername(uuid); + if (displayName == null) { + displayName = name; } + + List lines = new ArrayList<>(); + lines.add(ChatColor.GOLD + "=== Stats for " + ChatColor.AQUA + displayName + ChatColor.GOLD + " ==="); + lines.add(ChatColor.YELLOW + "Wins: " + ChatColor.GREEN + wins); + lines.add(ChatColor.YELLOW + "Losses: " + ChatColor.RED + losses); + + return lines; } + // Stat and leaderboard functions end } From 2ee6b2208cebcbb952dd866b31d8292375bd5174 Mon Sep 17 00:00:00 2001 From: Garsooon Date: Sat, 12 Jul 2025 17:31:06 -0700 Subject: [PATCH 3/5] Round down to 2nd decimal place --- .../arenafighter/Commands/FightCommand.java | 5 ++++- .../Commands/SpectateBetCommand.java | 21 ++++++++++++------- .../org/garsooon/arenafighter/Data/Bet.java | 9 ++++++++ .../arenafighter/Fight/FightManager.java | 9 ++++++-- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java b/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java index 5a863cf..f69ce13 100644 --- a/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java +++ b/src/main/java/org/garsooon/arenafighter/Commands/FightCommand.java @@ -7,6 +7,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; +import org.garsooon.arenafighter.Data.Bet; import org.garsooon.arenafighter.Data.Challenge; import org.garsooon.arenafighter.Economy.Method; import org.garsooon.arenafighter.Economy.Methods; @@ -139,6 +140,7 @@ private boolean handleChallenge(Player challenger, String[] args) { if (args.length >= 3) { try { wagerAmount = Double.parseDouble(args[2]); + wagerAmount = Bet.roundDownTwoDecimals(wagerAmount); // <-- round down here if (wagerAmount < 0) { challenger.sendMessage(ChatColor.RED + "Wager amount cannot be negative."); return true; @@ -238,6 +240,7 @@ private boolean handleAccept(Player accepter, String[] args) { } double wager = challenge.getWagerAmount(); + double truncatedWager = Bet.roundDownTwoDecimals(wager); // Balance check for wagers if (wager > 0) { @@ -254,7 +257,7 @@ private boolean handleAccept(Player accepter, String[] args) { // Broadcast fight accept with wager message when there is a wager String line1 = ChatColor.GOLD + accepter.getName() + ChatColor.YELLOW + " has accepted " + ChatColor.GOLD + challenger.getName() + ChatColor.YELLOW + "'s duel request"; - String line2 = (wager > 0 ? ChatColor.YELLOW + " with a wager of " + ChatColor.GREEN + wager : "") + ChatColor.YELLOW + "!"; + String line2 = (truncatedWager > 0 ? ChatColor.YELLOW + " with a wager of " + ChatColor.GREEN + truncatedWager : "") + ChatColor.YELLOW + "!"; String line3 = ChatColor.YELLOW + "The fight will begin in 30 seconds..."; Bukkit.broadcastMessage(line1); diff --git a/src/main/java/org/garsooon/arenafighter/Commands/SpectateBetCommand.java b/src/main/java/org/garsooon/arenafighter/Commands/SpectateBetCommand.java index 5092945..e69e4de 100644 --- a/src/main/java/org/garsooon/arenafighter/Commands/SpectateBetCommand.java +++ b/src/main/java/org/garsooon/arenafighter/Commands/SpectateBetCommand.java @@ -9,6 +9,7 @@ import org.garsooon.arenafighter.Fight.Fight; import org.garsooon.arenafighter.Economy.Methods; import org.garsooon.arenafighter.Economy.Method; +import org.garsooon.arenafighter.Data.Bet; public class SpectateBetCommand implements CommandExecutor { @@ -29,7 +30,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St player.sendMessage(ChatColor.RED + "Economy plugin not detected. Betting is disabled."); return true; } - + if (args.length != 2) { player.sendMessage(ChatColor.RED + "Usage: /bet "); return true; @@ -40,6 +41,11 @@ public boolean onCommand(CommandSender sender, Command command, String label, St try { amount = Double.parseDouble(args[1]); + amount = Bet.roundDownTwoDecimals(amount); + if (amount <= 0) { + player.sendMessage(ChatColor.RED + "Bet amount must be greater than 0."); + return true; + } } catch (NumberFormatException e) { player.sendMessage(ChatColor.RED + "Invalid amount."); return true; @@ -58,7 +64,6 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } - //FUTURE USE if (fight.hasStarted()) { player.sendMessage(ChatColor.RED + "You cannot bet, the fight has already started."); return true; @@ -74,17 +79,17 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } - if (fight.containsPlayer(player)) { - player.sendMessage(ChatColor.RED + "You cannot bet on a fight you are participating in."); - return true; - } - if (!economy.withdrawPlayer(player.getName(), amount, player.getWorld())) { player.sendMessage(ChatColor.RED + "Failed to withdraw funds."); return true; } + String msg = ChatColor.AQUA + player.getName() + + ChatColor.YELLOW + " placed a bet of " + + ChatColor.GOLD + amount + + ChatColor.YELLOW + " on " + + ChatColor.GREEN + fighterName + ChatColor.YELLOW + "!"; + player.getServer().broadcastMessage(msg); - player.sendMessage(ChatColor.GREEN + "You placed a bet of " + amount + " on " + fighterName + "."); return true; } } diff --git a/src/main/java/org/garsooon/arenafighter/Data/Bet.java b/src/main/java/org/garsooon/arenafighter/Data/Bet.java index fa000eb..30f8d10 100644 --- a/src/main/java/org/garsooon/arenafighter/Data/Bet.java +++ b/src/main/java/org/garsooon/arenafighter/Data/Bet.java @@ -1,5 +1,8 @@ package org.garsooon.arenafighter.Data; +import java.math.BigDecimal; +import java.math.RoundingMode; + public class Bet { private final String spectator; private final String fighter; @@ -23,4 +26,10 @@ public String getFighter() { public double getAmount() { return amount; } + + public static double roundDownTwoDecimals(double value) { + BigDecimal bd = BigDecimal.valueOf(value); + bd = bd.setScale(2, RoundingMode.DOWN); // truncate to 2 decimal places, no rounding up + return bd.doubleValue(); + } } diff --git a/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java b/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java index abc772e..bf72db1 100644 --- a/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java +++ b/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java @@ -13,6 +13,7 @@ import org.garsooon.arenafighter.Arena.Arena; import org.garsooon.arenafighter.Arena.ArenaFighter; import org.garsooon.arenafighter.Arena.ArenaManager; +import org.garsooon.arenafighter.Data.Bet; import org.garsooon.arenafighter.Data.Challenge; import org.garsooon.arenafighter.Data.PlayerDataManager; import org.garsooon.arenafighter.Economy.Method; @@ -165,6 +166,8 @@ private void ejectPlayer(Player player) { } public boolean startFight(Player player1, Player player2, double wager) { + double truncatedWager = Bet.roundDownTwoDecimals(wager); + if (isInFight(player1) || isInFight(player2)) return false; // Final balance check before fight begins @@ -250,7 +253,7 @@ public boolean startFight(Player player1, Player player2, double wager) { ChatColor.GREEN + arena.getName(); if (wager > 0) { - message += ChatColor.YELLOW + " with a wager of " + ChatColor.GOLD + wager; + message += ChatColor.YELLOW + " with a wager of " + ChatColor.GOLD + truncatedWager; } fight.clearBets(); @@ -311,6 +314,7 @@ public void endFight(Player winner, Player loser) { double wager = fight.getWager(); if (wager > 0) { + double truncatedWager = Bet.roundDownTwoDecimals(wager); deposit(winner, wager * 2); winner.sendMessage(ChatColor.GREEN + "You have won " + (wager * 2) + " from the wager!"); } @@ -327,7 +331,8 @@ public void endFight(Player winner, Player loser) { ChatColor.GREEN + fight.getArena().getName(); if (wager > 0) { - message += ChatColor.YELLOW + " and won a wager of " + ChatColor.GOLD + (wager * 2); + double truncatedWager = Bet.roundDownTwoDecimals(wager * 2); + message += ChatColor.YELLOW + " and won a wager of " + ChatColor.GOLD + truncatedWager; } message += ChatColor.YELLOW + "!"; From 50089ca5027f5b75743ec4575c412826169c289d Mon Sep 17 00:00:00 2001 From: Garsooon Date: Sat, 12 Jul 2025 20:19:32 -0700 Subject: [PATCH 4/5] Fix UUID Thread stall on cracked account stats call --- .../arenafighter/Fight/FightManager.java | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java b/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java index bf72db1..2238354 100644 --- a/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java +++ b/src/main/java/org/garsooon/arenafighter/Fight/FightManager.java @@ -828,6 +828,7 @@ public static int getInt(Object obj) { } } + @SuppressWarnings("unchecked") private void loadStats() { stats = new HashMap<>(); if (!statsFile.exists()) return; @@ -888,20 +889,10 @@ public Map getTopPlayersByWins() { if (value instanceof Map) { Map playerStats = (Map) value; Object winsObj = playerStats.get("wins"); + String storedName = (String) playerStats.get("username"); - if (winsObj instanceof Number) { - try { - UUID uuid = UUID.fromString(uuidStr); - - String name = PlayerDataManager.getUsername(uuid); - if (name == null) { - name = uuidStr; // fallback raw UUID - } - - winsMap.put(name, ((Number) winsObj).intValue()); - } catch (IllegalArgumentException ignored) { - - } + if (winsObj instanceof Number && storedName != null) { + winsMap.put(storedName, ((Number) winsObj).intValue()); } } } @@ -948,15 +939,25 @@ public List getPlayerStats(UUID uuid) { // For /fight stats @SuppressWarnings("unchecked") public List getPlayerStatsByName(String name) { - UUID uuid; - try { - uuid = UUIDFetcher.getUUIDOf(name); - } catch (Exception e) { - return Collections.singletonList(ChatColor.RED + "Could not resolve player: " + name); + UUID uuid = null; + + // Search for UUID based on stored username in the raw YAML map + for (String key : stats.keySet()) { + Object raw = stats.get(key); + if (raw instanceof Map) { + Map entry = (Map) raw; + String storedName = (String) entry.get("username"); + if (storedName != null && storedName.equalsIgnoreCase(name)) { + try { + uuid = UUID.fromString(key); + break; + } catch (IllegalArgumentException ignored) {} + } + } } if (uuid == null) { - return Collections.singletonList(ChatColor.RED + "Could not resolve player: " + name); + return Collections.singletonList(ChatColor.RED + "No stats found for " + name + "."); } Object raw = stats.get(uuid.toString()); @@ -967,12 +968,8 @@ public List getPlayerStatsByName(String name) { Map playerStats = (Map) raw; int wins = getInt(playerStats.get("wins")); int losses = getInt(playerStats.get("losses")); - - // Resolve username using PlayerDataManager or fallback to input name - String displayName = PlayerDataManager.getUsername(uuid); - if (displayName == null) { - displayName = name; - } + String displayName = (String) playerStats.get("username"); + if (displayName == null) displayName = uuid.toString().substring(0, 8); List lines = new ArrayList<>(); lines.add(ChatColor.GOLD + "=== Stats for " + ChatColor.AQUA + displayName + ChatColor.GOLD + " ==="); From 8f998b4cfb2ad951f6433b45418ca66a5bca9cdb Mon Sep 17 00:00:00 2001 From: Garsooon Date: Sun, 13 Jul 2025 06:15:03 -0700 Subject: [PATCH 5/5] Re-add fighter bet check --- .../garsooon/arenafighter/Commands/SpectateBetCommand.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/garsooon/arenafighter/Commands/SpectateBetCommand.java b/src/main/java/org/garsooon/arenafighter/Commands/SpectateBetCommand.java index e69e4de..b867dc1 100644 --- a/src/main/java/org/garsooon/arenafighter/Commands/SpectateBetCommand.java +++ b/src/main/java/org/garsooon/arenafighter/Commands/SpectateBetCommand.java @@ -74,6 +74,11 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } + if (fight.containsPlayer(player)) { + player.sendMessage(ChatColor.RED + "You cannot bet on a fight you are participating in."); + return true; + } + if (!fight.placeBet(player.getName(), fighterName, amount)) { player.sendMessage(ChatColor.RED + "You have already placed a bet."); return true;