From f69367cb31e1233ae2e5274704f9c4091034850d Mon Sep 17 00:00:00 2001 From: Artemis-the-gr8 Date: Wed, 26 Oct 2022 12:59:10 +0200 Subject: [PATCH] The exclude function now works completely, the only thing left is give fancier feedback (#88) --- .../com/artemis/the/gr8/playerstats/Main.java | 2 +- .../playerstats/commands/ExcludeCommand.java | 43 ++++-- .../gr8/playerstats/commands/StatCommand.java | 6 +- .../playerstats/commands/TabCompleter.java | 24 ++- .../gr8/playerstats/config/ConfigHandler.java | 2 +- .../msg/components/PrideComponentFactory.java | 5 +- .../multithreading/PlayerLoadAction.java | 2 +- .../multithreading/ThreadManager.java | 2 +- .../gr8/playerstats/utils/FileHandler.java | 8 +- .../utils/OfflinePlayerHandler.java | 140 ++++++++++-------- 10 files changed, 135 insertions(+), 99 deletions(-) diff --git a/src/main/java/com/artemis/the/gr8/playerstats/Main.java b/src/main/java/com/artemis/the/gr8/playerstats/Main.java index 0df25dd..ccdb8b3 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/Main.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/Main.java @@ -127,7 +127,7 @@ public final class Main extends JavaPlugin implements PlayerStats { } PluginCommand excludecmd = this.getCommand("statisticexclude"); if (excludecmd != null) { - excludecmd.setExecutor(new ExcludeCommand()); + excludecmd.setExecutor(new ExcludeCommand(outputManager)); excludecmd.setTabCompleter(tabCompleter); } diff --git a/src/main/java/com/artemis/the/gr8/playerstats/commands/ExcludeCommand.java b/src/main/java/com/artemis/the/gr8/playerstats/commands/ExcludeCommand.java index 22ad05b..c2980fd 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/commands/ExcludeCommand.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/commands/ExcludeCommand.java @@ -1,44 +1,55 @@ package com.artemis.the.gr8.playerstats.commands; -import com.artemis.the.gr8.playerstats.utils.MyLogger; +import com.artemis.the.gr8.playerstats.msg.OutputManager; import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; -import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.jetbrains.annotations.NotNull; -import java.util.List; +import java.util.ArrayList; public final class ExcludeCommand implements CommandExecutor { + private static OutputManager outputManager; private final OfflinePlayerHandler offlinePlayerHandler; - public ExcludeCommand() { + public ExcludeCommand(OutputManager outputManager) { + ExcludeCommand.outputManager = outputManager; this.offlinePlayerHandler = OfflinePlayerHandler.getInstance(); } @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (args.length == 1 && args[0].equalsIgnoreCase("list")) { - List excludedPlayers = offlinePlayerHandler.getListOfExcludedPlayerNames(); - - for (String player : excludedPlayers) { - MyLogger.logLowLevelMsg(player); - } + ArrayList excludedPlayers = offlinePlayerHandler.getExcludedPlayerNames(); + sender.sendMessage(String.valueOf(excludedPlayers)); + return true; } //this is going to return false for all UUIDs in file at boot-up - that's an issue - else if (args.length >= 2 && offlinePlayerHandler.isLoadedPlayer(args[1])) { - String playerName = args[1]; - OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName); + else if (args.length >= 2) { + String playerName = args[1]; switch (args[0]) { - case "add" -> offlinePlayerHandler.addPlayerToExcludeList(player.getUniqueId()); - case "remove" -> offlinePlayerHandler.removePlayerFromExcludeList(player.getUniqueId()); + case "add" -> { + if (offlinePlayerHandler.isLoadedPlayer(playerName)) { + offlinePlayerHandler.addLoadedPlayerToExcludeList(playerName); + sender.sendMessage("Excluded " + playerName + "!"); + return true; + } + } + case "remove" -> { + if (offlinePlayerHandler.isExcludedPlayer(playerName)) { + offlinePlayerHandler.addExcludedPlayerToLoadedList(playerName); + sender.sendMessage("Removed " + playerName + " from the exclude list again!"); + return true; + } + } case "info" -> { - boolean isExcluded = offlinePlayerHandler.isExcluded(player.getUniqueId()); - MyLogger.logLowLevelMsg(player.getName() + " is excluded: " + isExcluded); + boolean isExcluded = offlinePlayerHandler.isExcludedPlayer(playerName); + sender.sendMessage(playerName+ " is excluded: " + isExcluded); + return true; } } } diff --git a/src/main/java/com/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/artemis/the/gr8/playerstats/commands/StatCommand.java index ae6a580..d09879e 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/commands/StatCommand.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/commands/StatCommand.java @@ -45,11 +45,13 @@ public final class StatCommand implements CommandExecutor { @Override public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (args.length == 0 || args[0].equalsIgnoreCase("help")) { //in case of less than 1 argument or "help", display the help message + if (args.length == 0 || + args[0].equalsIgnoreCase("help") || + args[0].equalsIgnoreCase("info")) { outputManager.sendHelp(sender); } else if (args[0].equalsIgnoreCase("examples") || - args[0].equalsIgnoreCase("example")) { //in case of "statistic examples", show examples + args[0].equalsIgnoreCase("example")) { outputManager.sendExamples(sender); } else { diff --git a/src/main/java/com/artemis/the/gr8/playerstats/commands/TabCompleter.java b/src/main/java/com/artemis/the/gr8/playerstats/commands/TabCompleter.java index b6ebba3..836688e 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/commands/TabCompleter.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/commands/TabCompleter.java @@ -7,7 +7,6 @@ import org.bukkit.Statistic; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.EntityType; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,14 +28,9 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter { public TabCompleter() { offlinePlayerHandler = OfflinePlayerHandler.getInstance(); enumHandler = EnumHandler.getInstance(); - prepareLists(); } - //args[0] = statistic (length = 1) - //args[1] = target (player/server/top) OR sub-stat (block/item/entity) (length = 2) - //args[2] = playerName OR target (player/server/top) (length = 3) - //args[3] = playerName (length = 4) @Override public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { if (command.getName().equalsIgnoreCase("statistic")) { @@ -59,8 +53,13 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter { } else if (args.length == 2) { tabSuggestions = switch (args[0]) { - case "add" -> offlinePlayerHandler.getOfflinePlayerNames(); - case "remove" -> removablePlayerNames(); + case "add" -> offlinePlayerHandler.getLoadedOfflinePlayerNames(); + case "remove" -> offlinePlayerHandler.getExcludedPlayerNames(); + case "info" -> { + ArrayList loadedPlayers = offlinePlayerHandler.getLoadedOfflinePlayerNames(); + loadedPlayers.addAll(offlinePlayerHandler.getExcludedPlayerNames()); + yield loadedPlayers; + } default -> tabSuggestions; }; } @@ -91,7 +90,7 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter { tabSuggestions = statCommandTargets; //if arg before "player" was entity-sub-stat, suggest targets } else { //otherwise "player" is the target: suggest playerNames - tabSuggestions = offlinePlayerHandler.getOfflinePlayerNames(); + tabSuggestions = offlinePlayerHandler.getLoadedOfflinePlayerNames(); } } @@ -117,6 +116,7 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter { private @NotNull List firstStatCommandArgSuggestions() { List suggestions = enumHandler.getAllStatNames(); suggestions.add("examples"); + suggestions.add("info"); suggestions.add("help"); return suggestions; } @@ -142,11 +142,6 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter { } } - @Contract(pure = true) - private @Nullable List removablePlayerNames() { - return statCommandTargets; - } - private void prepareLists() { statCommandTargets = new ArrayList<>(); statCommandTargets.add("top"); @@ -158,6 +153,7 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter { excludeCommandOptions.add("add"); excludeCommandOptions.add("list"); excludeCommandOptions.add("remove"); + excludeCommandOptions.add("info"); //breaking an item means running its durability negative itemsThatCanBreak = Arrays.stream(Material.values()) diff --git a/src/main/java/com/artemis/the/gr8/playerstats/config/ConfigHandler.java b/src/main/java/com/artemis/the/gr8/playerstats/config/ConfigHandler.java index 7e875c0..6da522d 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/config/ConfigHandler.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/config/ConfigHandler.java @@ -63,7 +63,7 @@ public final class ConfigHandler extends FileHandler { Map defaultValues = defaultValueGetter.getValuesToAdjust(); defaultValues.put("config-version", configVersion); - super.addValuesToFile(defaultValues); + super.addValues(defaultValues); reload(); MyLogger.logLowLevelMsg("Your config has been updated to version " + configVersion + diff --git a/src/main/java/com/artemis/the/gr8/playerstats/msg/components/PrideComponentFactory.java b/src/main/java/com/artemis/the/gr8/playerstats/msg/components/PrideComponentFactory.java index dab0dde..c43322c 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/msg/components/PrideComponentFactory.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/msg/components/PrideComponentFactory.java @@ -6,6 +6,8 @@ import com.artemis.the.gr8.playerstats.enums.PluginColor; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.minimessage.MiniMessage; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; import java.util.Random; @@ -85,7 +87,8 @@ public class PrideComponentFactory extends ComponentFactory { .build(); } - private TextComponent backwardsPluginPrefixComponent() { + @Contract(" -> new") + private @NotNull TextComponent backwardsPluginPrefixComponent() { return text() .append(MiniMessage.miniMessage() .deserialize("<#631ae6>[" + diff --git a/src/main/java/com/artemis/the/gr8/playerstats/multithreading/PlayerLoadAction.java b/src/main/java/com/artemis/the/gr8/playerstats/multithreading/PlayerLoadAction.java index 7ba037b..0d9e7f1 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/multithreading/PlayerLoadAction.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/multithreading/PlayerLoadAction.java @@ -73,7 +73,7 @@ final class PlayerLoadAction extends RecursiveAction { String playerName = player.getName(); MyLogger.actionRunning(Thread.currentThread().getName()); if (playerName != null && - !offlinePlayerHandler.isExcluded(player.getUniqueId()) && + !offlinePlayerHandler.isExcludedPlayer(player.getUniqueId()) && UnixTimeHandler.hasPlayedSince(lastPlayedLimit, player.getLastPlayed())) { offlinePlayerUUIDs.put(playerName, player.getUniqueId()); } diff --git a/src/main/java/com/artemis/the/gr8/playerstats/multithreading/ThreadManager.java b/src/main/java/com/artemis/the/gr8/playerstats/multithreading/ThreadManager.java index 417e08e..377b071 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/multithreading/ThreadManager.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/multithreading/ThreadManager.java @@ -58,7 +58,7 @@ public final class ThreadManager { public static @NotNull StatAction getStatAction(StatRequest.Settings requestSettings) { OfflinePlayerHandler offlinePlayerHandler = OfflinePlayerHandler.getInstance(); - ImmutableList relevantPlayerNames = ImmutableList.copyOf(offlinePlayerHandler.getOfflinePlayerNames()); + ImmutableList relevantPlayerNames = ImmutableList.copyOf(offlinePlayerHandler.getLoadedOfflinePlayerNames()); ConcurrentHashMap resultingStatNumbers = new ConcurrentHashMap<>(relevantPlayerNames.size()); StatAction task = new StatAction(relevantPlayerNames, requestSettings, resultingStatNumbers); diff --git a/src/main/java/com/artemis/the/gr8/playerstats/utils/FileHandler.java b/src/main/java/com/artemis/the/gr8/playerstats/utils/FileHandler.java index ce42e4a..24de82e 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/utils/FileHandler.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/utils/FileHandler.java @@ -25,7 +25,7 @@ public abstract class FileHandler { loadFile(); } - public void loadFile() { + private void loadFile() { JavaPlugin plugin = Main.getPluginInstance(); file = new File(plugin.getDataFolder(), fileName); @@ -48,7 +48,7 @@ public abstract class FileHandler { return fileConfiguration; } - public void addValuesToFile(@NotNull Map keyValuePairs) { + public void addValues(@NotNull Map keyValuePairs) { keyValuePairs.forEach(this::setValue); save(); updateFile(); @@ -59,7 +59,7 @@ public abstract class FileHandler { * (or expanded if it already exists) * @param value the value(s) to expand the List with */ - public void addEntryToListInFile(@NotNull String key, @NotNull String value) { + public void writeEntryToList(@NotNull String key, @NotNull String value) { List existingList = fileConfiguration.getStringList(key); List updatedList = existingList.stream() @@ -72,7 +72,7 @@ public abstract class FileHandler { updateFile(); } - public void removeEntryFromListInFile(@NotNull String key, @NotNull String value) { + public void removeEntryFromList(@NotNull String key, @NotNull String value) { List currentValues = fileConfiguration.getStringList(key); if (currentValues.remove(value)) { diff --git a/src/main/java/com/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java b/src/main/java/com/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java index b5cfaca..1dae341 100644 --- a/src/main/java/com/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java +++ b/src/main/java/com/artemis/the/gr8/playerstats/utils/OfflinePlayerHandler.java @@ -4,7 +4,6 @@ import com.artemis.the.gr8.playerstats.config.ConfigHandler; import com.artemis.the.gr8.playerstats.multithreading.ThreadManager; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; -import org.bukkit.configuration.file.FileConfiguration; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; @@ -13,7 +12,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; import java.util.function.Predicate; -import java.util.stream.Collectors; /** * A utility class that deals with OfflinePlayers. It stores a list @@ -25,14 +23,13 @@ public final class OfflinePlayerHandler extends FileHandler { private static volatile OfflinePlayerHandler instance; private final ConfigHandler config; - private static FileConfiguration excludedPlayers; - private static ConcurrentHashMap offlinePlayerUUIDs; + private static ConcurrentHashMap includedPlayerUUIDs; + private static ConcurrentHashMap excludedPlayerUUIDs; private OfflinePlayerHandler() { super("excluded_players.yml"); config = ConfigHandler.getInstance(); - excludedPlayers = super.getFileConfiguration(); loadOfflinePlayers(); } @@ -53,8 +50,6 @@ public final class OfflinePlayerHandler extends FileHandler { @Override public void reload() { super.reload(); - excludedPlayers = super.getFileConfiguration(); - loadOfflinePlayers(); } @@ -66,43 +61,40 @@ public final class OfflinePlayerHandler extends FileHandler { * @return true if this player is included */ public boolean isLoadedPlayer(String playerName) { - return offlinePlayerUUIDs.containsKey(playerName); + return includedPlayerUUIDs.containsKey(playerName); } - public void addPlayerToExcludeList(UUID uniqueID) { - super.addEntryToListInFile("excluded", uniqueID.toString()); + public boolean isExcludedPlayer(String playerName) { + return excludedPlayerUUIDs.containsKey(playerName); } - public void removePlayerFromExcludeList(UUID uniqueID) { - super.removeEntryFromListInFile("excluded", uniqueID.toString()); + public boolean isExcludedPlayer(UUID uniqueID) { + return excludedPlayerUUIDs.containsValue(uniqueID); } - public List getListOfExcludedPlayerNames() { - List excludedUUIDs = excludedPlayers.getStringList("excluded"); - return excludedUUIDs.stream() - .map(UUID::fromString) - .map(Bukkit::getOfflinePlayer) - .map(OfflinePlayer::getName) - .collect(Collectors.toList()); + public void addLoadedPlayerToExcludeList(String playerName) throws IllegalArgumentException { + UUID uuid = includedPlayerUUIDs.get(playerName); + if (uuid == null) { + throw new IllegalArgumentException("This player is not loaded, and therefore cannot be excluded!"); + } + super.writeEntryToList("excluded", uuid.toString()); + includedPlayerUUIDs.remove(playerName); + excludedPlayerUUIDs.put(playerName, uuid); } - public boolean isExcluded(UUID uniqueID) { - List excluded = excludedPlayers.getStringList("excluded"); - - return excluded.stream() - .filter(Objects::nonNull) - .map(UUID::fromString) - .anyMatch(uuid -> uuid.equals(uniqueID)); + public void addExcludedPlayerToLoadedList(String playerName) { + UUID uuid = excludedPlayerUUIDs.get(playerName); + if (uuid == null) { + throw new IllegalArgumentException("This player is not excluded, and therefore cannot be un-excluded!"); + } + super.removeEntryFromList("excluded", uuid.toString()); + excludedPlayerUUIDs.remove(playerName); + includedPlayerUUIDs.put(playerName, uuid); } - /** - * Gets the number of OfflinePlayers that are - * currently included in statistic calculations. - * - * @return the number of included OfflinePlayers - */ - public int getOfflinePlayerCount() { - return offlinePlayerUUIDs.size(); + @Contract(" -> new") + public @NotNull ArrayList getExcludedPlayerNames() { + return Collections.list(excludedPlayerUUIDs.keys()); } /** @@ -112,8 +104,18 @@ public final class OfflinePlayerHandler extends FileHandler { * @return the ArrayList */ @Contract(" -> new") - public @NotNull ArrayList getOfflinePlayerNames() { - return Collections.list(offlinePlayerUUIDs.keys()); + public @NotNull ArrayList getLoadedOfflinePlayerNames() { + return Collections.list(includedPlayerUUIDs.keys()); + } + + /** + * Gets the number of OfflinePlayers that are + * currently included in statistic calculations. + * + * @return the number of included OfflinePlayers + */ + public int getOfflinePlayerCount() { + return includedPlayerUUIDs.size(); } /** @@ -126,8 +128,8 @@ public final class OfflinePlayerHandler extends FileHandler { * of players that should be included in statistic calculations */ public @NotNull OfflinePlayer getOfflinePlayer(String playerName) throws IllegalArgumentException { - if (offlinePlayerUUIDs.get(playerName) != null) { - return Bukkit.getOfflinePlayer(offlinePlayerUUIDs.get(playerName)); + if (includedPlayerUUIDs.get(playerName) != null) { + return Bukkit.getOfflinePlayer(includedPlayerUUIDs.get(playerName)); } else { MyLogger.logWarning("Cannot calculate statistics for player-name: " + playerName + @@ -139,29 +141,51 @@ public final class OfflinePlayerHandler extends FileHandler { private void loadOfflinePlayers() { Executors.newSingleThreadExecutor().execute(() -> { - long time = System.currentTimeMillis(); - - OfflinePlayer[] offlinePlayers; - if (config.whitelistOnly()) { - offlinePlayers = getWhitelistedPlayers(); - } - else if (config.excludeBanned()) { - offlinePlayers = getNonBannedPlayers(); - } - else { - offlinePlayers = Bukkit.getOfflinePlayers(); - } - - int size = offlinePlayerUUIDs != null ? offlinePlayerUUIDs.size() : 16; - offlinePlayerUUIDs = new ConcurrentHashMap<>(size); - - ForkJoinPool.commonPool().invoke(ThreadManager.getPlayerLoadAction(offlinePlayers, offlinePlayerUUIDs)); - - MyLogger.actionFinished(); - MyLogger.logLowLevelTask(("Loaded " + offlinePlayerUUIDs.size() + " offline players"), time); + loadExcludedPlayerNames(); + loadIncludedOfflinePlayers(); }); } + private void loadIncludedOfflinePlayers() { + long time = System.currentTimeMillis(); + + OfflinePlayer[] offlinePlayers; + if (config.whitelistOnly()) { + offlinePlayers = getWhitelistedPlayers(); + } else if (config.excludeBanned()) { + offlinePlayers = getNonBannedPlayers(); + } else { + offlinePlayers = Bukkit.getOfflinePlayers(); + } + + int size = includedPlayerUUIDs != null ? includedPlayerUUIDs.size() : 16; + includedPlayerUUIDs = new ConcurrentHashMap<>(size); + + ForkJoinPool.commonPool().invoke(ThreadManager.getPlayerLoadAction(offlinePlayers, includedPlayerUUIDs)); + + MyLogger.actionFinished(); + MyLogger.logLowLevelTask(("Loaded " + includedPlayerUUIDs.size() + " offline players"), time); + } + + private void loadExcludedPlayerNames() { + long time = System.currentTimeMillis(); + + excludedPlayerUUIDs = new ConcurrentHashMap<>(); + List excluded = super.getFileConfiguration().getStringList("excluded"); + excluded.stream() + .filter(Objects::nonNull) + .map(UUID::fromString) + .forEach(uuid -> { + OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); + String playerName = player.getName(); + if (playerName != null) { + excludedPlayerUUIDs.put(playerName, uuid); + } + }); + + MyLogger.logLowLevelTask("Loaded " + excludedPlayerUUIDs.size() + " excluded players from file", time); + } + private OfflinePlayer[] getWhitelistedPlayers() { return Bukkit.getWhitelistedPlayers().toArray(OfflinePlayer[]::new); }