From e21d44e1190ceb44c05ad28bfd1690dd3c57e123 Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Wed, 11 May 2022 13:40:08 +0200 Subject: [PATCH 1/8] Started working on top statistic (#1) --- .../artemis/the/gr8/playerstats/Main.java | 8 +- .../the/gr8/playerstats/StatManager.java | 121 +++++++++++------- .../playerstats/commands/ReloadCommand.java | 16 ++- .../gr8/playerstats/commands/StatCommand.java | 73 ++++++----- .../playerstats/commands/TabCompleter.java | 2 +- .../utils/OfflinePlayerHandler.java | 47 +++++-- .../playerstats/utils/OutputFormatter.java | 14 +- 7 files changed, 177 insertions(+), 104 deletions(-) diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java index d15d36a..2fdb746 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java @@ -18,13 +18,13 @@ public class Main extends JavaPlugin { ConfigHandler config = new ConfigHandler(this); EnumHandler enumHandler = new EnumHandler(); - OutputFormatter outputFormatter = new OutputFormatter(config); + OutputFormatter outputFormatter = new OutputFormatter(config, this); StatManager statManager = new StatManager(enumHandler, this); this.getCommand("statistic").setExecutor(new StatCommand(outputFormatter, statManager, this)); this.getCommand("statistic").setTabCompleter(new TabCompleter( enumHandler, statManager,this)); - this.getCommand("statisticreload").setExecutor(new ReloadCommand(config, outputFormatter)); + this.getCommand("statisticreload").setExecutor(new ReloadCommand(config, outputFormatter, this)); Bukkit.getPluginManager().registerEvents(new JoinListener(), this); this.getLogger().info("Enabled PlayerStats!"); @@ -45,4 +45,8 @@ public class Main extends JavaPlugin { } } + public long logTimeTaken(String className, long previousTime, int lineNumber) { + getLogger().info(className + " " + lineNumber + ": " + (System.currentTimeMillis() - previousTime)); + return System.currentTimeMillis(); + } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java index 70f4fd0..a65f563 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java @@ -7,9 +7,7 @@ import org.bukkit.OfflinePlayer; import org.bukkit.Statistic; import org.bukkit.entity.EntityType; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; public class StatManager { @@ -48,38 +46,32 @@ public class StatManager { OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); - plugin.getLogger().info("StatManager 51: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatManager", time, 49); if (player == null) throw new NullPointerException("No player called " + playerName + " was found!"); Statistic stat = getStatistic(statName); - plugin.getLogger().info("StatManager 56: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatManager", time, 53); if (stat != null) { switch (stat.getType()) { case UNTYPED -> { - plugin.getLogger().info("StatManager 62: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatManager", time, 58); return player.getStatistic(stat); } case BLOCK -> { - plugin.getLogger().info("StatManager 67: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatManager", time, 62); Material block = enumHandler.getBlock(subStatEntryName); if (block == null) throw new NullPointerException(subStatEntryName + " is not a valid block name!"); return player.getStatistic(stat, block); } case ENTITY -> { - plugin.getLogger().info("StatManager 74: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatManager", time, 68); EntityType entity = enumHandler.getEntityType(subStatEntryName); if (entity == null) throw new NullPointerException(subStatEntryName + " is not a valid entity name!"); return player.getStatistic(stat, entity); } case ITEM -> { - plugin.getLogger().info("StatManager 81: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatManager", time, 74); Material item = enumHandler.getItem(subStatEntryName); if (item == null) throw new NullPointerException(subStatEntryName + " is not a valid item name!"); return player.getStatistic(stat, item); @@ -89,15 +81,62 @@ public class StatManager { throw new NullPointerException(statName + " is not a valid statistic name!"); } - //returns the statistic enum constant, or null if non-existent (param: statName, not case sensitive) - private Statistic getStatistic(String statName) { - try { - return Statistic.valueOf(statName.toUpperCase()); - } - catch (IllegalArgumentException | NullPointerException exception) { - plugin.logStatRelatedExceptions(exception); - return null; + public LinkedHashMap getTopStatistics(String statistic) { + return getTopStatistics(statistic, null); + } + + public LinkedHashMap getTopStatistics(String statName, String subStatEntry) { + long time = System.currentTimeMillis(); + HashMap playerStats = new HashMap<>((int) (offlinePlayerHandler.getOfflinePlayerCount() * 1.05)); + time = plugin.logTimeTaken("StatManager", time, 91); + + Statistic stat = getStatistic(statName); + time = plugin.logTimeTaken("StatManager", time, 94); + + if (stat != null) { + switch (stat.getType()) { + case UNTYPED -> { + time = plugin.logTimeTaken("StatManager", time, 99); + for (String playerName : offlinePlayerHandler.getAllOfflinePlayerNames()) { + OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); + if (player != null) { + playerStats.put(playerName, player.getStatistic(stat)); + } + } + } + } + + time = plugin.logTimeTaken("StatManager", time, 109); + LinkedHashMap topStats = playerStats.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + time = plugin.logTimeTaken("StatManager", time, 113); + + plugin.getLogger().info("Top 10: " + topStats); + time = plugin.logTimeTaken("StatManager", time, 116); + + HashMap playerStats2 = new HashMap<>((int) (offlinePlayerHandler.getOfflinePlayerCount() * 1.05)); + offlinePlayerHandler.getAllOfflinePlayerNames().stream().forEach(playerName -> { + OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); + if (player != null) playerStats2.put(playerName, player.getStatistic(stat)); + }); + + time = plugin.logTimeTaken("StatManager", time, 124); + LinkedHashMap topStats2 = playerStats2.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + + time = plugin.logTimeTaken("StatManager", time, 129); + plugin.getLogger().info("Top 10: " + topStats2); + time = plugin.logTimeTaken("StatManager", time, 131); + return topStats; } + return null; + } + + //checks if string is a valid statistic (param: statName, not case sensitive) + public boolean isStatistic(String statName) { + return statNames.contains(statName.toLowerCase()); } //gets the type of the statistic from the string, otherwise returns null (param: statName, not case sensitive) @@ -111,17 +150,17 @@ public class StatManager { } } - //checks if string is a valid statistic (param: statName, not case sensitive) - public boolean isStatistic(String statName) { - return statNames.contains(statName.toLowerCase()); + //returns the names of all general statistics in lowercase + public List getStatNames() { + return statNames; } - //checks if string is a valid substatistic dealing with entities (param: statName, not case sensitive) - public boolean isStatEntityType(String statName) { - return entityStatNames.contains(statName.toLowerCase()); + //returns all statistics that have type entities, in lowercase + public List getEntityStatNames() { + return entityStatNames; } - //checks in the most general sense if this statistic is a substatistic (param: statName, not case sensitive) + //checks if this statistic is a subStatEntry, meaning it is a block, item or entity (param: statName, not case sensitive) public boolean isSubStatEntry(String statName) { return subStatEntryNames.contains(statName.toLowerCase()); } @@ -148,18 +187,14 @@ public class StatManager { return false; } - //returns the names of all general statistics in lowercase - public List getStatNames() { - return statNames; - } - - //returns all statistics that have type entities, in lowercase - public List getEntityTypeNames() { - return entityStatNames; - } - - //returns all substatnames in lowercase - public List getSubStatEntryNames() { - return subStatEntryNames; + //returns the statistic enum constant, or null if non-existent (param: statName, not case sensitive) + private Statistic getStatistic(String statName) { + try { + return Statistic.valueOf(statName.toUpperCase()); + } + catch (IllegalArgumentException | NullPointerException exception) { + plugin.logStatRelatedExceptions(exception); + return null; + } } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java index 2702d55..8da0f96 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java @@ -1,6 +1,8 @@ package com.gmail.artemis.the.gr8.playerstats.commands; import com.gmail.artemis.the.gr8.playerstats.ConfigHandler; +import com.gmail.artemis.the.gr8.playerstats.Main; +import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OutputFormatter; import org.bukkit.ChatColor; import org.bukkit.command.Command; @@ -11,18 +13,30 @@ import org.jetbrains.annotations.NotNull; public class ReloadCommand implements CommandExecutor { private final ConfigHandler config; + private final OfflinePlayerHandler offlinePlayerHandler; private final OutputFormatter outputFormatter; + private final Main plugin; - public ReloadCommand(ConfigHandler c, OutputFormatter o) { + public ReloadCommand(ConfigHandler c, OutputFormatter o, Main p) { outputFormatter = o; + offlinePlayerHandler = OfflinePlayerHandler.getInstance(); config = c; + plugin = p; } @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { if (config.reloadConfig()) { + long time = System.currentTimeMillis(); + outputFormatter.updateOutputColors(); + time = plugin.logTimeTaken("ReloadCommand", time, 33); + + offlinePlayerHandler.updateOfflinePlayers(); + time = plugin.logTimeTaken("ReloadCommand", time, 36); + sender.sendMessage(ChatColor.GREEN + "Config reloaded!"); + time = plugin.logTimeTaken("ReloadCommand", time, 39); return true; } return false; diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java index 5f82ec9..230ff9f 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java @@ -10,6 +10,8 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.LinkedHashMap; + public class StatCommand implements CommandExecutor { @@ -37,74 +39,75 @@ public class StatCommand implements CommandExecutor { String subStatEntry = null; String playerName = null; boolean playerFlag = false; + boolean topFlag = false; - plugin.getLogger().info("onCommand 40: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatCommand", time, 44); //all args are in lowercase for (String arg : args) { if (statManager.isStatistic(arg)) { statName = (statName == null) ? arg : statName; - plugin.getLogger().info("onCommand 48: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatCommand", time, 50); } else if (statManager.isSubStatEntry(arg)) { if (arg.equalsIgnoreCase("player")) { if (!playerFlag) { subStatEntry = (subStatEntry == null) ? arg : subStatEntry; playerFlag = true; - plugin.getLogger().info("onCommand 56: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatCommand", time, 57); } } else { subStatEntry = (subStatEntry == null || playerFlag) ? arg : subStatEntry; - plugin.getLogger().info("onCommand 62: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatCommand", time, 62); } } + else if (arg.equalsIgnoreCase("top")) { + topFlag = true; + } else if (arg.equalsIgnoreCase("me") && sender instanceof Player) { playerName = sender.getName(); - plugin.getLogger().info("onCommand 69: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatCommand", time, 71); } else if (offlinePlayerHandler.isOfflinePlayerName(arg)) { playerName = (playerName == null) ? arg : playerName; - plugin.getLogger().info("onCommand 74: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatCommand", time, 75); } } - if (playerName != null && statName != null) { - plugin.getLogger().info("onCommand 79: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + if (statName != null) { + time = plugin.logTimeTaken("StatCommand", time, 79); + subStatEntry = statManager.isMatchingSubStatEntry(statName, subStatEntry) ? subStatEntry : null; - plugin.getLogger().info("onCommand 82: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); - try { - plugin.getLogger().info("onCommand 85: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("StatCommand", time, 82); - int stat = statManager.getStatistic(statName, subStatEntry, playerName); - plugin.getLogger().info("onCommand 89: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); - - String msg = outputFormatter.formatPlayerStat(playerName, statName, subStatEntry, stat); - plugin.getLogger().info("onCommand 93: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); - - sender.sendMessage(msg); - plugin.getLogger().info("onCommand 97: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); - } - catch (Exception e) { - sender.sendMessage(e.toString()); + if (topFlag) { + LinkedHashMap topStats = statManager.getTopStatistics(statName, subStatEntry); + return true; } + else if (playerName != null) { + try { + time = plugin.logTimeTaken("StatCommand", time, 91); + + int stat = statManager.getStatistic(statName, subStatEntry, playerName); + time = plugin.logTimeTaken("StatCommand", time, 94); + + String msg = outputFormatter.formatPlayerStat(playerName, statName, subStatEntry, stat); + time = plugin.logTimeTaken("StatCommand", time, 97); + + sender.sendMessage(msg); + time = plugin.logTimeTaken("StatCommand", time, 100); + } + catch (Exception e) { + sender.sendMessage(e.toString()); + } + } } } - plugin.getLogger().info("onCommand 106: " + (System.currentTimeMillis() - time)); + time = plugin.logTimeTaken("StatCommand", time, 108); plugin.getLogger().info("Total time elapsed: " + (System.currentTimeMillis() - startTime)); return true; } + } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/TabCompleter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/TabCompleter.java index 3ace0a9..970cb46 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/TabCompleter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/TabCompleter.java @@ -66,7 +66,7 @@ public class TabCompleter implements org.bukkit.command.TabCompleter { //if previous arg = "player", suggest playerNames else if (args[args.length-2].equalsIgnoreCase("player")) { - if (args.length >= 3 && statManager.getEntityTypeNames().contains(args[args.length-3].toLowerCase())) { + if (args.length >= 3 && statManager.getEntityStatNames().contains(args[args.length-3].toLowerCase())) { tabSuggestions = commandOptions; } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java index b19db79..37a984c 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java @@ -4,14 +4,13 @@ import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import java.util.*; -import java.util.stream.Collectors; public class OfflinePlayerHandler { private static OfflinePlayerHandler instance; - private List offlinePlayers; - private List offlinePlayerNames; private HashMap offlinePlayerMap; + private List offlinePlayerNames; + private int totalOfflinePlayers; private OfflinePlayerHandler() { updateOfflinePlayers(); @@ -30,25 +29,45 @@ public class OfflinePlayerHandler { public OfflinePlayer getOfflinePlayer(String playerName) { long time = System.currentTimeMillis(); - - OfflinePlayer player = offlinePlayerMap.get(playerName); - System.out.println(("OfflinePlayerHandler 35: " + (System.currentTimeMillis() - time))); - return player; + return offlinePlayerMap.get(playerName); } - public List getAllOfflinePlayers() { - return offlinePlayers; + public int getOfflinePlayerCount() { + return totalOfflinePlayers > 0 ? totalOfflinePlayers : 1; } public List getAllOfflinePlayerNames() { return offlinePlayerNames; } + //stores a private HashMap with keys:playerName and values:OfflinePlayer, and a private list of the names for easy access public void updateOfflinePlayers() { - offlinePlayerMap = new HashMap<>(); - offlinePlayers = Arrays.stream(Bukkit.getOfflinePlayers()).filter(offlinePlayer -> - offlinePlayer.getName() != null && offlinePlayer.hasPlayedBefore()).collect(Collectors.toList()); - offlinePlayerNames = offlinePlayers.stream().map(OfflinePlayer::getName).collect(Collectors.toList()); - offlinePlayers.forEach(offlinePlayer -> offlinePlayerMap.put(offlinePlayer.getName(), offlinePlayer)); + long totalTime = System.currentTimeMillis(); + long time = System.currentTimeMillis(); + if (offlinePlayerMap == null) offlinePlayerMap = new HashMap<>(); + else if (!offlinePlayerMap.isEmpty()) { + offlinePlayerMap.clear(); + } + + if (offlinePlayerNames == null) offlinePlayerNames = new ArrayList<>(); + else if (!offlinePlayerNames.isEmpty()) { + offlinePlayerNames.clear(); + } + + Arrays.stream(Bukkit.getOfflinePlayers()).filter(offlinePlayer -> + offlinePlayer.getName() != null && offlinePlayer.hasPlayedBefore()).forEach(offlinePlayer -> { + offlinePlayerNames.add(offlinePlayer.getName()); + offlinePlayerMap.put(offlinePlayer.getName(), offlinePlayer); + }); + System.out.println("OfflinePlayerHandler, making the HashMap and ArrayList: " + (System.currentTimeMillis() - time)); + time = System.currentTimeMillis(); + + totalOfflinePlayers = offlinePlayerMap.size(); + System.out.println("OfflinePlayerHandler, counting the HashMap: " + (System.currentTimeMillis() - time)); + time = System.currentTimeMillis(); + + totalOfflinePlayers = offlinePlayerNames.size(); + System.out.println("OfflinePlayerHandler, counting the ArrayList: " + (System.currentTimeMillis() - time)); + System.out.println("updateOfflinePlayers total time: " + (System.currentTimeMillis() - totalTime)); } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java index beb4ca6..a3a102d 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java @@ -15,10 +15,12 @@ public class OutputFormatter { //subStatNames(Ranked) //numbers(Ranked) private final ConfigHandler config; + private final Main plugin; private HashMap chatColors; - public OutputFormatter(ConfigHandler c) { + public OutputFormatter(ConfigHandler c, Main p) { config = c; + plugin = p; updateOutputColors(); } @@ -32,19 +34,15 @@ public class OutputFormatter { public String formatPlayerStat(String playerName, String statName, String subStatEntryName, int stat) { long time = System.currentTimeMillis(); - System.out.println("OutputFormatter 33: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("OutputFormatter", time, 37); String subStat = subStatEntryName != null ? chatColors.get("subStatNames") + " (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : ""; - - System.out.println("OutputFormatter 39: " + (System.currentTimeMillis() - time)); - time = System.currentTimeMillis(); + time = plugin.logTimeTaken("OutputFormatter", time, 41); String msg = chatColors.get("playerNames") + playerName + chatColors.get("numbers") + ": " + stat + " " + chatColors.get("statNames") + statName.toLowerCase().replace("_", " ") + subStat; - - System.out.println("OutputFormatter 45: " + (System.currentTimeMillis() - time)); + time = plugin.logTimeTaken("OutputFormatter", time, 45); return msg; } From af23f05af4e9e650c7df2892166dfcfff4e30365 Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Wed, 11 May 2022 17:23:37 +0200 Subject: [PATCH 2/8] Implemented a lot of error throwing and catching, and hopefully made an efficient method to get top stats for all types (#1) --- .../artemis/the/gr8/playerstats/Main.java | 15 +- .../the/gr8/playerstats/StatManager.java | 251 +++++++++++------- .../playerstats/commands/ReloadCommand.java | 6 +- .../gr8/playerstats/commands/StatCommand.java | 43 ++- .../playerstats/listeners/JoinListener.java | 2 + .../gr8/playerstats/utils/EnumHandler.java | 8 +- .../playerstats/utils/OutputFormatter.java | 42 +-- 7 files changed, 204 insertions(+), 163 deletions(-) diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java index 2fdb746..8191a71 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java @@ -5,7 +5,6 @@ import com.gmail.artemis.the.gr8.playerstats.commands.StatCommand; import com.gmail.artemis.the.gr8.playerstats.commands.TabCompleter; import com.gmail.artemis.the.gr8.playerstats.listeners.JoinListener; import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; -import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OutputFormatter; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; @@ -35,18 +34,8 @@ public class Main extends JavaPlugin { this.getLogger().info("Disabled PlayerStats!"); } - - public void logStatRelatedExceptions(Exception exception) { - if (exception instanceof IllegalArgumentException) { - getLogger().warning("IllegalArgumentException - this is probably not a valid statistic name!"); - } - else if (exception instanceof NullPointerException) { - getLogger().warning("NullPointerException - no statistic name was provided"); - } - } - - public long logTimeTaken(String className, long previousTime, int lineNumber) { - getLogger().info(className + " " + lineNumber + ": " + (System.currentTimeMillis() - previousTime)); + public long logTimeTaken(String className, String methodName, long previousTime, int lineNumber) { + getLogger().info(className + " " + methodName + " " + lineNumber + ": " + (System.currentTimeMillis() - previousTime)); return System.currentTimeMillis(); } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java index a65f563..d7a4c2f 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java @@ -6,7 +6,9 @@ import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.Statistic; import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; +import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -19,6 +21,8 @@ public class StatManager { private final List entityStatNames; private final List subStatEntryNames; + private final String className; + public StatManager(EnumHandler e, Main p) { plugin = p; enumHandler = e; @@ -34,104 +38,141 @@ public class StatManager { subStatEntryNames.addAll(enumHandler.getBlockNames()); subStatEntryNames.addAll(enumHandler.getEntityTypeNames()); subStatEntryNames.addAll(enumHandler.getItemNames()); - } - public int getStatistic(String statName, String playerName) throws IllegalArgumentException, NullPointerException { - return getStatistic(statName, null, playerName); + className = "StatManger"; } //returns the integer associated with a certain statistic for a player public int getStatistic(String statName, String subStatEntryName, String playerName) throws IllegalArgumentException, NullPointerException { + OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); + if (player != null) { + Statistic stat = getStatisticEnum(statName); + if (stat != null) { + return getPlayerStat(player, stat, subStatEntryName); + } + throw new IllegalArgumentException("Statistic " + statName + " could not be retrieved!"); + } + throw new IllegalArgumentException("Player object for " + playerName + " could not be retrieved!"); + } + + private int getPlayerStat(@NotNull OfflinePlayer player, @NotNull Statistic stat, String subStatEntryName) throws IllegalArgumentException { + String methodName = "getPlayerStat"; long time = System.currentTimeMillis(); - OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); - - time = plugin.logTimeTaken("StatManager", time, 49); - if (player == null) throw new NullPointerException("No player called " + playerName + " was found!"); - - Statistic stat = getStatistic(statName); - time = plugin.logTimeTaken("StatManager", time, 53); - - if (stat != null) { - switch (stat.getType()) { - case UNTYPED -> { - time = plugin.logTimeTaken("StatManager", time, 58); - return player.getStatistic(stat); - } - case BLOCK -> { - time = plugin.logTimeTaken("StatManager", time, 62); - Material block = enumHandler.getBlock(subStatEntryName); - if (block == null) throw new NullPointerException(subStatEntryName + " is not a valid block name!"); - return player.getStatistic(stat, block); - } - case ENTITY -> { - time = plugin.logTimeTaken("StatManager", time, 68); - EntityType entity = enumHandler.getEntityType(subStatEntryName); - if (entity == null) throw new NullPointerException(subStatEntryName + " is not a valid entity name!"); - return player.getStatistic(stat, entity); - } - case ITEM -> { - time = plugin.logTimeTaken("StatManager", time, 74); - Material item = enumHandler.getItem(subStatEntryName); - if (item == null) throw new NullPointerException(subStatEntryName + " is not a valid item name!"); - return player.getStatistic(stat, item); - } + switch (stat.getType()) { + case UNTYPED -> { + return player.getStatistic(stat); + } + case BLOCK -> { + Material block = enumHandler.getBlock(subStatEntryName); + plugin.logTimeTaken(className, methodName, time, 68); + if (block != null) { + return player.getStatistic(stat, block); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid block name!"); } } - throw new NullPointerException(statName + " is not a valid statistic name!"); + case ENTITY -> { + EntityType entity = enumHandler.getEntityType(subStatEntryName); + plugin.logTimeTaken(className, methodName, time, 78); + if (entity != null) { + return player.getStatistic(stat, entity); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid entity name!"); + } + } + case ITEM -> { + Material item = enumHandler.getItem(subStatEntryName); + plugin.logTimeTaken(className, methodName, time, 88); + if (item != null) { + return player.getStatistic(stat, item); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid item name!"); + } + } + default -> + throw new IllegalArgumentException("This statistic does not seem to be of type:untyped/block/entity/item, I think we should panic"); + } } - public LinkedHashMap getTopStatistics(String statistic) { - return getTopStatistics(statistic, null); - } - - public LinkedHashMap getTopStatistics(String statName, String subStatEntry) { + public LinkedHashMap getTopStatistics(String statName, String subStatEntry) throws IllegalArgumentException, NullPointerException { + String methodName = "getTopStatistic"; long time = System.currentTimeMillis(); - HashMap playerStats = new HashMap<>((int) (offlinePlayerHandler.getOfflinePlayerCount() * 1.05)); - time = plugin.logTimeTaken("StatManager", time, 91); - Statistic stat = getStatistic(statName); - time = plugin.logTimeTaken("StatManager", time, 94); + Statistic stat = getStatisticEnum(statName); + time = plugin.logTimeTaken(className, methodName, time, 106); if (stat != null) { - switch (stat.getType()) { - case UNTYPED -> { - time = plugin.logTimeTaken("StatManager", time, 99); - for (String playerName : offlinePlayerHandler.getAllOfflinePlayerNames()) { - OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); - if (player != null) { - playerStats.put(playerName, player.getStatistic(stat)); + if (stat.getType().equals(Statistic.Type.UNTYPED) || isMatchingSubStatEntry(stat, subStatEntry)) { + HashMap playerStats = new HashMap<>((int) (offlinePlayerHandler.getOfflinePlayerCount() * 1.05)); + time = plugin.logTimeTaken(className, methodName, time, 111); + + for (String playerName : offlinePlayerHandler.getAllOfflinePlayerNames()) { + OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); + if (player != null) { + try { + playerStats.put(playerName, getPlayerStat(player, stat, subStatEntry)); + } + catch (IllegalArgumentException ignored) { } } } + time = plugin.logTimeTaken(className, methodName, time, 123); + + LinkedHashMap topStats = playerStats.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + time = plugin.logTimeTaken(className, methodName, time, 128); + + plugin.getLogger().info("Top 10: " + topStats); + plugin.logTimeTaken(className, methodName, time, 131); + return topStats; } - - time = plugin.logTimeTaken("StatManager", time, 109); - LinkedHashMap topStats = playerStats.entrySet().stream() - .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) - .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - time = plugin.logTimeTaken("StatManager", time, 113); - - plugin.getLogger().info("Top 10: " + topStats); - time = plugin.logTimeTaken("StatManager", time, 116); - - HashMap playerStats2 = new HashMap<>((int) (offlinePlayerHandler.getOfflinePlayerCount() * 1.05)); - offlinePlayerHandler.getAllOfflinePlayerNames().stream().forEach(playerName -> { - OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); - if (player != null) playerStats2.put(playerName, player.getStatistic(stat)); - }); - - time = plugin.logTimeTaken("StatManager", time, 124); - LinkedHashMap topStats2 = playerStats2.entrySet().stream() - .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) - .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - - time = plugin.logTimeTaken("StatManager", time, 129); - plugin.getLogger().info("Top 10: " + topStats2); - time = plugin.logTimeTaken("StatManager", time, 131); - return topStats; + throw new IllegalArgumentException(subStatEntry + " is not a valid substatistic entry for this statistic!"); } - return null; + throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); + } + + public LinkedHashMap getTopStatistics2(String statName, String subStatEntry) { + String methodName = "getTopStatistics2"; + long time = System.currentTimeMillis(); + + Statistic stat = getStatisticEnum(statName); + time = plugin.logTimeTaken(className, methodName, time, 144); + + if (stat != null) { + if (stat.getType().equals(Statistic.Type.UNTYPED) || isMatchingSubStatEntry(stat, subStatEntry)) { + HashMap playerStats = new HashMap<>((int) (offlinePlayerHandler.getOfflinePlayerCount() * 1.05)); + time = plugin.logTimeTaken(className, methodName, time, 149); + + offlinePlayerHandler.getAllOfflinePlayerNames().forEach(playerName -> { + OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); + if (player != null) + try { + playerStats.put(playerName, getPlayerStat(player, stat, subStatEntry)); + } + catch (IllegalArgumentException ignored) { + + } + }); + + time = plugin.logTimeTaken(className, methodName, time, 162); + + LinkedHashMap topStats = playerStats.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + time = plugin.logTimeTaken(className, methodName, time, 167); + + plugin.getLogger().info("Top 10: " + topStats); + plugin.logTimeTaken(className, methodName, time, 170); + return topStats; + } + throw new IllegalArgumentException(subStatEntry + " is not a valid substatistic entry for this statistic!"); + } + throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); } //checks if string is a valid statistic (param: statName, not case sensitive) @@ -144,8 +185,12 @@ public class StatManager { try { return Statistic.valueOf(statName.toUpperCase()).getType(); } - catch (IllegalArgumentException | NullPointerException exception) { - plugin.logStatRelatedExceptions(exception); + catch (IllegalArgumentException e) { + plugin.getLogger().warning("IllegalArgumentException: " + statName + " is not a valid statistic name!"); + return null; + } + catch (NullPointerException e) { + plugin.getLogger().warning("NullPointerException: please provide a statistic name!"); return null; } } @@ -167,33 +212,41 @@ public class StatManager { //checks whether a subStatEntry is of the type that the statistic requires public boolean isMatchingSubStatEntry(String statName, String subStatEntry) { - Statistic.Type type = getStatType(statName); - if (type != null && subStatEntry != null) { - switch (type) { - case ENTITY -> { - return enumHandler.isEntityType(subStatEntry); - } - case ITEM -> { - return enumHandler.isItem(subStatEntry); - } - case BLOCK -> { - return enumHandler.isBlock(subStatEntry); - } - case UNTYPED -> { - return false; - } + Statistic stat = getStatisticEnum(statName); + return (stat != null && isMatchingSubStatEntry(stat, subStatEntry)); + } + + private boolean isMatchingSubStatEntry(@NotNull Statistic stat, String subStatEntry) { + switch (stat.getType()) { + case ENTITY -> { + return subStatEntry != null && enumHandler.isEntityType(subStatEntry); + } + case ITEM -> { + return subStatEntry != null && enumHandler.isItem(subStatEntry); + } + case BLOCK -> { + return subStatEntry != null && enumHandler.isBlock(subStatEntry); + } + case UNTYPED -> { + return subStatEntry==null; + } + default -> { + return false; } } - return false; } //returns the statistic enum constant, or null if non-existent (param: statName, not case sensitive) - private Statistic getStatistic(String statName) { + private Statistic getStatisticEnum(String statName) { try { return Statistic.valueOf(statName.toUpperCase()); } - catch (IllegalArgumentException | NullPointerException exception) { - plugin.logStatRelatedExceptions(exception); + catch (IllegalArgumentException e) { + plugin.getLogger().warning("IllegalArgumentException: " + statName + " is not a valid statistic name!"); + return null; + } + catch (NullPointerException e) { + plugin.getLogger().warning("NullPointerException: please provide a statistic name!"); return null; } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java index 8da0f96..2533c42 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java @@ -30,13 +30,13 @@ public class ReloadCommand implements CommandExecutor { long time = System.currentTimeMillis(); outputFormatter.updateOutputColors(); - time = plugin.logTimeTaken("ReloadCommand", time, 33); + time = plugin.logTimeTaken("ReloadCommand", "onCommand", time, 33); offlinePlayerHandler.updateOfflinePlayers(); - time = plugin.logTimeTaken("ReloadCommand", time, 36); + time = plugin.logTimeTaken("ReloadCommand", "onCommand", time, 36); sender.sendMessage(ChatColor.GREEN + "Config reloaded!"); - time = plugin.logTimeTaken("ReloadCommand", time, 39); + time = plugin.logTimeTaken("ReloadCommand", "onCommand", time, 39); return true; } return false; diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java index 230ff9f..c9c26c6 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java @@ -31,35 +31,28 @@ public class StatCommand implements CommandExecutor { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { long time = System.currentTimeMillis(); - long startTime = System.currentTimeMillis(); + //part 1: collecting all relevant information from the args if (args.length >= 2) { - String statName = null; String subStatEntry = null; String playerName = null; boolean playerFlag = false; boolean topFlag = false; - time = plugin.logTimeTaken("StatCommand", time, 44); - - //all args are in lowercase for (String arg : args) { if (statManager.isStatistic(arg)) { statName = (statName == null) ? arg : statName; - time = plugin.logTimeTaken("StatCommand", time, 50); } else if (statManager.isSubStatEntry(arg)) { if (arg.equalsIgnoreCase("player")) { if (!playerFlag) { subStatEntry = (subStatEntry == null) ? arg : subStatEntry; playerFlag = true; - time = plugin.logTimeTaken("StatCommand", time, 57); } } else { subStatEntry = (subStatEntry == null || playerFlag) ? arg : subStatEntry; - time = plugin.logTimeTaken("StatCommand", time, 62); } } @@ -68,45 +61,39 @@ public class StatCommand implements CommandExecutor { } else if (arg.equalsIgnoreCase("me") && sender instanceof Player) { playerName = sender.getName(); - time = plugin.logTimeTaken("StatCommand", time, 71); } else if (offlinePlayerHandler.isOfflinePlayerName(arg)) { playerName = (playerName == null) ? arg : playerName; - time = plugin.logTimeTaken("StatCommand", time, 75); } } - if (statName != null) { - time = plugin.logTimeTaken("StatCommand", time, 79); + //part 2: sending the information to the StatManager + if (statName != null) { subStatEntry = statManager.isMatchingSubStatEntry(statName, subStatEntry) ? subStatEntry : null; - time = plugin.logTimeTaken("StatCommand", time, 82); if (topFlag) { - LinkedHashMap topStats = statManager.getTopStatistics(statName, subStatEntry); - return true; + try { + LinkedHashMap topStats = statManager.getTopStatistics(statName, subStatEntry); + return true; + } + catch (Exception e) { + sender.sendMessage(outputFormatter.formatExceptions(e.toString())); + } + } else if (playerName != null) { try { - time = plugin.logTimeTaken("StatCommand", time, 91); - - int stat = statManager.getStatistic(statName, subStatEntry, playerName); - time = plugin.logTimeTaken("StatCommand", time, 94); - - String msg = outputFormatter.formatPlayerStat(playerName, statName, subStatEntry, stat); - time = plugin.logTimeTaken("StatCommand", time, 97); - - sender.sendMessage(msg); - time = plugin.logTimeTaken("StatCommand", time, 100); + sender.sendMessage(outputFormatter.formatPlayerStat(playerName, statName, subStatEntry, statManager.getStatistic + (statName, subStatEntry, playerName))); } catch (Exception e) { - sender.sendMessage(e.toString()); + sender.sendMessage(outputFormatter.formatExceptions(e.toString())); } } } } - time = plugin.logTimeTaken("StatCommand", time, 108); - plugin.getLogger().info("Total time elapsed: " + (System.currentTimeMillis() - startTime)); + plugin.logTimeTaken("StatCommand", "onCommand", time, 90); return true; } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/listeners/JoinListener.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/listeners/JoinListener.java index f489c26..2f7cc0d 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/listeners/JoinListener.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/listeners/JoinListener.java @@ -1,6 +1,7 @@ package com.gmail.artemis.the.gr8.playerstats.listeners; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; +import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; @@ -13,6 +14,7 @@ public class JoinListener implements Listener { offlinePlayerHandler = OfflinePlayerHandler.getInstance(); } + @EventHandler public void onPlayerJoin(PlayerJoinEvent joinEvent) { if (!joinEvent.getPlayer().hasPlayedBefore()) { offlinePlayerHandler.updateOfflinePlayers(); diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/EnumHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/EnumHandler.java index 2074569..8688b59 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/EnumHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/EnumHandler.java @@ -2,6 +2,7 @@ package com.gmail.artemis.the.gr8.playerstats.utils; import org.bukkit.Material; import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.List; @@ -27,6 +28,7 @@ public class EnumHandler { } //returns corresponding item enum constant (uppercase), otherwise null (param: itemName, not case sensitive) + @Nullable public Material getItem(String itemName) { return Material.matchMaterial(itemName); } @@ -41,13 +43,14 @@ public class EnumHandler { } //returns EntityType enum constant (uppercase) if the input name is valid, otherwise null (param: entityName, not case sensitive) + @Nullable public EntityType getEntityType(String entityName) { - EntityType entityType = null; + EntityType entityType; try { entityType = EntityType.valueOf(entityName.toUpperCase()); } catch (IllegalArgumentException | NullPointerException exception) { - exception.printStackTrace(); + return null; } return entityType; } @@ -62,6 +65,7 @@ public class EnumHandler { } //returns corresponding block enum constant (uppercase), otherwise null (param: materialName, not case sensitive) + @Nullable public Material getBlock(String materialName) { return Material.matchMaterial(materialName); } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java index a3a102d..1eaa3b7 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java @@ -17,35 +17,41 @@ public class OutputFormatter { private final ConfigHandler config; private final Main plugin; private HashMap chatColors; + private String pluginPrefix; + private String className; public OutputFormatter(ConfigHandler c, Main p) { config = c; plugin = p; + pluginPrefix = ChatColor.GRAY + "[" + ChatColor.GOLD + "PlayerStats" + ChatColor.GRAY + "]" + ChatColor.RESET; + updateOutputColors(); + className = "OutputFormatter"; + } + + public String formatExceptions(String exception) { + return pluginPrefix + " " + exception; + } + + public String formatPlayerStat(String playerName, String statName, String subStatEntryName, int stat) { + String methodName = "formatPlayerStats"; + long time = System.currentTimeMillis(); + time = plugin.logTimeTaken(className, methodName, time, 39); + + String subStat = subStatEntryName != null ? + chatColors.get("subStatNames") + " (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : ""; + time = plugin.logTimeTaken(className, methodName, time, 43); + + String msg = chatColors.get("playerNames") + playerName + chatColors.get("numbers") + ": " + stat + " " + + chatColors.get("statNames") + statName.toLowerCase().replace("_", " ") + subStat; + time = plugin.logTimeTaken(className, methodName, time, 47); + return msg; } public String formatTopStats(LinkedHashMap topStats) { return ""; } - public String formatPlayerStat(String playerName, String statName, int stat) { - return formatPlayerStat(playerName, statName, null, stat); - } - - public String formatPlayerStat(String playerName, String statName, String subStatEntryName, int stat) { - long time = System.currentTimeMillis(); - time = plugin.logTimeTaken("OutputFormatter", time, 37); - - String subStat = subStatEntryName != null ? - chatColors.get("subStatNames") + " (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : ""; - time = plugin.logTimeTaken("OutputFormatter", time, 41); - - String msg = chatColors.get("playerNames") + playerName + chatColors.get("numbers") + ": " + stat + " " + - chatColors.get("statNames") + statName.toLowerCase().replace("_", " ") + subStat; - time = plugin.logTimeTaken("OutputFormatter", time, 45); - return msg; - } - public void updateOutputColors() { chatColors = config.getChatColors(); } From dad332b961d14cac6c398eb01b1507ca5e70b953 Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Thu, 12 May 2022 17:11:52 +0200 Subject: [PATCH 3/8] Worked on formatting, started implementing method to get stats below each other at same indentation --- .../the/gr8/playerstats/ConfigHandler.java | 17 ++-- .../the/gr8/playerstats/StatManager.java | 97 +++++++++---------- .../gr8/playerstats/commands/StatCommand.java | 8 ++ .../playerstats/utils/OutputFormatter.java | 64 +++++++++--- src/main/resources/config.yml | 8 +- 5 files changed, 118 insertions(+), 76 deletions(-) diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java index 2bbe46b..81dfa8c 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java @@ -24,16 +24,17 @@ public class ConfigHandler { HashMap chatColors = new HashMap<>(); ConfigurationSection individual = config.getConfigurationSection("individual-statistics"); - chatColors.put("playerNames", getChatColor(individual, "player-names")); - chatColors.put("statNames", getChatColor(individual, "stat-names")); - chatColors.put("subStatNames", getChatColor(individual, "sub-stat-names")); - chatColors.put("numbers", getChatColor(individual, "numbers")); + chatColors.put("player-names", getChatColor(individual, "player-names")); + chatColors.put("stat-names", getChatColor(individual, "stat-names")); + chatColors.put("sub-stat-names", getChatColor(individual, "sub-stat-names")); + chatColors.put("stat-numbers", getChatColor(individual, "stat-numbers")); ConfigurationSection ranked = config.getConfigurationSection("ranked-list"); - chatColors.put("playerNamesRanked", getChatColor(ranked, "player-names")); - chatColors.put("statNamesRanked", getChatColor(ranked, "stat-names")); - chatColors.put("subStatNamesRanked", getChatColor(ranked, "sub-stat-names")); - chatColors.put("numbersRanked", getChatColor(ranked, "numbers")); + chatColors.put("player-names-ranked", getChatColor(ranked, "player-names")); + chatColors.put("list-title", getChatColor(ranked, "list-title")); + chatColors.put("sub-stat-names-ranked", getChatColor(ranked, "sub-stat-names")); + chatColors.put("stat-numbers-ranked", getChatColor(ranked, "stat-numbers")); + chatColors.put("list-numbers", getChatColor(ranked, "list-numbers")); return chatColors; } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java index d7a4c2f..b3e79c2 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java @@ -8,7 +8,6 @@ import org.bukkit.Statistic; import org.bukkit.entity.EntityType; import org.jetbrains.annotations.NotNull; -import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -55,49 +54,6 @@ public class StatManager { throw new IllegalArgumentException("Player object for " + playerName + " could not be retrieved!"); } - private int getPlayerStat(@NotNull OfflinePlayer player, @NotNull Statistic stat, String subStatEntryName) throws IllegalArgumentException { - String methodName = "getPlayerStat"; - long time = System.currentTimeMillis(); - - switch (stat.getType()) { - case UNTYPED -> { - return player.getStatistic(stat); - } - case BLOCK -> { - Material block = enumHandler.getBlock(subStatEntryName); - plugin.logTimeTaken(className, methodName, time, 68); - if (block != null) { - return player.getStatistic(stat, block); - } - else { - throw new IllegalArgumentException(subStatEntryName + " is not a valid block name!"); - } - } - case ENTITY -> { - EntityType entity = enumHandler.getEntityType(subStatEntryName); - plugin.logTimeTaken(className, methodName, time, 78); - if (entity != null) { - return player.getStatistic(stat, entity); - } - else { - throw new IllegalArgumentException(subStatEntryName + " is not a valid entity name!"); - } - } - case ITEM -> { - Material item = enumHandler.getItem(subStatEntryName); - plugin.logTimeTaken(className, methodName, time, 88); - if (item != null) { - return player.getStatistic(stat, item); - } - else { - throw new IllegalArgumentException(subStatEntryName + " is not a valid item name!"); - } - } - default -> - throw new IllegalArgumentException("This statistic does not seem to be of type:untyped/block/entity/item, I think we should panic"); - } - } - public LinkedHashMap getTopStatistics(String statName, String subStatEntry) throws IllegalArgumentException, NullPointerException { String methodName = "getTopStatistic"; long time = System.currentTimeMillis(); @@ -114,7 +70,10 @@ public class StatManager { OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); if (player != null) { try { - playerStats.put(playerName, getPlayerStat(player, stat, subStatEntry)); + int statistic = getPlayerStat(player, stat, subStatEntry); + if (statistic > 0) { + playerStats.put(playerName, getPlayerStat(player, stat, subStatEntry)); + } } catch (IllegalArgumentException ignored) { } @@ -175,11 +134,6 @@ public class StatManager { throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); } - //checks if string is a valid statistic (param: statName, not case sensitive) - public boolean isStatistic(String statName) { - return statNames.contains(statName.toLowerCase()); - } - //gets the type of the statistic from the string, otherwise returns null (param: statName, not case sensitive) public Statistic.Type getStatType(String statName) { try { @@ -205,6 +159,11 @@ public class StatManager { return entityStatNames; } + //checks if string is a valid statistic (param: statName, not case sensitive) + public boolean isStatistic(String statName) { + return statNames.contains(statName.toLowerCase()); + } + //checks if this statistic is a subStatEntry, meaning it is a block, item or entity (param: statName, not case sensitive) public boolean isSubStatEntry(String statName) { return subStatEntryNames.contains(statName.toLowerCase()); @@ -236,6 +195,44 @@ public class StatManager { } } + private int getPlayerStat(@NotNull OfflinePlayer player, @NotNull Statistic stat, String subStatEntryName) throws IllegalArgumentException { + + switch (stat.getType()) { + case UNTYPED -> { + return player.getStatistic(stat); + } + case BLOCK -> { + Material block = enumHandler.getBlock(subStatEntryName); + if (block != null) { + return player.getStatistic(stat, block); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid block name!"); + } + } + case ENTITY -> { + EntityType entity = enumHandler.getEntityType(subStatEntryName); + if (entity != null) { + return player.getStatistic(stat, entity); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid entity name!"); + } + } + case ITEM -> { + Material item = enumHandler.getItem(subStatEntryName); + if (item != null) { + return player.getStatistic(stat, item); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid item name!"); + } + } + default -> + throw new IllegalArgumentException("This statistic does not seem to be of type:untyped/block/entity/item, I think we should panic"); + } + } + //returns the statistic enum constant, or null if non-existent (param: statName, not case sensitive) private Statistic getStatisticEnum(String statName) { try { diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java index c9c26c6..e58feee 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java @@ -73,7 +73,15 @@ public class StatCommand implements CommandExecutor { if (topFlag) { try { + time = plugin.logTimeTaken("StatCommand", "onCommand", time, 76); LinkedHashMap topStats = statManager.getTopStatistics(statName, subStatEntry); + + time = plugin.logTimeTaken("StatCommand", "onCommand", time, 79); + + LinkedHashMap topStats2 = statManager.getTopStatistics2(statName, subStatEntry); + time = plugin.logTimeTaken("StatCommand", "onCommand", time, 82); + + sender.sendMessage(outputFormatter.formatTopStats(topStats, statName, subStatEntry)); return true; } catch (Exception e) { diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java index 1eaa3b7..eb5aeb9 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java @@ -3,17 +3,20 @@ package com.gmail.artemis.the.gr8.playerstats.utils; import com.gmail.artemis.the.gr8.playerstats.ConfigHandler; import com.gmail.artemis.the.gr8.playerstats.Main; import org.bukkit.ChatColor; +import org.bukkit.map.MinecraftFont; -import java.util.HashMap; -import java.util.LinkedHashMap; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; public class OutputFormatter { - //keys for the HashMap are: - //playerNames(Ranked) - //statNames(Ranked) - //subStatNames(Ranked) - //numbers(Ranked) + //keys for the HashMap are the same as the config options: + //player-names(-ranked) + //stat-names OR list-title + //sub-stat-names(-ranked) + //stat-numbers(-ranked) + //list-numbers + private final ConfigHandler config; private final Main plugin; private HashMap chatColors; @@ -23,14 +26,14 @@ public class OutputFormatter { public OutputFormatter(ConfigHandler c, Main p) { config = c; plugin = p; - pluginPrefix = ChatColor.GRAY + "[" + ChatColor.GOLD + "PlayerStats" + ChatColor.GRAY + "]" + ChatColor.RESET; + pluginPrefix = ChatColor.GRAY + "[" + ChatColor.GOLD + "PlayerStats" + ChatColor.GRAY + "] " + ChatColor.RESET; updateOutputColors(); className = "OutputFormatter"; } public String formatExceptions(String exception) { - return pluginPrefix + " " + exception; + return pluginPrefix + exception; } public String formatPlayerStat(String playerName, String statName, String subStatEntryName, int stat) { @@ -39,17 +42,48 @@ public class OutputFormatter { time = plugin.logTimeTaken(className, methodName, time, 39); String subStat = subStatEntryName != null ? - chatColors.get("subStatNames") + " (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : ""; + chatColors.get("sub-stat-names") + " (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : ""; time = plugin.logTimeTaken(className, methodName, time, 43); - String msg = chatColors.get("playerNames") + playerName + chatColors.get("numbers") + ": " + stat + " " + - chatColors.get("statNames") + statName.toLowerCase().replace("_", " ") + subStat; - time = plugin.logTimeTaken(className, methodName, time, 47); + String msg = chatColors.get("player-names") + playerName + chatColors.get("stat-numbers") + ": " + stat + " " + + chatColors.get("stat-names") + statName.toLowerCase().replace("_", " ") + subStat; + plugin.logTimeTaken(className, methodName, time, 47); return msg; } - public String formatTopStats(LinkedHashMap topStats) { - return ""; + public String formatTopStats(LinkedHashMap topStats, String statName, String subStatEntryName) { + String subStat = subStatEntryName != null ? + chatColors.get("sub-stat-names-ranked") + " (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : ""; + String topCount = chatColors.get("list-numbers") + " " + topStats.size(); + String title = pluginPrefix + chatColors.get("list-title") + "Top" + topCount + chatColors.get("list-title") + " " + + statName.toLowerCase().replace("_", " ") + subStat; + + int count = 0; + final int[] longestName = {0}; + Set playerNames = topStats.keySet(); + MinecraftFont font = new MinecraftFont(); + playerNames.stream().map(font::getWidth).max(Integer::compareTo).orElseThrow(); + + try { + longestName[0] = playerNames.stream().map(String::length).max(Integer::compareTo).orElseThrow(); + } + catch (NoSuchElementException e) { + longestName[0] = 20; + } + + + StringBuilder rankList = new StringBuilder(); + for (String playerName : playerNames) { + count = count+1; + + String spaces = (longestName[0] - playerName.length() > 0) ? " ".repeat(longestName[0] - playerName.length()) : ""; + rankList.append("\n") + .append(chatColors.get("list-numbers")).append(count).append(". ") + .append(chatColors.get("player-names-ranked")).append(playerName).append(": ") + .append(spaces) + .append(chatColors.get("stat-numbers-ranked")).append(topStats.get(playerName).toString()); + } + return title + rankList; } public void updateOutputColors() { diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index e1a083c..ef63dbd 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -6,10 +6,12 @@ individual-statistics: player-names: gold stat-names: yellow sub-stat-names: yellow - numbers: white + stat-numbers: white ranked-list: player-names: gold - stat-names: yellow + list-title: white sub-stat-names: yellow - numbers: white \ No newline at end of file + stat-numbers: white + list-numbers: white + From 3db21f5970c8958bfa371a541b6557721d432392 Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Fri, 13 May 2022 13:10:32 +0200 Subject: [PATCH 4/8] Further experimenting with indentation --- pom.xml | 6 +++ .../the/gr8/playerstats/ConfigHandler.java | 1 + .../gr8/playerstats/commands/StatCommand.java | 1 + .../playerstats/utils/OutputFormatter.java | 39 +++++++++++++------ src/main/resources/config.yml | 7 ++-- 5 files changed, 39 insertions(+), 15 deletions(-) diff --git a/pom.xml b/pom.xml index 3007989..48e1b7e 100644 --- a/pom.xml +++ b/pom.xml @@ -29,6 +29,12 @@ 23.0.0 compile + + + org.apache.commons + commons-lang3 + 3.12.0 + diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java index 81dfa8c..46c3fb6 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java @@ -35,6 +35,7 @@ public class ConfigHandler { chatColors.put("sub-stat-names-ranked", getChatColor(ranked, "sub-stat-names")); chatColors.put("stat-numbers-ranked", getChatColor(ranked, "stat-numbers")); chatColors.put("list-numbers", getChatColor(ranked, "list-numbers")); + chatColors.put("underscores", getChatColor(ranked, "underscores")); return chatColors; } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java index e58feee..cf4f9e9 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java @@ -86,6 +86,7 @@ public class StatCommand implements CommandExecutor { } catch (Exception e) { sender.sendMessage(outputFormatter.formatExceptions(e.toString())); + e.printStackTrace(); } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java index eb5aeb9..5f42ae6 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java @@ -6,7 +6,8 @@ import org.bukkit.ChatColor; import org.bukkit.map.MinecraftFont; import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; +import org.apache.commons.lang3.StringUtils; + public class OutputFormatter { @@ -59,28 +60,42 @@ public class OutputFormatter { statName.toLowerCase().replace("_", " ") + subStat; int count = 0; - final int[] longestName = {0}; + Set playerNames = topStats.keySet(); MinecraftFont font = new MinecraftFont(); - playerNames.stream().map(font::getWidth).max(Integer::compareTo).orElseThrow(); - - try { - longestName[0] = playerNames.stream().map(String::length).max(Integer::compareTo).orElseThrow(); + int max = 130; + boolean useWidth = true; + /*try { + //https://stackoverflow.com/questions/43034015/how-do-i-properly-align-using-string-format-in-java + max = playerNames.stream().map(font::getWidth).max(Integer::compareTo).orElseThrow(); } catch (NoSuchElementException e) { - longestName[0] = 20; + useWidth = false; } - - + */ + String hairSpace = "\u200A"; StringBuilder rankList = new StringBuilder(); for (String playerName : playerNames) { count = count+1; - String spaces = (longestName[0] - playerName.length() > 0) ? " ".repeat(longestName[0] - playerName.length()) : ""; rankList.append("\n") .append(chatColors.get("list-numbers")).append(count).append(". ") - .append(chatColors.get("player-names-ranked")).append(playerName).append(": ") - .append(spaces) + .append(chatColors.get("player-names-ranked")).append(playerName).append(" ") + .append(chatColors.get("underscores")); + StringBuilder underscores = new StringBuilder(); + + int i = 0; + while (font.getWidth(count + ". " + playerName + " " + underscores) < 124) { + underscores.append("_"); + i++; + } + + int extraSpaces = 129 - font.getWidth(count + ". " + playerName + " " + underscores); + hairSpace = hairSpace.repeat(extraSpaces); + + plugin.getLogger().info("while loop executed [" + i + "] times"); + rankList.append(underscores) + .append(hairSpace) .append(chatColors.get("stat-numbers-ranked")).append(topStats.get(playerName).toString()); } return title + rankList; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index ef63dbd..bec9ad6 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -9,9 +9,10 @@ individual-statistics: stat-numbers: white ranked-list: - player-names: gold - list-title: white + player-names: green + list-title: yellow sub-stat-names: yellow stat-numbers: white - list-numbers: white + list-numbers: gold + underscores: gray From a9dca1db836e8d12430a521f661ea1e904fcdd49 Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Sun, 15 May 2022 17:33:31 +0200 Subject: [PATCH 5/8] Done with top stats formatting (#22, #9, #1) --- pom.xml | 8 +-- .../the/gr8/playerstats/ConfigHandler.java | 44 +++++++----- .../artemis/the/gr8/playerstats/Main.java | 3 +- .../gr8/playerstats/commands/StatCommand.java | 15 +++- .../playerstats/utils/OutputFormatter.java | 68 +++++-------------- src/main/resources/config.yml | 9 ++- 6 files changed, 68 insertions(+), 79 deletions(-) diff --git a/pom.xml b/pom.xml index 48e1b7e..d71520b 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ org.spigotmc spigot-api - 1.18-R0.1-SNAPSHOT + 1.18.2-R0.1-SNAPSHOT provided @@ -29,12 +29,6 @@ 23.0.0 compile - - - org.apache.commons - commons-lang3 - 3.12.0 - diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java index 46c3fb6..1f6b76d 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java @@ -19,6 +19,18 @@ public class ConfigHandler { saveDefaultConfig(); } + //returns the config setting for use-dots, or the default value "true" if no value can be retrieved + public boolean getUseDots() { + ConfigurationSection ranked = config.getConfigurationSection("ranked-list"); + try { + return ranked == null || ranked.getBoolean("use-dots"); + } + catch (Exception e) { + e.printStackTrace(); + return true; + } + } + //returns a HashMap with all the available color choices, or a ChatColor.RESET if no colors were found public HashMap getChatColors() { HashMap chatColors = new HashMap<>(); @@ -35,10 +47,25 @@ public class ConfigHandler { chatColors.put("sub-stat-names-ranked", getChatColor(ranked, "sub-stat-names")); chatColors.put("stat-numbers-ranked", getChatColor(ranked, "stat-numbers")); chatColors.put("list-numbers", getChatColor(ranked, "list-numbers")); - chatColors.put("underscores", getChatColor(ranked, "underscores")); + chatColors.put("dots", getChatColor(ranked, "dots")); return chatColors; } + //reload the config after changes have been made to it + public boolean reloadConfig() { + try { + if (!configFile.exists()) { + saveDefaultConfig(); + } + config = YamlConfiguration.loadConfiguration(configFile); + return true; + } + catch (Exception e) { + e.printStackTrace(); + return false; + } + } + //returns the requested entry from the provided configuration section, null if section does not exist, and ChatColor.RESET if there is no entry private ChatColor getChatColor(ConfigurationSection section, String path) { ChatColor color; @@ -58,21 +85,6 @@ public class ConfigHandler { return color; } - //reload the config after changes have been made to it - public boolean reloadConfig() { - try { - if (!configFile.exists()) { - saveDefaultConfig(); - } - config = YamlConfiguration.loadConfiguration(configFile); - return true; - } - catch (Exception e) { - e.printStackTrace(); - return false; - } - } - //create a config file if none exists yet (from the config.yml in the plugin's resources) private void saveDefaultConfig() { config = plugin.getConfig(); diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java index 8191a71..169b3eb 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java @@ -13,11 +13,10 @@ public class Main extends JavaPlugin { @Override public void onEnable() { - ConfigHandler config = new ConfigHandler(this); EnumHandler enumHandler = new EnumHandler(); - OutputFormatter outputFormatter = new OutputFormatter(config, this); + OutputFormatter outputFormatter = new OutputFormatter(config); StatManager statManager = new StatManager(enumHandler, this); this.getCommand("statistic").setExecutor(new StatCommand(outputFormatter, statManager, this)); diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java index cf4f9e9..2943a00 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java @@ -4,6 +4,11 @@ import com.gmail.artemis.the.gr8.playerstats.Main; import com.gmail.artemis.the.gr8.playerstats.StatManager; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OutputFormatter; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -81,7 +86,11 @@ public class StatCommand implements CommandExecutor { LinkedHashMap topStats2 = statManager.getTopStatistics2(statName, subStatEntry); time = plugin.logTimeTaken("StatCommand", "onCommand", time, 82); - sender.sendMessage(outputFormatter.formatTopStats(topStats, statName, subStatEntry)); + String top = outputFormatter.formatTopStats(topStats, statName, subStatEntry); + String top2 = outputFormatter.formatTopStats(topStats2, statName, subStatEntry); + sender.sendMessage(top); + sender.sendMessage(top2); + return true; } catch (Exception e) { @@ -93,6 +102,10 @@ public class StatCommand implements CommandExecutor { else if (playerName != null) { try { + BaseComponent[] component = new ComponentBuilder("hi?").color(ChatColor.of("#4a32a8")).create(); + sender.spigot().sendMessage(component); + String msg = ChatColor.of("#f27d07") + "... hi"; + sender.sendMessage(msg); sender.sendMessage(outputFormatter.formatPlayerStat(playerName, statName, subStatEntry, statManager.getStatistic (statName, subStatEntry, playerName))); } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java index 5f42ae6..c35fcaf 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java @@ -1,36 +1,23 @@ package com.gmail.artemis.the.gr8.playerstats.utils; import com.gmail.artemis.the.gr8.playerstats.ConfigHandler; -import com.gmail.artemis.the.gr8.playerstats.Main; import org.bukkit.ChatColor; import org.bukkit.map.MinecraftFont; import java.util.*; -import org.apache.commons.lang3.StringUtils; - public class OutputFormatter { - //keys for the HashMap are the same as the config options: - //player-names(-ranked) - //stat-names OR list-title - //sub-stat-names(-ranked) - //stat-numbers(-ranked) - //list-numbers + //keys for the HashMap are the same as the config options (so e.g. player-names/player-names-ranked) private final ConfigHandler config; - private final Main plugin; private HashMap chatColors; - private String pluginPrefix; - private String className; + private final String pluginPrefix; - public OutputFormatter(ConfigHandler c, Main p) { + public OutputFormatter(ConfigHandler c) { config = c; - plugin = p; pluginPrefix = ChatColor.GRAY + "[" + ChatColor.GOLD + "PlayerStats" + ChatColor.GRAY + "] " + ChatColor.RESET; - updateOutputColors(); - className = "OutputFormatter"; } public String formatExceptions(String exception) { @@ -38,42 +25,25 @@ public class OutputFormatter { } public String formatPlayerStat(String playerName, String statName, String subStatEntryName, int stat) { - String methodName = "formatPlayerStats"; - long time = System.currentTimeMillis(); - time = plugin.logTimeTaken(className, methodName, time, 39); - String subStat = subStatEntryName != null ? chatColors.get("sub-stat-names") + " (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : ""; - time = plugin.logTimeTaken(className, methodName, time, 43); - String msg = chatColors.get("player-names") + playerName + chatColors.get("stat-numbers") + ": " + stat + " " + + return chatColors.get("player-names") + playerName + chatColors.get("stat-numbers") + ": " + stat + " " + chatColors.get("stat-names") + statName.toLowerCase().replace("_", " ") + subStat; - plugin.logTimeTaken(className, methodName, time, 47); - return msg; } public String formatTopStats(LinkedHashMap topStats, String statName, String subStatEntryName) { String subStat = subStatEntryName != null ? chatColors.get("sub-stat-names-ranked") + " (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : ""; String topCount = chatColors.get("list-numbers") + " " + topStats.size(); - String title = pluginPrefix + chatColors.get("list-title") + "Top" + topCount + chatColors.get("list-title") + " " + + String title = "\n" + pluginPrefix + chatColors.get("list-title") + "Top" + topCount + chatColors.get("list-title") + " " + statName.toLowerCase().replace("_", " ") + subStat; + boolean useDots = config.getUseDots(); int count = 0; - Set playerNames = topStats.keySet(); MinecraftFont font = new MinecraftFont(); - int max = 130; - boolean useWidth = true; - /*try { - //https://stackoverflow.com/questions/43034015/how-do-i-properly-align-using-string-format-in-java - max = playerNames.stream().map(font::getWidth).max(Integer::compareTo).orElseThrow(); - } - catch (NoSuchElementException e) { - useWidth = false; - } - */ - String hairSpace = "\u200A"; + StringBuilder rankList = new StringBuilder(); for (String playerName : playerNames) { count = count+1; @@ -81,22 +51,20 @@ public class OutputFormatter { rankList.append("\n") .append(chatColors.get("list-numbers")).append(count).append(". ") .append(chatColors.get("player-names-ranked")).append(playerName).append(" ") - .append(chatColors.get("underscores")); - StringBuilder underscores = new StringBuilder(); + .append(chatColors.get("dots")); - int i = 0; - while (font.getWidth(count + ". " + playerName + " " + underscores) < 124) { - underscores.append("_"); - i++; + if (useDots) { + rankList.append(" "); + int dots = (125 - font.getWidth(count + ". " + playerName + " ")); + if (dots >= 1) { + rankList.append(".".repeat(dots)); + } + } + else { + rankList.append(":"); } - int extraSpaces = 129 - font.getWidth(count + ". " + playerName + " " + underscores); - hairSpace = hairSpace.repeat(extraSpaces); - - plugin.getLogger().info("while loop executed [" + i + "] times"); - rankList.append(underscores) - .append(hairSpace) - .append(chatColors.get("stat-numbers-ranked")).append(topStats.get(playerName).toString()); + rankList.append(" ").append(chatColors.get("stat-numbers-ranked")).append(topStats.get(playerName).toString()); } return title + rankList; } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index bec9ad6..942b7dc 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,7 @@ # PlayerStats Configuration -# --- Color Options --- -# supports: all default Minecraft colors +# --- Format Options --- + individual-statistics: player-names: gold stat-names: yellow @@ -14,5 +14,8 @@ ranked-list: sub-stat-names: yellow stat-numbers: white list-numbers: gold - underscores: gray + +# If true, the statistics will be aligned so that they are all underneath each other + use-dots: true + dots: gray From 42bdca42dfc5dede16a47ec9ea5088484c0c4f3d Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Mon, 16 May 2022 17:21:13 +0200 Subject: [PATCH 6/8] Github testing --- src/main/resources/config.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 942b7dc..7103e89 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,7 +1,9 @@ # PlayerStats Configuration -# --- Format Options --- +# --- General Options --- + +# --- Format & Color Options --- individual-statistics: player-names: gold stat-names: yellow From 2709232e6721a913d1cc7e95d7ba8b76998d07c2 Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Tue, 17 May 2022 23:19:38 +0200 Subject: [PATCH 7/8] Started using multi-threading to improve performance (#24) --- .../artemis/the/gr8/playerstats/Main.java | 14 +- .../the/gr8/playerstats/StatManager.java | 250 ------------------ .../the/gr8/playerstats/StatRequest.java | 67 +++++ .../the/gr8/playerstats/StatThread.java | 193 ++++++++++++++ .../playerstats/commands/ReloadCommand.java | 8 +- .../gr8/playerstats/commands/StatCommand.java | 117 ++++---- .../playerstats/commands/TabCompleter.java | 20 +- .../{ => filehandlers}/ConfigHandler.java | 3 +- .../playerstats/listeners/JoinListener.java | 5 +- .../gr8/playerstats/utils/EnumHandler.java | 115 +++++++- .../utils/OfflinePlayerHandler.java | 26 +- .../playerstats/utils/OutputFormatter.java | 2 +- 12 files changed, 448 insertions(+), 372 deletions(-) delete mode 100644 src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java create mode 100644 src/main/java/com/gmail/artemis/the/gr8/playerstats/StatRequest.java create mode 100644 src/main/java/com/gmail/artemis/the/gr8/playerstats/StatThread.java rename src/main/java/com/gmail/artemis/the/gr8/playerstats/{ => filehandlers}/ConfigHandler.java (96%) diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java index 169b3eb..e1e95a0 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java @@ -3,8 +3,10 @@ package com.gmail.artemis.the.gr8.playerstats; import com.gmail.artemis.the.gr8.playerstats.commands.ReloadCommand; import com.gmail.artemis.the.gr8.playerstats.commands.StatCommand; import com.gmail.artemis.the.gr8.playerstats.commands.TabCompleter; +import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler; import com.gmail.artemis.the.gr8.playerstats.listeners.JoinListener; import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; +import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OutputFormatter; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; @@ -14,14 +16,14 @@ public class Main extends JavaPlugin { @Override public void onEnable() { ConfigHandler config = new ConfigHandler(this); - EnumHandler enumHandler = new EnumHandler(); - + EnumHandler enumHandler = new EnumHandler(this); OutputFormatter outputFormatter = new OutputFormatter(config); - StatManager statManager = new StatManager(enumHandler, this); - this.getCommand("statistic").setExecutor(new StatCommand(outputFormatter, statManager, this)); - this.getCommand("statistic").setTabCompleter(new TabCompleter( - enumHandler, statManager,this)); + //prepare private hashMap of offline players + OfflinePlayerHandler.updateOfflinePlayers(); + + this.getCommand("statistic").setExecutor(new StatCommand(outputFormatter, enumHandler, this)); + this.getCommand("statistic").setTabCompleter(new TabCompleter(enumHandler, this)); this.getCommand("statisticreload").setExecutor(new ReloadCommand(config, outputFormatter, this)); Bukkit.getPluginManager().registerEvents(new JoinListener(), this); diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java deleted file mode 100644 index b3e79c2..0000000 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatManager.java +++ /dev/null @@ -1,250 +0,0 @@ -package com.gmail.artemis.the.gr8.playerstats; - -import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; -import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; -import org.bukkit.Material; -import org.bukkit.OfflinePlayer; -import org.bukkit.Statistic; -import org.bukkit.entity.EntityType; -import org.jetbrains.annotations.NotNull; - -import java.util.*; -import java.util.stream.Collectors; - -public class StatManager { - - private final Main plugin; - private final EnumHandler enumHandler; - private final OfflinePlayerHandler offlinePlayerHandler; - private final List statNames; - private final List entityStatNames; - private final List subStatEntryNames; - - private final String className; - - public StatManager(EnumHandler e, Main p) { - plugin = p; - enumHandler = e; - offlinePlayerHandler = OfflinePlayerHandler.getInstance(); - - statNames = Arrays.stream(Statistic.values()).map( - Statistic::toString).map(String::toLowerCase).toList(); - entityStatNames = Arrays.stream(Statistic.values()).filter(statistic -> - statistic.getType().equals(Statistic.Type.ENTITY)).map( - Statistic::toString).map(String::toLowerCase).collect(Collectors.toList()); - - subStatEntryNames = new ArrayList<>(); - subStatEntryNames.addAll(enumHandler.getBlockNames()); - subStatEntryNames.addAll(enumHandler.getEntityTypeNames()); - subStatEntryNames.addAll(enumHandler.getItemNames()); - - className = "StatManger"; - } - - //returns the integer associated with a certain statistic for a player - public int getStatistic(String statName, String subStatEntryName, String playerName) throws IllegalArgumentException, NullPointerException { - OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); - if (player != null) { - Statistic stat = getStatisticEnum(statName); - if (stat != null) { - return getPlayerStat(player, stat, subStatEntryName); - } - throw new IllegalArgumentException("Statistic " + statName + " could not be retrieved!"); - } - throw new IllegalArgumentException("Player object for " + playerName + " could not be retrieved!"); - } - - public LinkedHashMap getTopStatistics(String statName, String subStatEntry) throws IllegalArgumentException, NullPointerException { - String methodName = "getTopStatistic"; - long time = System.currentTimeMillis(); - - Statistic stat = getStatisticEnum(statName); - time = plugin.logTimeTaken(className, methodName, time, 106); - - if (stat != null) { - if (stat.getType().equals(Statistic.Type.UNTYPED) || isMatchingSubStatEntry(stat, subStatEntry)) { - HashMap playerStats = new HashMap<>((int) (offlinePlayerHandler.getOfflinePlayerCount() * 1.05)); - time = plugin.logTimeTaken(className, methodName, time, 111); - - for (String playerName : offlinePlayerHandler.getAllOfflinePlayerNames()) { - OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); - if (player != null) { - try { - int statistic = getPlayerStat(player, stat, subStatEntry); - if (statistic > 0) { - playerStats.put(playerName, getPlayerStat(player, stat, subStatEntry)); - } - } - catch (IllegalArgumentException ignored) { - } - } - } - time = plugin.logTimeTaken(className, methodName, time, 123); - - LinkedHashMap topStats = playerStats.entrySet().stream() - .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) - .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - time = plugin.logTimeTaken(className, methodName, time, 128); - - plugin.getLogger().info("Top 10: " + topStats); - plugin.logTimeTaken(className, methodName, time, 131); - return topStats; - } - throw new IllegalArgumentException(subStatEntry + " is not a valid substatistic entry for this statistic!"); - } - throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); - } - - public LinkedHashMap getTopStatistics2(String statName, String subStatEntry) { - String methodName = "getTopStatistics2"; - long time = System.currentTimeMillis(); - - Statistic stat = getStatisticEnum(statName); - time = plugin.logTimeTaken(className, methodName, time, 144); - - if (stat != null) { - if (stat.getType().equals(Statistic.Type.UNTYPED) || isMatchingSubStatEntry(stat, subStatEntry)) { - HashMap playerStats = new HashMap<>((int) (offlinePlayerHandler.getOfflinePlayerCount() * 1.05)); - time = plugin.logTimeTaken(className, methodName, time, 149); - - offlinePlayerHandler.getAllOfflinePlayerNames().forEach(playerName -> { - OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); - if (player != null) - try { - playerStats.put(playerName, getPlayerStat(player, stat, subStatEntry)); - } - catch (IllegalArgumentException ignored) { - - } - }); - - time = plugin.logTimeTaken(className, methodName, time, 162); - - LinkedHashMap topStats = playerStats.entrySet().stream() - .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) - .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - time = plugin.logTimeTaken(className, methodName, time, 167); - - plugin.getLogger().info("Top 10: " + topStats); - plugin.logTimeTaken(className, methodName, time, 170); - return topStats; - } - throw new IllegalArgumentException(subStatEntry + " is not a valid substatistic entry for this statistic!"); - } - throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); - } - - //gets the type of the statistic from the string, otherwise returns null (param: statName, not case sensitive) - public Statistic.Type getStatType(String statName) { - try { - return Statistic.valueOf(statName.toUpperCase()).getType(); - } - catch (IllegalArgumentException e) { - plugin.getLogger().warning("IllegalArgumentException: " + statName + " is not a valid statistic name!"); - return null; - } - catch (NullPointerException e) { - plugin.getLogger().warning("NullPointerException: please provide a statistic name!"); - return null; - } - } - - //returns the names of all general statistics in lowercase - public List getStatNames() { - return statNames; - } - - //returns all statistics that have type entities, in lowercase - public List getEntityStatNames() { - return entityStatNames; - } - - //checks if string is a valid statistic (param: statName, not case sensitive) - public boolean isStatistic(String statName) { - return statNames.contains(statName.toLowerCase()); - } - - //checks if this statistic is a subStatEntry, meaning it is a block, item or entity (param: statName, not case sensitive) - public boolean isSubStatEntry(String statName) { - return subStatEntryNames.contains(statName.toLowerCase()); - } - - //checks whether a subStatEntry is of the type that the statistic requires - public boolean isMatchingSubStatEntry(String statName, String subStatEntry) { - Statistic stat = getStatisticEnum(statName); - return (stat != null && isMatchingSubStatEntry(stat, subStatEntry)); - } - - private boolean isMatchingSubStatEntry(@NotNull Statistic stat, String subStatEntry) { - switch (stat.getType()) { - case ENTITY -> { - return subStatEntry != null && enumHandler.isEntityType(subStatEntry); - } - case ITEM -> { - return subStatEntry != null && enumHandler.isItem(subStatEntry); - } - case BLOCK -> { - return subStatEntry != null && enumHandler.isBlock(subStatEntry); - } - case UNTYPED -> { - return subStatEntry==null; - } - default -> { - return false; - } - } - } - - private int getPlayerStat(@NotNull OfflinePlayer player, @NotNull Statistic stat, String subStatEntryName) throws IllegalArgumentException { - - switch (stat.getType()) { - case UNTYPED -> { - return player.getStatistic(stat); - } - case BLOCK -> { - Material block = enumHandler.getBlock(subStatEntryName); - if (block != null) { - return player.getStatistic(stat, block); - } - else { - throw new IllegalArgumentException(subStatEntryName + " is not a valid block name!"); - } - } - case ENTITY -> { - EntityType entity = enumHandler.getEntityType(subStatEntryName); - if (entity != null) { - return player.getStatistic(stat, entity); - } - else { - throw new IllegalArgumentException(subStatEntryName + " is not a valid entity name!"); - } - } - case ITEM -> { - Material item = enumHandler.getItem(subStatEntryName); - if (item != null) { - return player.getStatistic(stat, item); - } - else { - throw new IllegalArgumentException(subStatEntryName + " is not a valid item name!"); - } - } - default -> - throw new IllegalArgumentException("This statistic does not seem to be of type:untyped/block/entity/item, I think we should panic"); - } - } - - //returns the statistic enum constant, or null if non-existent (param: statName, not case sensitive) - private Statistic getStatisticEnum(String statName) { - try { - return Statistic.valueOf(statName.toUpperCase()); - } - catch (IllegalArgumentException e) { - plugin.getLogger().warning("IllegalArgumentException: " + statName + " is not a valid statistic name!"); - return null; - } - catch (NullPointerException e) { - plugin.getLogger().warning("NullPointerException: please provide a statistic name!"); - return null; - } - } -} diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatRequest.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatRequest.java new file mode 100644 index 0000000..64731da --- /dev/null +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatRequest.java @@ -0,0 +1,67 @@ +package com.gmail.artemis.the.gr8.playerstats; + +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +public class StatRequest { + + private final CommandSender sender; + private String statName; + private String subStatEntry; + private String playerName; + private boolean playerFlag; + private boolean topFlag; + + //playerFlag and topFlag are false by default, will be set to true if "player" or "top" is in the args + public StatRequest(@NotNull CommandSender s) { + sender = s; + playerFlag = false; + topFlag = false; + } + + public CommandSender getCommandSender() { + return sender; + } + + public String getStatName() { + return statName; + } + + public void setStatName(String statName) { + this.statName = statName; + } + + public String getSubStatEntry() { + return subStatEntry; + } + + public void setSubStatEntry(String subStatEntry) { + this.subStatEntry = subStatEntry; + } + + public String getPlayerName() { + return playerName; + } + + public void setPlayerName(String playerName) { + this.playerName = playerName; + } + + //the "player" arg in the statCommand is a special case, because it could either be a valid subStatEntry, or indicate that the lookup action should target a specific player + //this is why the playerFlag exists - if this is true, and playerName is null, subStatEntry will be "player" + public boolean playerFlag() { + return playerFlag; + } + + public void setPlayerFlag(boolean playerFlag) { + this.playerFlag = playerFlag; + } + + public boolean topFlag() { + return topFlag; + } + + public void setTopFlag(boolean topFlag) { + this.topFlag = topFlag; + } +} diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatThread.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatThread.java new file mode 100644 index 0000000..a61f57f --- /dev/null +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatThread.java @@ -0,0 +1,193 @@ +package com.gmail.artemis.the.gr8.playerstats; + +import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; +import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; +import com.gmail.artemis.the.gr8.playerstats.utils.OutputFormatter; +import org.bukkit.Material; +import org.bukkit.OfflinePlayer; +import org.bukkit.Statistic; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public class StatThread extends Thread { + + private final StatRequest request; + private final EnumHandler enumHandler; + private final OutputFormatter outputFormatter; + private final Main plugin; + private String className = "StatThread"; + + //constructor (called on thread creation) + public StatThread(StatRequest s, EnumHandler e, OutputFormatter o, Main p) { + request = s; + + enumHandler = e; + outputFormatter = o; + plugin = p; + } + + //what the thread will do once started + @Override + public void run() throws IllegalStateException, NullPointerException { + long time = System.currentTimeMillis(); + + if (outputFormatter == null || plugin == null) { + throw new IllegalStateException("Not all classes off the plugin are running!"); + } + if (request == null) { + throw new NullPointerException("No statistic request was found!"); + } + + CommandSender sender = request.getCommandSender(); + String playerName = request.getPlayerName(); + String statName = request.getStatName(); + String subStatEntry = request.getSubStatEntry(); + boolean topFlag = request.topFlag(); + + if (playerName != null) { + try { + sender.sendMessage( + outputFormatter.formatPlayerStat( + playerName, statName, subStatEntry, getStatistic( + statName, subStatEntry, playerName))); + } catch (Exception e) { + sender.sendMessage(outputFormatter.formatExceptions(e.toString())); + } + + } else if (topFlag) { + try { + LinkedHashMap topStats = getTopStatisticsForLoop(statName, subStatEntry); + time = plugin.logTimeTaken(className, "run(): for loop", time, 67); + + LinkedHashMap topStats2 = getTopStatisticsForEach(statName, subStatEntry); + time = plugin.logTimeTaken(className, "run(): for each loop", time, 70); + + String top2 = outputFormatter.formatTopStats(topStats2, statName, subStatEntry); + sender.sendMessage(top2); + plugin.logTimeTaken(className, "run(): format output", time, 74); + + } catch (Exception e) { + sender.sendMessage(outputFormatter.formatExceptions(e.toString())); + e.printStackTrace(); + } + } + + } + + //returns the integer associated with a certain statistic for a player + private int getStatistic(String statName, String subStatEntryName, String playerName) throws IllegalArgumentException, NullPointerException { + OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName); + if (player != null) { + Statistic stat = enumHandler.getStatEnum(statName); + if (stat != null) { + return getPlayerStat(player, stat, subStatEntryName); + } + throw new IllegalArgumentException("Statistic " + statName + " could not be retrieved!"); + } + throw new IllegalArgumentException("Player object for " + playerName + " could not be retrieved!"); + } + + private LinkedHashMap getTopStatisticsForLoop(String statName, String subStatEntry) throws NullPointerException { + long time = System.currentTimeMillis(); + + Statistic stat = enumHandler.getStatEnum(statName); + + if (stat != null) { + HashMap playerStats = new HashMap<>((int) (OfflinePlayerHandler.getOfflinePlayerCount() * 1.05)); + for (String playerName : OfflinePlayerHandler.getAllOfflinePlayerNames()) { + OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName); + if (player != null) { + try { + int statistic = getPlayerStat(player, stat, subStatEntry); + if (statistic > 0) { + playerStats.put(playerName, statistic); + } + } catch (IllegalArgumentException ignored) { + } + } + } + time = plugin.logTimeTaken(className, "for loop", time, 116); + + LinkedHashMap topStats = playerStats.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + + plugin.logTimeTaken(className, "for loop, sorting", time, 122); + return topStats; + + } + throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); + } + + private LinkedHashMap getTopStatisticsForEach(String statName, String subStatEntry) { + long time = System.currentTimeMillis(); + + Statistic stat = enumHandler.getStatEnum(statName); + + if (stat != null) { + HashMap playerStats = new HashMap<>((int) (OfflinePlayerHandler.getOfflinePlayerCount() * 1.05)); + OfflinePlayerHandler.getAllOfflinePlayerNames().forEach(playerName -> { + OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName); + if (player != null) + try { + playerStats.put(playerName, getPlayerStat(player, stat, subStatEntry)); + } catch (IllegalArgumentException ignored) { + } + }); + + time = plugin.logTimeTaken(className, "for each loop", time, 145); + + LinkedHashMap topStats = playerStats.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + + plugin.logTimeTaken(className, "for each loop, sorting", time, 151); + return topStats; + } + throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); + } + + private int getPlayerStat(@NotNull OfflinePlayer player, @NotNull Statistic stat, String subStatEntryName) throws IllegalArgumentException { + switch (stat.getType()) { + case UNTYPED -> { + return player.getStatistic(stat); + } + case BLOCK -> { + Material block = enumHandler.getBlock(subStatEntryName); + if (block != null) { + return player.getStatistic(stat, block); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid block name!"); + } + } + case ENTITY -> { + EntityType entity = enumHandler.getEntityType(subStatEntryName); + if (entity != null) { + return player.getStatistic(stat, entity); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid entity name!"); + } + } + case ITEM -> { + Material item = enumHandler.getItem(subStatEntryName); + if (item != null) { + return player.getStatistic(stat, item); + } + else { + throw new IllegalArgumentException(subStatEntryName + " is not a valid item name!"); + } + } + default -> + throw new IllegalArgumentException("This statistic does not seem to be of type:untyped/block/entity/item, I think we should panic"); + } + } +} diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java index 2533c42..96d2400 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/ReloadCommand.java @@ -1,6 +1,6 @@ package com.gmail.artemis.the.gr8.playerstats.commands; -import com.gmail.artemis.the.gr8.playerstats.ConfigHandler; +import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler; import com.gmail.artemis.the.gr8.playerstats.Main; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OutputFormatter; @@ -13,13 +13,11 @@ import org.jetbrains.annotations.NotNull; public class ReloadCommand implements CommandExecutor { private final ConfigHandler config; - private final OfflinePlayerHandler offlinePlayerHandler; private final OutputFormatter outputFormatter; private final Main plugin; public ReloadCommand(ConfigHandler c, OutputFormatter o, Main p) { outputFormatter = o; - offlinePlayerHandler = OfflinePlayerHandler.getInstance(); config = c; plugin = p; } @@ -32,11 +30,11 @@ public class ReloadCommand implements CommandExecutor { outputFormatter.updateOutputColors(); time = plugin.logTimeTaken("ReloadCommand", "onCommand", time, 33); - offlinePlayerHandler.updateOfflinePlayers(); + OfflinePlayerHandler.updateOfflinePlayers(); time = plugin.logTimeTaken("ReloadCommand", "onCommand", time, 36); sender.sendMessage(ChatColor.GREEN + "Config reloaded!"); - time = plugin.logTimeTaken("ReloadCommand", "onCommand", time, 39); + plugin.logTimeTaken("ReloadCommand", "onCommand", time, 39); return true; } return false; diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java index 2943a00..7eb7f29 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java @@ -1,36 +1,28 @@ package com.gmail.artemis.the.gr8.playerstats.commands; import com.gmail.artemis.the.gr8.playerstats.Main; -import com.gmail.artemis.the.gr8.playerstats.StatManager; +import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; +import com.gmail.artemis.the.gr8.playerstats.StatRequest; +import com.gmail.artemis.the.gr8.playerstats.StatThread; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OutputFormatter; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.TextComponent; -import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.util.LinkedHashMap; - public class StatCommand implements CommandExecutor { - private final OfflinePlayerHandler offlinePlayerHandler; private final OutputFormatter outputFormatter; - private final StatManager statManager; + private final EnumHandler enumHandler; private final Main plugin; - public StatCommand(OutputFormatter o, StatManager s, Main p) { + public StatCommand(OutputFormatter o, EnumHandler e, Main p) { outputFormatter = o; - statManager = s; + enumHandler = e; plugin = p; - - offlinePlayerHandler = OfflinePlayerHandler.getInstance(); } @Override @@ -39,84 +31,65 @@ public class StatCommand implements CommandExecutor { //part 1: collecting all relevant information from the args if (args.length >= 2) { - String statName = null; - String subStatEntry = null; - String playerName = null; - boolean playerFlag = false; - boolean topFlag = false; + StatRequest request = new StatRequest(sender); for (String arg : args) { - if (statManager.isStatistic(arg)) { - statName = (statName == null) ? arg : statName; + if (enumHandler.isStatistic(arg) && request.getStatName() == null) { + request.setStatName(arg); } - else if (statManager.isSubStatEntry(arg)) { + else if (enumHandler.isSubStatEntry(arg)) { if (arg.equalsIgnoreCase("player")) { - if (!playerFlag) { - subStatEntry = (subStatEntry == null) ? arg : subStatEntry; - playerFlag = true; + if (request.playerFlag()) { + if (request.getSubStatEntry() == null) request.setSubStatEntry(arg); + } + else { + request.setPlayerFlag(true); } } + else { - subStatEntry = (subStatEntry == null || playerFlag) ? arg : subStatEntry; + if (request.getSubStatEntry() == null) request.setSubStatEntry(arg); } } else if (arg.equalsIgnoreCase("top")) { - topFlag = true; + request.setTopFlag(true); } else if (arg.equalsIgnoreCase("me") && sender instanceof Player) { - playerName = sender.getName(); + request.setPlayerName(sender.getName()); } - else if (offlinePlayerHandler.isOfflinePlayerName(arg)) { - playerName = (playerName == null) ? arg : playerName; + else if (OfflinePlayerHandler.isOfflinePlayerName(arg) && request.getPlayerName() == null) { + request.setPlayerName(arg); } } - //part 2: sending the information to the StatManager - if (statName != null) { - subStatEntry = statManager.isMatchingSubStatEntry(statName, subStatEntry) ? subStatEntry : null; + //part 2: sending the information to the StatThread + if (isValidStatRequest(request)) { + StatThread statThread = new StatThread(request, enumHandler, outputFormatter, plugin); + statThread.start(); - if (topFlag) { - try { - time = plugin.logTimeTaken("StatCommand", "onCommand", time, 76); - LinkedHashMap topStats = statManager.getTopStatistics(statName, subStatEntry); - - time = plugin.logTimeTaken("StatCommand", "onCommand", time, 79); - - LinkedHashMap topStats2 = statManager.getTopStatistics2(statName, subStatEntry); - time = plugin.logTimeTaken("StatCommand", "onCommand", time, 82); - - String top = outputFormatter.formatTopStats(topStats, statName, subStatEntry); - String top2 = outputFormatter.formatTopStats(topStats2, statName, subStatEntry); - sender.sendMessage(top); - sender.sendMessage(top2); - - return true; - } - catch (Exception e) { - sender.sendMessage(outputFormatter.formatExceptions(e.toString())); - e.printStackTrace(); - } - - } - - else if (playerName != null) { - try { - BaseComponent[] component = new ComponentBuilder("hi?").color(ChatColor.of("#4a32a8")).create(); - sender.spigot().sendMessage(component); - String msg = ChatColor.of("#f27d07") + "... hi"; - sender.sendMessage(msg); - sender.sendMessage(outputFormatter.formatPlayerStat(playerName, statName, subStatEntry, statManager.getStatistic - (statName, subStatEntry, playerName))); - } - catch (Exception e) { - sender.sendMessage(outputFormatter.formatExceptions(e.toString())); - } - } + plugin.logTimeTaken("StatCommand", "onCommand", time, 71); + return true; } } - plugin.logTimeTaken("StatCommand", "onCommand", time, 90); - return true; + return false; } + //check whether all necessary ingredients are present to proceed with a lookup + private boolean isValidStatRequest(StatRequest request) { + if (request.getStatName() != null) { + if (request.topFlag() || request.getPlayerName() != null) { + validatePlayerFlag(request); + return enumHandler.isValidStatEntry(request.getStatName(), request.getSubStatEntry()); + } + } + return false; + } + + //account for the fact that "player" could be either a subStatEntry or a flag to indicate the target for the lookup, and correct the request if necessary + private void validatePlayerFlag(StatRequest request) { + if (!enumHandler.isValidStatEntry(request.getStatName(), request.getSubStatEntry()) && request.playerFlag()) { + request.setSubStatEntry("player"); + } + } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/TabCompleter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/TabCompleter.java index 970cb46..29a0f08 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/TabCompleter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/TabCompleter.java @@ -1,7 +1,6 @@ package com.gmail.artemis.the.gr8.playerstats.commands; import com.gmail.artemis.the.gr8.playerstats.Main; -import com.gmail.artemis.the.gr8.playerstats.StatManager; import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import org.bukkit.command.Command; @@ -15,16 +14,12 @@ import java.util.stream.Collectors; public class TabCompleter implements org.bukkit.command.TabCompleter { private final EnumHandler enumHandler; - private final OfflinePlayerHandler offlinePlayerHandler; - private final StatManager statManager; private final Main plugin; private final List commandOptions; - public TabCompleter(EnumHandler e, StatManager s, Main p) { + public TabCompleter(EnumHandler e, Main p) { enumHandler = e; - offlinePlayerHandler = OfflinePlayerHandler.getInstance(); - statManager = s; plugin = p; commandOptions = new ArrayList<>(); @@ -45,14 +40,14 @@ public class TabCompleter implements org.bukkit.command.TabCompleter { //after typing "stat", suggest a list of viable statistics if (args.length >= 1) { if (args.length == 1) { - tabSuggestions = statManager.getStatNames().stream().filter(stat -> + tabSuggestions = enumHandler.getStatNames().stream().filter(stat -> stat.contains(args[0].toLowerCase())).collect(Collectors.toList()); } //after checking if args[0] is a viable statistic, suggest substatistic OR commandOptions else { - if (statManager.isStatistic(args[args.length-2])) { - tabSuggestions = switch (statManager.getStatType(args[args.length-2])) { + if (enumHandler.isStatistic(args[args.length-2])) { + tabSuggestions = switch (enumHandler.getStatType(args[args.length-2])) { case UNTYPED -> commandOptions; case BLOCK -> enumHandler.getBlockNames().stream().filter(block -> block.contains(args[args.length - 1])).collect(Collectors.toList()); @@ -61,23 +56,22 @@ public class TabCompleter implements org.bukkit.command.TabCompleter { case ENTITY -> enumHandler.getEntityTypeNames().stream().filter(entity -> entity.contains(args[args.length - 1])).collect(Collectors.toList()); }; - } //if previous arg = "player", suggest playerNames else if (args[args.length-2].equalsIgnoreCase("player")) { - if (args.length >= 3 && statManager.getEntityStatNames().contains(args[args.length-3].toLowerCase())) { + if (args.length >= 3 && enumHandler.getEntityStatNames().contains(args[args.length-3].toLowerCase())) { tabSuggestions = commandOptions; } else { - tabSuggestions = offlinePlayerHandler.getAllOfflinePlayerNames().stream().filter(player -> + tabSuggestions = OfflinePlayerHandler.getAllOfflinePlayerNames().stream().filter(player -> player.toLowerCase().contains(args[args.length-1].toLowerCase())).collect(Collectors.toList()); } } //after a substatistic, suggest commandOptions - else if (statManager.isSubStatEntry(args[args.length-2])) { + else if (enumHandler.isSubStatEntry(args[args.length-2])) { tabSuggestions = commandOptions; } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/filehandlers/ConfigHandler.java similarity index 96% rename from src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java rename to src/main/java/com/gmail/artemis/the/gr8/playerstats/filehandlers/ConfigHandler.java index 1f6b76d..172aa18 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ConfigHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/filehandlers/ConfigHandler.java @@ -1,5 +1,6 @@ -package com.gmail.artemis.the.gr8.playerstats; +package com.gmail.artemis.the.gr8.playerstats.filehandlers; +import com.gmail.artemis.the.gr8.playerstats.Main; import org.bukkit.ChatColor; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/listeners/JoinListener.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/listeners/JoinListener.java index 2f7cc0d..96136cb 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/listeners/JoinListener.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/listeners/JoinListener.java @@ -8,16 +8,13 @@ import org.bukkit.event.player.PlayerJoinEvent; public class JoinListener implements Listener { - private final OfflinePlayerHandler offlinePlayerHandler; - public JoinListener() { - offlinePlayerHandler = OfflinePlayerHandler.getInstance(); } @EventHandler public void onPlayerJoin(PlayerJoinEvent joinEvent) { if (!joinEvent.getPlayer().hasPlayedBefore()) { - offlinePlayerHandler.updateOfflinePlayers(); + OfflinePlayerHandler.updateOfflinePlayers(); } } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/EnumHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/EnumHandler.java index 8688b59..644bbcc 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/EnumHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/EnumHandler.java @@ -1,28 +1,51 @@ package com.gmail.artemis.the.gr8.playerstats.utils; +import com.gmail.artemis.the.gr8.playerstats.Main; import org.bukkit.Material; +import org.bukkit.Statistic; import org.bukkit.entity.EntityType; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; public class EnumHandler { private final List blockNames; private final List entityTypeNames; private final List itemNames; + private final List statNames; + private final List entityStatNames; + private final List subStatEntryNames; + private final Main plugin; - public EnumHandler() { + public EnumHandler(Main p) { + plugin = p; + blockNames = Arrays.stream(Material.values()).filter( Material::isBlock).map(Material::toString).map(String::toLowerCase).toList(); entityTypeNames = Arrays.stream(EntityType.values()).map( EntityType::toString).map(String::toLowerCase).toList(); itemNames = Arrays.stream(Material.values()).filter( Material::isItem).map(Material::toString).map(String::toLowerCase).toList(); + statNames = Arrays.stream(Statistic.values()).map( + Statistic::toString).map(String::toLowerCase).toList(); + + entityStatNames = Arrays.stream(Statistic.values()).filter(statistic -> + statistic.getType().equals(Statistic.Type.ENTITY)).map( + Statistic::toString).map(String::toLowerCase).collect(Collectors.toList()); + + subStatEntryNames = new ArrayList<>(); + subStatEntryNames.addAll(getBlockNames()); + subStatEntryNames.addAll(getEntityTypeNames()); + subStatEntryNames.addAll(getItemNames()); } + //checks whether the provided string is a valid item public boolean isItem(String itemName) { return itemNames.contains(itemName.toLowerCase()); } @@ -38,6 +61,7 @@ public class EnumHandler { return itemNames; } + //checks whether the provided string is a valid entity public boolean isEntityType(String entityName) { return entityTypeNames.contains(entityName.toLowerCase()); } @@ -49,7 +73,12 @@ public class EnumHandler { try { entityType = EntityType.valueOf(entityName.toUpperCase()); } - catch (IllegalArgumentException | NullPointerException exception) { + catch (IllegalArgumentException e) { + plugin.getLogger().warning("IllegalArgumentException: " + entityName + " is not a valid statistic name!"); + return null; + } + catch (NullPointerException e) { + plugin.getLogger().warning("NullPointerException: please provide a statistic name!"); return null; } return entityType; @@ -60,6 +89,7 @@ public class EnumHandler { return entityTypeNames; } + //checks whether the provided string is a valid block public boolean isBlock(String materialName) { return blockNames.contains(materialName.toLowerCase()); } @@ -75,4 +105,85 @@ public class EnumHandler { return blockNames; } + //returns the statistic enum constant, or null if non-existent (param: statName, not case sensitive) + public Statistic getStatEnum(String statName) { + try { + return Statistic.valueOf(statName.toUpperCase()); + } + catch (IllegalArgumentException e) { + plugin.getLogger().warning("IllegalArgumentException: " + statName + " is not a valid statistic name!"); + return null; + } + catch (NullPointerException e) { + plugin.getLogger().warning("NullPointerException: please provide a statistic name!"); + return null; + } + } + + //gets the type of the statistic from the string, otherwise returns null (param: statName, not case sensitive) + public Statistic.Type getStatType(String statName) { + try { + return Statistic.valueOf(statName.toUpperCase()).getType(); + } + catch (IllegalArgumentException e) { + plugin.getLogger().warning("IllegalArgumentException: " + statName + " is not a valid statistic name!"); + return null; + } + catch (NullPointerException e) { + plugin.getLogger().warning("NullPointerException: please provide a statistic name!"); + return null; + } + } + + //checks if string is a valid statistic (param: statName, not case sensitive) + public boolean isStatistic(String statName) { + return statNames.contains(statName.toLowerCase()); + } + + //returns the names of all general statistics in lowercase + public List getStatNames() { + return statNames; + } + + //returns all statistics that have type entities, in lowercase + public List getEntityStatNames() { + return entityStatNames; + } + + //checks if this statistic is a subStatEntry, meaning it is a block, item or entity (param: statName, not case sensitive) + public boolean isSubStatEntry(String statName) { + return subStatEntryNames.contains(statName.toLowerCase()); + } + + //checks if string is a valid statistic (param: statName, not case sensitive) + public boolean isValidStatEntry(String statName) { + return isValidStatEntry(statName, null); + } + + //checks whether a subStatEntry is of the type that the statistic requires + public boolean isValidStatEntry(String statName, String subStatEntry) { + Statistic stat = getStatEnum(statName); + return (stat != null && isMatchingSubStatEntry(stat, subStatEntry)); + } + + //returns true if subStatEntry matches the type the stat requires, or if stat is untyped and subStatEntry is null + private boolean isMatchingSubStatEntry(@NotNull Statistic stat, String subStatEntry) { + switch (stat.getType()) { + case ENTITY -> { + return subStatEntry != null && isEntityType(subStatEntry); + } + case ITEM -> { + return subStatEntry != null && isItem(subStatEntry); + } + case BLOCK -> { + return subStatEntry != null && isBlock(subStatEntry); + } + case UNTYPED -> { + return subStatEntry==null; + } + default -> { + return false; + } + } + } } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java index 37a984c..7cc5fa7 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java @@ -7,41 +7,31 @@ import java.util.*; public class OfflinePlayerHandler { - private static OfflinePlayerHandler instance; - private HashMap offlinePlayerMap; - private List offlinePlayerNames; - private int totalOfflinePlayers; + private static HashMap offlinePlayerMap; + private static List offlinePlayerNames; + private static int totalOfflinePlayers; private OfflinePlayerHandler() { - updateOfflinePlayers(); } - public static OfflinePlayerHandler getInstance() { - if (instance == null) { - instance = new OfflinePlayerHandler(); - } - return instance; - } - - public boolean isOfflinePlayerName(String playerName) { + public static boolean isOfflinePlayerName(String playerName) { return offlinePlayerNames.contains(playerName); } - public OfflinePlayer getOfflinePlayer(String playerName) { - long time = System.currentTimeMillis(); + public static OfflinePlayer getOfflinePlayer(String playerName) { return offlinePlayerMap.get(playerName); } - public int getOfflinePlayerCount() { + public static int getOfflinePlayerCount() { return totalOfflinePlayers > 0 ? totalOfflinePlayers : 1; } - public List getAllOfflinePlayerNames() { + public static List getAllOfflinePlayerNames() { return offlinePlayerNames; } //stores a private HashMap with keys:playerName and values:OfflinePlayer, and a private list of the names for easy access - public void updateOfflinePlayers() { + public static void updateOfflinePlayers() { long totalTime = System.currentTimeMillis(); long time = System.currentTimeMillis(); if (offlinePlayerMap == null) offlinePlayerMap = new HashMap<>(); diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java index c35fcaf..08d54e9 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java @@ -1,6 +1,6 @@ package com.gmail.artemis.the.gr8.playerstats.utils; -import com.gmail.artemis.the.gr8.playerstats.ConfigHandler; +import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler; import org.bukkit.ChatColor; import org.bukkit.map.MinecraftFont; From ef33e36c65a9169058ce29500dcd8e73ba66359a Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Wed, 18 May 2022 00:10:14 +0200 Subject: [PATCH 8/8] Successfully implemented multi-threading! (#24) --- .../the/gr8/playerstats/StatThread.java | 65 ++++--------------- .../playerstats/utils/OutputFormatter.java | 4 +- src/main/resources/config.yml | 2 +- 3 files changed, 16 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatThread.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatThread.java index a61f57f..822b02b 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatThread.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/StatThread.java @@ -57,28 +57,26 @@ public class StatThread extends Thread { outputFormatter.formatPlayerStat( playerName, statName, subStatEntry, getStatistic( statName, subStatEntry, playerName))); + plugin.logTimeTaken(className, "run(): individual stat", time, 60); + } catch (Exception e) { sender.sendMessage(outputFormatter.formatExceptions(e.toString())); } } else if (topFlag) { try { - LinkedHashMap topStats = getTopStatisticsForLoop(statName, subStatEntry); - time = plugin.logTimeTaken(className, "run(): for loop", time, 67); + LinkedHashMap topStats = getTopStatistics(statName, subStatEntry); + plugin.logTimeTaken(className, "run(): for each loop", time, 69); - LinkedHashMap topStats2 = getTopStatisticsForEach(statName, subStatEntry); - time = plugin.logTimeTaken(className, "run(): for each loop", time, 70); - - String top2 = outputFormatter.formatTopStats(topStats2, statName, subStatEntry); - sender.sendMessage(top2); - plugin.logTimeTaken(className, "run(): format output", time, 74); + String top = outputFormatter.formatTopStats(topStats, statName, subStatEntry); + sender.sendMessage(top); + plugin.logTimeTaken(className, "run(): format output", time, 73); } catch (Exception e) { sender.sendMessage(outputFormatter.formatExceptions(e.toString())); e.printStackTrace(); } } - } //returns the integer associated with a certain statistic for a player @@ -94,41 +92,7 @@ public class StatThread extends Thread { throw new IllegalArgumentException("Player object for " + playerName + " could not be retrieved!"); } - private LinkedHashMap getTopStatisticsForLoop(String statName, String subStatEntry) throws NullPointerException { - long time = System.currentTimeMillis(); - - Statistic stat = enumHandler.getStatEnum(statName); - - if (stat != null) { - HashMap playerStats = new HashMap<>((int) (OfflinePlayerHandler.getOfflinePlayerCount() * 1.05)); - for (String playerName : OfflinePlayerHandler.getAllOfflinePlayerNames()) { - OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName); - if (player != null) { - try { - int statistic = getPlayerStat(player, stat, subStatEntry); - if (statistic > 0) { - playerStats.put(playerName, statistic); - } - } catch (IllegalArgumentException ignored) { - } - } - } - time = plugin.logTimeTaken(className, "for loop", time, 116); - - LinkedHashMap topStats = playerStats.entrySet().stream() - .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) - .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - - plugin.logTimeTaken(className, "for loop, sorting", time, 122); - return topStats; - - } - throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); - } - - private LinkedHashMap getTopStatisticsForEach(String statName, String subStatEntry) { - long time = System.currentTimeMillis(); - + private LinkedHashMap getTopStatistics(String statName, String subStatEntry) { Statistic stat = enumHandler.getStatEnum(statName); if (stat != null) { @@ -137,19 +101,16 @@ public class StatThread extends Thread { OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName); if (player != null) try { - playerStats.put(playerName, getPlayerStat(player, stat, subStatEntry)); + int statistic = getPlayerStat(player, stat, subStatEntry); + if (statistic > 0) { + playerStats.put(playerName, statistic); + } } catch (IllegalArgumentException ignored) { } }); - - time = plugin.logTimeTaken(className, "for each loop", time, 145); - - LinkedHashMap topStats = playerStats.entrySet().stream() + return playerStats.entrySet().stream() .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) .limit(10).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - - plugin.logTimeTaken(className, "for each loop, sorting", time, 151); - return topStats; } throw new NullPointerException("Statistic " + statName + " could not be retrieved!"); } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java index 08d54e9..7cf57a1 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/OutputFormatter.java @@ -50,12 +50,12 @@ public class OutputFormatter { rankList.append("\n") .append(chatColors.get("list-numbers")).append(count).append(". ") - .append(chatColors.get("player-names-ranked")).append(playerName).append(" ") + .append(chatColors.get("player-names-ranked")).append(playerName) .append(chatColors.get("dots")); if (useDots) { rankList.append(" "); - int dots = (125 - font.getWidth(count + ". " + playerName + " ")); + int dots = (int) Math.round((125.0 - font.getWidth(count + ". " + playerName))/2); if (dots >= 1) { rankList.append(".".repeat(dots)); } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 7103e89..2fc8f81 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -19,5 +19,5 @@ ranked-list: # If true, the statistics will be aligned so that they are all underneath each other use-dots: true - dots: gray + dots: dark_gray