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 18a53d5..4a49ced 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 @@ -21,20 +21,11 @@ public class Main extends JavaPlugin { private static BukkitAudiences adventure; private static PlayerStats playerStatsAPI; + private static OutputManager outputManager; + private static ShareManager shareManager; + private static StatManager statManager; + private static ThreadManager threadManager; - public static @NotNull BukkitAudiences adventure() throws IllegalStateException { - if (adventure == null) { - throw new IllegalStateException("Tried to access Adventure without PlayerStats being enabled!"); - } - return adventure; - } - - public static @NotNull PlayerStats getPlayerStatsAPI() throws IllegalStateException { - if (playerStatsAPI == null) { - throw new IllegalStateException("PlayerStats does not seem to be loaded!"); - } - return playerStatsAPI; - } @Override public void onEnable() { @@ -42,14 +33,8 @@ public class Main extends JavaPlugin { ConfigHandler config = new ConfigHandler(this); OfflinePlayerHandler offlinePlayerHandler = new OfflinePlayerHandler(); - OutputManager outputManager = OutputManager.getInstance(config); - StatManager statManager = StatManager.getInstance(outputManager, offlinePlayerHandler); - ThreadManager threadManager = ThreadManager.getInstance(config, outputManager, statManager, offlinePlayerHandler); - ShareManager shareManager = ShareManager.getInstance(config); - - //initialize the Adventure library and the API - adventure = BukkitAudiences.create(this); - playerStatsAPI = PlayerStatsAPI.load(this, threadManager, outputManager, statManager); + //initialize all the Managers and the API + initializeMainClasses(config, offlinePlayerHandler); //register all commands and the tabCompleter PluginCommand statcmd = this.getCommand("statistic"); @@ -77,4 +62,57 @@ public class Main extends JavaPlugin { } this.getLogger().info("Disabled PlayerStats!"); } + + public static @NotNull BukkitAudiences getAdventure() throws IllegalStateException { + if (adventure == null) { + throw new IllegalStateException("Tried to access Adventure without PlayerStats being enabled!"); + } + return adventure; + } + + public static @NotNull PlayerStats getPlayerStatsAPI() throws IllegalStateException { + if (playerStatsAPI == null) { + throw new IllegalStateException("PlayerStats does not seem to be loaded!"); + } + return playerStatsAPI; + } + + public static @NotNull OutputManager getOutputManager() throws IllegalStateException { + if (outputManager == null) { + throw new IllegalStateException("The OutputManager is not loaded! Is PlayerStats enabled?"); + } + return outputManager; + } + + public static @NotNull ShareManager getShareManager() throws IllegalStateException { + if (shareManager == null) { + throw new IllegalStateException("The ShareManager is not loaded! Is PlayerStats enabled?"); + } + return shareManager; + } + + public static @NotNull StatManager getStatManager() throws IllegalStateException { + if (statManager == null) { + throw new IllegalStateException("The StatManager is not loaded! Is PlayerStats enabled?"); + } + return statManager; + } + + public static @NotNull ThreadManager getThreadManager() throws IllegalStateException { + if (threadManager == null) { + throw new IllegalStateException("The ThreadManager is not loaded! Is PlayerStats enabled?"); + } + return threadManager; + } + + private void initializeMainClasses(ConfigHandler config, OfflinePlayerHandler offlinePlayerHandler) { + adventure = BukkitAudiences.create(this); + + shareManager = new ShareManager(config); + outputManager = new OutputManager(getAdventure(), config, shareManager); + statManager = new StatManager(outputManager, offlinePlayerHandler, config.getTopListMaxSize()); + threadManager = new ThreadManager(config, statManager, outputManager, offlinePlayerHandler); + + playerStatsAPI = PlayerStatsAPI.load(statManager, outputManager); + } } \ No newline at end of file diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ShareManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ShareManager.java index 90cf7dc..2c448d5 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ShareManager.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ShareManager.java @@ -24,8 +24,6 @@ import static java.time.temporal.ChronoUnit.SECONDS; results of past stat-lookups, so the results can be retrieved and shared when a Player clicks the share-button.*/ public final class ShareManager { - private static volatile ShareManager instance; - private static boolean isEnabled; private static int waitingTime; @@ -34,23 +32,10 @@ public final class ShareManager { private ConcurrentHashMap shareTimeStamp; private ArrayBlockingQueue sharedResults; - private ShareManager(ConfigHandler config) { + public ShareManager(ConfigHandler config) { updateSettings(config); } - public static ShareManager getInstance(ConfigHandler config) { - ShareManager shareManager = instance; - if (shareManager != null) { - return shareManager; - } - synchronized (ShareManager.class) { - if (instance == null) { - instance = new ShareManager(config); - } - return instance; - } - } - public static boolean isEnabled() { return isEnabled; } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java index 788e24c..a62a119 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java @@ -20,8 +20,6 @@ import java.util.HashMap; to ensure those will never run at the same time. */ public final class ThreadManager { - private static volatile ThreadManager instance; - private final static int threshold = 10; private int statThreadID; private int reloadThreadID; @@ -36,11 +34,11 @@ public final class ThreadManager { private final HashMap statThreads; private static long lastRecordedCalcTime; - private ThreadManager(ConfigHandler c, OutputManager m, StatManager s, OfflinePlayerHandler o) { - config = c; - outputManager = m; - statManager = s; - offlinePlayerHandler = o; + public ThreadManager(ConfigHandler config, StatManager statManager, OutputManager outputManager, OfflinePlayerHandler offlinePlayerHandler) { + ThreadManager.config = config; + ThreadManager.outputManager = outputManager; + ThreadManager.statManager = statManager; + this.offlinePlayerHandler = offlinePlayerHandler; statThreads = new HashMap<>(); statThreadID = 0; @@ -50,19 +48,6 @@ public final class ThreadManager { startReloadThread(null); } - public static ThreadManager getInstance(ConfigHandler config, OutputManager output, StatManager statManager, OfflinePlayerHandler offlinePlayerHandler) { - ThreadManager threadManager = instance; - if (threadManager != null) { - return threadManager; - } - synchronized (ThreadManager.class) { - if (instance == null) { - instance = new ThreadManager(config, output, statManager, offlinePlayerHandler); - } - return instance; - } - } - public static int getTaskThreshold() { return threshold; } @@ -108,7 +93,7 @@ public final class ThreadManager { } private void startNewStatThread(StatRequest request) { - lastActiveStatThread = new StatThread(config, outputManager, statManager, offlinePlayerHandler, statThreadID, request, lastActiveReloadThread); + lastActiveStatThread = new StatThread(outputManager, statManager, statThreadID, request, lastActiveReloadThread); statThreads.put(request.getCommandSender().getName(), lastActiveStatThread); lastActiveStatThread.start(); } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/PlayerStats.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/PlayerStats.java index aa12b59..9443f39 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/PlayerStats.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/PlayerStats.java @@ -27,7 +27,8 @@ public interface PlayerStats {

- if applicable, a sub-stat-name (example: diorite)(

- a target for this lookup: can be "top", "server", "player" (or "me" to indicate the current CommandSender)

- if "player" was chosen, include a player-name

- @param sender the CommandSender that requested this specific statistic*/ + @param sender the CommandSender that requested this specific statistic + @throws IllegalArgumentException if the args do not result in a valid statistic look-up*/ TextComponent getFancyStat(CommandSender sender, String[] args) throws IllegalArgumentException; /** Turns a TextComponent into its String representation. It will lose all color and style, diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/PlayerStatsAPI.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/PlayerStatsAPI.java index 26c817f..b4c4641 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/PlayerStatsAPI.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/PlayerStatsAPI.java @@ -1,35 +1,30 @@ package com.gmail.artemis.the.gr8.playerstats.api; -import com.gmail.artemis.the.gr8.playerstats.Main; -import com.gmail.artemis.the.gr8.playerstats.ThreadManager; import com.gmail.artemis.the.gr8.playerstats.models.StatRequest; import com.gmail.artemis.the.gr8.playerstats.statistic.StatManager; import net.kyori.adventure.text.TextComponent; import org.bukkit.command.CommandSender; -import org.bukkit.plugin.java.JavaPlugin; +import java.util.LinkedHashMap; + import static org.jetbrains.annotations.ApiStatus.Internal; -/** This is the implementation of the API Interface */ -public final class PlayerStatsAPI extends JavaPlugin implements PlayerStats { +/** The implementation of the API Interface */ +public final class PlayerStatsAPI implements PlayerStats { - private final Main plugin; - private static ThreadManager threadManager; private static StatFormatter statFormatter; private static StatManager statManager; @Internal - private PlayerStatsAPI(Main plugin, ThreadManager thread, StatFormatter format, StatManager stat) { - this.plugin = plugin; - threadManager = thread; + private PlayerStatsAPI(StatManager stat, StatFormatter format) { statFormatter = format; statManager = stat; } @Internal - public static PlayerStatsAPI load(Main plugin, ThreadManager threadManager, StatFormatter formatter, StatManager statManager) { - return new PlayerStatsAPI(plugin, threadManager, formatter, statManager); + public static PlayerStatsAPI load(StatManager statManager, StatFormatter statFormatter) { + return new PlayerStatsAPI(statManager, statFormatter); } @Override @@ -42,16 +37,16 @@ public final class PlayerStatsAPI extends JavaPlugin implements PlayerStats { return statFormatter.formatPlayerStat(request, stat); } case SERVER -> { - //do something async + long stat = statManager.getServerStat(request); + return statFormatter.formatServerStat(request, stat); } case TOP -> { - //also do something async + LinkedHashMap stats = statManager.getTopStats(request); + return statFormatter.formatTopStat(request, stats); } } - } else { - throw new IllegalArgumentException("This is not a valid stat-request!"); } - return null; + throw new IllegalArgumentException("This is not a valid stat-request!"); } public String componentToString(TextComponent component) { diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/RequestHandler.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/RequestGenerator.java similarity index 90% rename from src/main/java/com/gmail/artemis/the/gr8/playerstats/api/RequestHandler.java rename to src/main/java/com/gmail/artemis/the/gr8/playerstats/api/RequestGenerator.java index 912e449..09e5dec 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/RequestHandler.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/api/RequestGenerator.java @@ -3,10 +3,10 @@ package com.gmail.artemis.the.gr8.playerstats.api; import com.gmail.artemis.the.gr8.playerstats.models.StatRequest; import org.bukkit.command.CommandSender; -/** The {@link RequestHandler} will help you turn a String (such as "stat animals_bred") into a specific {@link StatRequest} +/** The {@link RequestGenerator} will help you turn a String (such as "stat animals_bred") into a specific {@link StatRequest} with all the information {@link PlayerStatsAPI} needs to work with. You'll need this StatRequest Object to get the statistic data that you want, and to format this data into a fancy Component or String, so you can output it somewhere.*/ -public interface RequestHandler { +public interface RequestGenerator { /** This will create a {@link StatRequest} from the provided args, with the requesting Player (or Console) as CommandSender. This CommandSender will receive feedback messages if the StatRequest could not be created. diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/OutputManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/OutputManager.java index d9c539a..4f36d0b 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/OutputManager.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/OutputManager.java @@ -1,6 +1,5 @@ package com.gmail.artemis.the.gr8.playerstats.msg; -import com.gmail.artemis.the.gr8.playerstats.Main; import com.gmail.artemis.the.gr8.playerstats.ShareManager; import com.gmail.artemis.the.gr8.playerstats.api.StatFormatter; import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler; @@ -33,8 +32,6 @@ import static com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage.*; (mainly to deal with the lack of hover-text, and for Bukkit consoles to make up for the lack of hex-colors).*/ public final class OutputManager implements StatFormatter { - private static volatile OutputManager instance; - private static BukkitAudiences adventure; private static ShareManager shareManager; private static MessageBuilder writer; @@ -42,27 +39,14 @@ public final class OutputManager implements StatFormatter { private static EnumMap> standardMessages; - private OutputManager(ConfigHandler config) { - adventure = Main.adventure(); - shareManager = ShareManager.getInstance(config); + public OutputManager(BukkitAudiences adventure, ConfigHandler config, ShareManager shareManager) { + OutputManager.adventure = adventure; + OutputManager.shareManager = shareManager; getMessageWriters(config); prepareFunctions(); } - public static OutputManager getInstance(ConfigHandler config) { - OutputManager outputManager = instance; - if (outputManager != null) { - return outputManager; - } - synchronized (OutputManager.class) { - if (instance == null) { - instance = new OutputManager(config); - } - return instance; - } - } - public void updateMessageWriters(ConfigHandler config) { getMessageWriters(config); } diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java index d4099bc..b8c60e3 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java @@ -1,5 +1,6 @@ package com.gmail.artemis.the.gr8.playerstats.reload; +import com.gmail.artemis.the.gr8.playerstats.Main; import com.gmail.artemis.the.gr8.playerstats.ShareManager; import com.gmail.artemis.the.gr8.playerstats.ThreadManager; import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler; @@ -7,6 +8,7 @@ import com.gmail.artemis.the.gr8.playerstats.enums.DebugLevel; import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage; import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager; import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread; +import com.gmail.artemis.the.gr8.playerstats.statistic.StatManager; import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import org.bukkit.Bukkit; @@ -25,10 +27,10 @@ public class ReloadThread extends Thread { private static ConfigHandler config; private static OutputManager outputManager; - private final OfflinePlayerHandler offlinePlayerHandler; - private static ShareManager shareManager; + private final OfflinePlayerHandler offlinePlayerHandler; + private final int reloadThreadID; private final StatThread statThread; @@ -39,7 +41,7 @@ public class ReloadThread extends Thread { outputManager = m; offlinePlayerHandler = o; - shareManager = ShareManager.getInstance(c); + shareManager = Main.getShareManager(); reloadThreadID = ID; statThread = s; @@ -52,7 +54,8 @@ public class ReloadThread extends Thread { /** This method will perform a series of tasks. If a {@link StatThread} is still running, it will join the statThread and wait for it to finish. Then, it will reload the config, update the offlinePlayerList in the {@link OfflinePlayerHandler}, update the {@link DebugLevel}, - update the share-settings in {@link ShareManager} and update the MessageBuilders in the {@link OutputManager}.*/ + update the share-settings in {@link ShareManager} and topListSize-settings in {@link StatManager}, + and update the MessageBuilders in the {@link OutputManager}.*/ @Override public void run() { long time = System.currentTimeMillis(); @@ -88,6 +91,7 @@ public class ReloadThread extends Thread { outputManager.updateMessageWriters(config); offlinePlayerHandler.updateOfflinePlayerList(loadOfflinePlayers()); shareManager.updateSettings(config); + StatManager.updateSettings(config.getTopListMaxSize()); } private ConcurrentHashMap loadOfflinePlayers() { diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatAction.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatAction.java index 7bd6049..25b0c13 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatAction.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatAction.java @@ -9,17 +9,17 @@ import org.bukkit.OfflinePlayer; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.RecursiveAction; +import java.util.concurrent.RecursiveTask; /** The action that is executed when a stat-command is triggered. */ -public final class StatAction extends RecursiveAction { +public final class StatAction extends RecursiveTask> { private static int threshold; private final OfflinePlayerHandler offlinePlayerHandler; private final ImmutableList playerNames; private final StatRequest request; - private final ConcurrentHashMap playerStats; + private final ConcurrentHashMap allStats; /** * Gets the statistic numbers for all players whose name is on the list, puts them in a ConcurrentHashMap @@ -27,34 +27,36 @@ public final class StatAction extends RecursiveAction { * @param offlinePlayerHandler the OfflinePlayerHandler to convert playerNames into Players * @param playerNames ImmutableList of playerNames for players that should be included in stat calculations * @param statRequest a validated statRequest - * @param playerStats the ConcurrentHashMap to put the results on + * @param allStats the ConcurrentHashMap to put the results on */ - public StatAction(OfflinePlayerHandler offlinePlayerHandler, ImmutableList playerNames, StatRequest statRequest, ConcurrentHashMap playerStats) { + public StatAction(OfflinePlayerHandler offlinePlayerHandler, ImmutableList playerNames, StatRequest statRequest, ConcurrentHashMap allStats) { threshold = ThreadManager.getTaskThreshold(); this.offlinePlayerHandler = offlinePlayerHandler; this.playerNames = playerNames; this.request = statRequest; - this.playerStats = playerStats; + this.allStats = allStats; MyLogger.subActionCreated(Thread.currentThread().getName()); } @Override - protected void compute() { + protected ConcurrentHashMap compute() { if (playerNames.size() < threshold) { - getStatsDirectly(); + return getStatsDirectly(); } else { - final StatAction subTask1 = new StatAction(offlinePlayerHandler, playerNames.subList(0, playerNames.size()/2), request, playerStats); - final StatAction subTask2 = new StatAction(offlinePlayerHandler, playerNames.subList(playerNames.size()/2, playerNames.size()), request, playerStats); + final StatAction subTask1 = new StatAction(offlinePlayerHandler, playerNames.subList(0, playerNames.size()/2), request, allStats); + final StatAction subTask2 = new StatAction(offlinePlayerHandler, playerNames.subList(playerNames.size()/2, playerNames.size()), request, allStats); //queue and compute all subtasks in the right order - invokeAll(subTask1, subTask2); + subTask1.fork(); + subTask2.compute(); + return subTask1.join(); } } - private void getStatsDirectly() { + private ConcurrentHashMap getStatsDirectly() { Iterator iterator = playerNames.iterator(); if (iterator.hasNext()) { do { @@ -70,10 +72,11 @@ public final class StatAction extends RecursiveAction { case ITEM -> statistic = player.getStatistic(request.getStatistic(), request.getItem()); } if (statistic > 0) { - playerStats.put(playerName, statistic); + allStats.put(playerName, statistic); } } } while (iterator.hasNext()); } + return allStats; } } \ No newline at end of file diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatManager.java index 9e97181..aed05eb 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatManager.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatManager.java @@ -1,12 +1,16 @@ package com.gmail.artemis.the.gr8.playerstats.statistic; -import com.gmail.artemis.the.gr8.playerstats.api.RequestHandler; +import com.gmail.artemis.the.gr8.playerstats.ThreadManager; +import com.gmail.artemis.the.gr8.playerstats.api.RequestGenerator; +import com.gmail.artemis.the.gr8.playerstats.api.StatGetter; import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage; import com.gmail.artemis.the.gr8.playerstats.enums.Target; import com.gmail.artemis.the.gr8.playerstats.models.StatRequest; import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager; import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; +import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; +import com.google.common.collect.ImmutableList; import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.Statistic; @@ -14,30 +18,27 @@ import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; -public final class StatManager implements RequestHandler { +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; +import java.util.stream.Collectors; - private static volatile StatManager instance; +public final class StatManager implements RequestGenerator, StatGetter { private final OfflinePlayerHandler offlinePlayerHandler; private static OutputManager outputManager; + private static int topListMaxSize; - private StatManager(OutputManager output, OfflinePlayerHandler offlinePlayerHandler) { + public StatManager(OutputManager outputManager, OfflinePlayerHandler offlinePlayerHandler, int topListMaxSize) { this.offlinePlayerHandler = offlinePlayerHandler; - outputManager = output; + StatManager.outputManager = outputManager; + StatManager.topListMaxSize = topListMaxSize; } - public static StatManager getInstance(OutputManager outputManager, OfflinePlayerHandler offlinePlayerHandler) { - StatManager statManager = instance; - if (statManager != null) { - return statManager; - } - synchronized (StatManager.class) { - if (instance == null) { - instance = new StatManager(outputManager, offlinePlayerHandler); - } - return instance; - } + public static void updateSettings(int topListMaxSize) { + StatManager.topListMaxSize = topListMaxSize; } public StatRequest generateRequest(CommandSender sender, String[] args) { @@ -174,4 +175,54 @@ public final class StatManager implements RequestHandler { } return 0; } + + public LinkedHashMap getTopStats(StatRequest request) { + return getAllStatsAsync(request).entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(topListMaxSize) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + } + + public long getServerStat(StatRequest request) { + List numbers = getAllStatsAsync(request) + .values() + .parallelStream() + .toList(); + return numbers.parallelStream().mapToLong(Integer::longValue).sum(); + } + + /** Invokes a bunch of worker pool threads to divide and conquer (get the statistics for all players + that are stored in the {@link OfflinePlayerHandler}) */ + public @NotNull ConcurrentHashMap getAllStatsAsync(StatRequest request) { + long time = System.currentTimeMillis(); + + ForkJoinPool commonPool = ForkJoinPool.commonPool(); + ConcurrentHashMap allStats; + + try { + allStats = commonPool.invoke(getStatTask(request)); + } catch (ConcurrentModificationException e) { + MyLogger.logMsg("The request could not be executed due to a ConcurrentModificationException. " + + "This likely happened because Bukkit hasn't fully initialized all player-data yet. " + + "Try again and it should be fine!", true); + throw new ConcurrentModificationException(e.toString()); + } + + MyLogger.actionFinished(2); + ThreadManager.recordCalcTime(System.currentTimeMillis() - time); + MyLogger.logTimeTaken("StatThread", "calculated all stats", time); + + return allStats; + } + + private StatAction getStatTask(StatRequest request) { + int size = offlinePlayerHandler.getOfflinePlayerCount() != 0 ? offlinePlayerHandler.getOfflinePlayerCount() : 16; + ConcurrentHashMap allStats = new ConcurrentHashMap<>(size); + ImmutableList playerNames = ImmutableList.copyOf(offlinePlayerHandler.getOfflinePlayerNames()); + + StatAction task = new StatAction(offlinePlayerHandler, playerNames, request, allStats); + MyLogger.actionCreated(playerNames.size()); + + return task; + } } \ No newline at end of file diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java index 4b5f779..832c78b 100644 --- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java +++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java @@ -9,33 +9,23 @@ import com.gmail.artemis.the.gr8.playerstats.ThreadManager; import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler; import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; -import com.google.common.collect.ImmutableList; import net.kyori.adventure.text.TextComponent; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ForkJoinPool; -import java.util.stream.Collectors; /** The Thread that is in charge of getting and calculating statistics.*/ public class StatThread extends Thread { - private static ConfigHandler config; private static OutputManager outputManager; private static StatManager statManager; - private final OfflinePlayerHandler offlinePlayerHandler; - private final ReloadThread reloadThread; private final StatRequest request; - public StatThread(ConfigHandler c, OutputManager m, StatManager t, OfflinePlayerHandler o, int ID, StatRequest s, @Nullable ReloadThread r) { - config = c; + public StatThread(OutputManager m, StatManager t, int ID, StatRequest s, @Nullable ReloadThread r) { outputManager = m; statManager = t; - offlinePlayerHandler = o; reloadThread = r; request = s; @@ -72,8 +62,8 @@ public class StatThread extends Thread { try { TextComponent statResult = switch (selection) { case PLAYER -> outputManager.formatPlayerStat(request, statManager.getPlayerStat(request)); - case TOP -> outputManager.formatTopStat(request, getTopStats()); - case SERVER -> outputManager.formatServerStat(request, getServerStat()); + case TOP -> outputManager.formatTopStat(request, statManager.getTopStats(request)); + case SERVER -> outputManager.formatServerStat(request, statManager.getServerStat(request)); }; outputManager.sendToCommandSender(request.getCommandSender(), statResult); } @@ -83,43 +73,4 @@ public class StatThread extends Thread { } } } - - private LinkedHashMap getTopStats() throws ConcurrentModificationException { - return getAllStats().entrySet().stream() - .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) - .limit(config.getTopListMaxSize()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); - } - - private long getServerStat() { - List numbers = getAllStats().values().parallelStream().toList(); - return numbers.parallelStream().mapToLong(Integer::longValue).sum(); - } - - //invokes a bunch of worker pool threads to divide and conquer (get the statistics for all players in the list) - private @NotNull ConcurrentHashMap getAllStats() throws ConcurrentModificationException { - long time = System.currentTimeMillis(); - - int size = offlinePlayerHandler.getOfflinePlayerCount() != 0 ? offlinePlayerHandler.getOfflinePlayerCount() : 16; - ConcurrentHashMap playerStats = new ConcurrentHashMap<>(size); - ImmutableList playerNames = ImmutableList.copyOf(offlinePlayerHandler.getOfflinePlayerNames()); - - StatAction task = new StatAction(offlinePlayerHandler, playerNames, request, playerStats); - MyLogger.actionCreated(playerNames.size()); - ForkJoinPool commonPool = ForkJoinPool.commonPool(); - - try { - commonPool.invoke(task); - } catch (ConcurrentModificationException e) { - MyLogger.logMsg("The request could not be executed due to a ConcurrentModificationException. " + - "This likely happened because Bukkit hasn't fully initialized all player-data yet. " + - "Try again and it should be fine!", true); - throw new ConcurrentModificationException(e.toString()); - } - - MyLogger.actionFinished(2); - ThreadManager.recordCalcTime(System.currentTimeMillis() - time); - MyLogger.logTimeTaken("StatThread", "calculated all stats", time); - - return playerStats; - } } \ No newline at end of file