Reworked InternalStatRequest and the other classes dealing with StatRequests (#114)

This commit is contained in:
Artemis-the-gr8 2022-10-11 13:08:11 +02:00
parent b46a25d23f
commit 31713007f5
19 changed files with 197 additions and 217 deletions

View File

@ -1,17 +1,16 @@
package com.artemis.the.gr8.playerstats; package com.artemis.the.gr8.playerstats;
import com.artemis.the.gr8.playerstats.api.PlayerStats; import com.artemis.the.gr8.playerstats.api.PlayerStats;
import com.artemis.the.gr8.playerstats.api.PlayerStatsImpl;
import com.artemis.the.gr8.playerstats.msg.OutputManager; import com.artemis.the.gr8.playerstats.msg.OutputManager;
import com.artemis.the.gr8.playerstats.api.PlayerStatsAPI;
import com.artemis.the.gr8.playerstats.commands.ReloadCommand; import com.artemis.the.gr8.playerstats.commands.ReloadCommand;
import com.artemis.the.gr8.playerstats.commands.ShareCommand; import com.artemis.the.gr8.playerstats.commands.ShareCommand;
import com.artemis.the.gr8.playerstats.commands.StatCommand; import com.artemis.the.gr8.playerstats.commands.StatCommand;
import com.artemis.the.gr8.playerstats.commands.TabCompleter; import com.artemis.the.gr8.playerstats.commands.TabCompleter;
import com.artemis.the.gr8.playerstats.config.ConfigHandler; import com.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.artemis.the.gr8.playerstats.listeners.JoinListener; import com.artemis.the.gr8.playerstats.listeners.JoinListener;
import com.artemis.the.gr8.playerstats.msg.MessageBuilder;
import com.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler; import com.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler;
import com.artemis.the.gr8.playerstats.statistic.StatCalculator; import com.artemis.the.gr8.playerstats.statistic.RequestProcessor;
import com.artemis.the.gr8.playerstats.utils.EnumHandler; import com.artemis.the.gr8.playerstats.utils.EnumHandler;
import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderAPIPlugin;
@ -35,14 +34,14 @@ public final class Main extends JavaPlugin {
private static BukkitAudiences adventure; private static BukkitAudiences adventure;
private static ConfigHandler config; private static ConfigHandler config;
private static ThreadManager threadManager;
private static LanguageKeyHandler languageKeyHandler; private static LanguageKeyHandler languageKeyHandler;
private static OfflinePlayerHandler offlinePlayerHandler; private static OfflinePlayerHandler offlinePlayerHandler;
private static EnumHandler enumHandler; private static EnumHandler enumHandler;
private static OutputManager outputManager; private static OutputManager outputManager;
private static ShareManager shareManager; private static ShareManager shareManager;
private static StatCalculator statCalculator; private static RequestProcessor requestProcessor;
private static ThreadManager threadManager;
private static PlayerStats playerStatsAPI; private static PlayerStats playerStatsAPI;
@ -110,11 +109,11 @@ public final class Main extends JavaPlugin {
return playerStatsAPI; return playerStatsAPI;
} }
public static @NotNull OutputManager getOutputManager() throws IllegalStateException { public static @NotNull RequestProcessor getRequestProcessor() throws IllegalStateException {
if (outputManager == null) { if (requestProcessor == null) {
throw new IllegalStateException("PlayerStats does not seem to be loaded!"); throw new IllegalStateException("PlayerStats does not seem to be loaded!");
} }
return outputManager; return requestProcessor;
} }
/** /**
@ -152,29 +151,21 @@ public final class Main extends JavaPlugin {
return enumHandler; return enumHandler;
} }
public static @NotNull StatCalculator getStatCalculator() {
if (statCalculator == null) {
statCalculator = new StatCalculator(getOfflinePlayerHandler());
}
return statCalculator;
}
private void initializeMainClasses() { private void initializeMainClasses() {
pluginInstance = this; pluginInstance = this;
adventure = BukkitAudiences.create(this); adventure = BukkitAudiences.create(this);
config = new ConfigHandler();
enumHandler = new EnumHandler(); enumHandler = new EnumHandler();
languageKeyHandler = new LanguageKeyHandler(); languageKeyHandler = new LanguageKeyHandler();
config = new ConfigHandler();
offlinePlayerHandler = new OfflinePlayerHandler(config); offlinePlayerHandler = new OfflinePlayerHandler(config);
shareManager = new ShareManager(config); shareManager = new ShareManager(config);
statCalculator = new StatCalculator(offlinePlayerHandler);
outputManager = new OutputManager(adventure, config, shareManager);
threadManager = new ThreadManager(config, statCalculator, outputManager);
MessageBuilder apiMessageBuilder = MessageBuilder.defaultBuilder(config); outputManager = new OutputManager(adventure, config, shareManager);
playerStatsAPI = new PlayerStatsAPI(apiMessageBuilder, offlinePlayerHandler); requestProcessor = new RequestProcessor(offlinePlayerHandler, outputManager);
threadManager = new ThreadManager(config, requestProcessor, outputManager);
playerStatsAPI = new PlayerStatsImpl(outputManager, offlinePlayerHandler);
} }
private void setupMetrics() { private void setupMetrics() {

View File

@ -4,11 +4,12 @@ import com.artemis.the.gr8.playerstats.msg.OutputManager;
import com.artemis.the.gr8.playerstats.config.ConfigHandler; import com.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.artemis.the.gr8.playerstats.enums.StandardMessage; import com.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.artemis.the.gr8.playerstats.reload.ReloadThread; import com.artemis.the.gr8.playerstats.reload.ReloadThread;
import com.artemis.the.gr8.playerstats.statistic.StatCalculator; import com.artemis.the.gr8.playerstats.statistic.RequestProcessor;
import com.artemis.the.gr8.playerstats.statistic.StatThread; import com.artemis.the.gr8.playerstats.statistic.StatThread;
import com.artemis.the.gr8.playerstats.statistic.request.StatRequest; import com.artemis.the.gr8.playerstats.statistic.request.StatRequest;
import com.artemis.the.gr8.playerstats.utils.MyLogger; import com.artemis.the.gr8.playerstats.utils.MyLogger;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap; import java.util.HashMap;
@ -29,17 +30,17 @@ public final class ThreadManager {
private static ConfigHandler config; private static ConfigHandler config;
private static OutputManager outputManager; private static OutputManager outputManager;
private static StatCalculator statCalculator; private static RequestProcessor requestProcessor;
private ReloadThread lastActiveReloadThread; private ReloadThread activatedReloadThread;
private StatThread lastActiveStatThread; private StatThread activatedStatThread;
private final HashMap<String, Thread> statThreads; private final HashMap<String, Thread> statThreads;
private static long lastRecordedCalcTime; private static long lastRecordedCalcTime;
public ThreadManager(ConfigHandler config, StatCalculator statCalculator, OutputManager outputManager) { public ThreadManager(ConfigHandler config, RequestProcessor requestProcessor, OutputManager outputManager) {
ThreadManager.config = config; ThreadManager.config = config;
ThreadManager.outputManager = outputManager; ThreadManager.outputManager = outputManager;
ThreadManager.statCalculator = statCalculator; ThreadManager.requestProcessor = requestProcessor;
statThreads = new HashMap<>(); statThreads = new HashMap<>();
statThreadID = 0; statThreadID = 0;
@ -52,25 +53,26 @@ public final class ThreadManager {
} }
public void startReloadThread(CommandSender sender) { public void startReloadThread(CommandSender sender) {
if (lastActiveReloadThread == null || !lastActiveReloadThread.isAlive()) { if (activatedReloadThread == null || !activatedReloadThread.isAlive()) {
reloadThreadID += 1; reloadThreadID += 1;
lastActiveReloadThread = new ReloadThread(config, outputManager, reloadThreadID, lastActiveStatThread, sender); activatedReloadThread = new ReloadThread(config, outputManager, reloadThreadID, activatedStatThread, sender);
lastActiveReloadThread.start(); activatedReloadThread.start();
} }
else { else {
MyLogger.logLowLevelMsg("Another reloadThread is already running! (" + lastActiveReloadThread.getName() + ")"); MyLogger.logLowLevelMsg("Another reloadThread is already running! (" + activatedReloadThread.getName() + ")");
} }
} }
public void startStatThread(StatRequest<?> request) { public void startStatThread(@NotNull StatRequest<?> request) {
statThreadID += 1; statThreadID += 1;
String cmdSender = request.getSettings().getCommandSender().getName(); CommandSender sender = request.getSettings().getCommandSender();
if (config.limitStatRequests() && statThreads.containsKey(sender.getName())) {
Thread runningThread = statThreads.get(sender.getName());
if (config.limitStatRequests() && statThreads.containsKey(cmdSender)) {
Thread runningThread = statThreads.get(cmdSender);
if (runningThread.isAlive()) { if (runningThread.isAlive()) {
outputManager.sendFeedbackMsg(request.getSettings().getCommandSender(), StandardMessage.REQUEST_ALREADY_RUNNING); outputManager.sendFeedbackMsg(sender, StandardMessage.REQUEST_ALREADY_RUNNING);
} else { } else {
startNewStatThread(request); startNewStatThread(request);
} }
@ -95,9 +97,9 @@ public final class ThreadManager {
return lastRecordedCalcTime; return lastRecordedCalcTime;
} }
private void startNewStatThread(StatRequest<?> requestSettings) { private void startNewStatThread(StatRequest<?> request) {
lastActiveStatThread = new StatThread(outputManager, statCalculator, statThreadID, requestSettings, lastActiveReloadThread); activatedStatThread = new StatThread(outputManager, requestProcessor, statThreadID, request, activatedReloadThread);
statThreads.put(requestSettings.getSettings().getCommandSender().getName(), lastActiveStatThread); statThreads.put(request.getSettings().getCommandSender().getName(), activatedStatThread);
lastActiveStatThread.start(); activatedStatThread.start();
} }
} }

View File

@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
* The outgoing API that represents the core functionality of PlayerStats! * The outgoing API that represents the core functionality of PlayerStats!
* *
* <p> To work with it, you'll need to call PlayerStats.{@link #getAPI()} and get an instance of * <p> To work with it, you'll need to call PlayerStats.{@link #getAPI()} and get an instance of
* {@link PlayerStatsAPI}. You can then use this object to access any of the further methods. * {@link PlayerStatsImpl}. You can then use this object to access any of the further methods.
* *
* <p> Since calculating a top or server statistics can take some time, I strongly * <p> Since calculating a top or server statistics can take some time, I strongly
* encourage you to call {@link StatRequest#execute()} asynchronously. * encourage you to call {@link StatRequest#execute()} asynchronously.
@ -17,11 +17,11 @@ import org.jetbrains.annotations.NotNull;
* and this can severely impact server performance. * and this can severely impact server performance.
* *
* @see StatManager * @see StatManager
* @see ApiFormatter * @see StatFormatter
*/ */
public interface PlayerStats { public interface PlayerStats {
/** Gets an instance of the {@link PlayerStatsAPI}. /** Gets an instance of the {@link PlayerStatsImpl}.
* @return the PlayerStats API * @return the PlayerStats API
* @throws IllegalStateException if PlayerStats is not loaded on the server when this method is called*/ * @throws IllegalStateException if PlayerStats is not loaded on the server when this method is called*/
@ -31,13 +31,13 @@ public interface PlayerStats {
} }
/** /**
* Gets the current version of PlayerStatsAPI. * Gets the current version of PlayerStatsImpl.
* Use this method to ensure the correct version of * Use this method to ensure the correct version of
* PlayerStats is running on the server before * PlayerStats is running on the server before
* accessing further API methods, to prevent * accessing further API methods, to prevent
* <code>ClassDefNotFoundExceptions</code>. * <code>ClassDefNotFoundExceptions</code>.
* *
* @return the version of PlayerStatsAPI present on the server * @return the version of PlayerStatsImpl present on the server
*/ */
default String getVersion() { default String getVersion() {
return "1.8"; return "1.8";
@ -45,5 +45,5 @@ public interface PlayerStats {
StatManager getStatManager(); StatManager getStatManager();
ApiFormatter getFormatter(); StatFormatter getFormatter();
} }

View File

@ -1,25 +1,28 @@
package com.artemis.the.gr8.playerstats.api; package com.artemis.the.gr8.playerstats.api;
import com.artemis.the.gr8.playerstats.msg.OutputManager;
import com.artemis.the.gr8.playerstats.statistic.request.*; import com.artemis.the.gr8.playerstats.statistic.request.*;
import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import java.util.LinkedHashMap;
import static org.jetbrains.annotations.ApiStatus.Internal; import static org.jetbrains.annotations.ApiStatus.Internal;
/** The implementation of the API Interface */ /** The implementation of the API Interface */
public final class PlayerStatsAPI implements PlayerStats, StatManager { public final class PlayerStatsImpl implements PlayerStats, StatManager {
private final OfflinePlayerHandler offlinePlayerHandler; private final OfflinePlayerHandler offlinePlayerHandler;
private static ApiFormatter apiFormatter; private static OutputManager outputManager;
@Internal @Internal
public PlayerStatsAPI(ApiFormatter formatter, OfflinePlayerHandler offlinePlayers) { public PlayerStatsImpl(OutputManager outputManager, OfflinePlayerHandler offlinePlayers) {
apiFormatter = formatter; PlayerStatsImpl.outputManager = outputManager;
offlinePlayerHandler = offlinePlayers; offlinePlayerHandler = offlinePlayers;
} }
@Override @Override
public ApiFormatter getFormatter() { public StatFormatter getFormatter() {
return apiFormatter; return outputManager.getCurrentMainMessageBuilder();
} }
@Override @Override
@ -28,22 +31,22 @@ public final class PlayerStatsAPI implements PlayerStats, StatManager {
} }
@Override @Override
public PlayerStatRequest playerStatRequest(String playerName) { public RequestGenerator<Integer> playerStatRequest(String playerName) {
return new PlayerStatRequest(playerName); return new PlayerStatRequest(playerName);
} }
@Override @Override
public ServerStatRequest serverStatRequest() { public RequestGenerator<Long> serverStatRequest() {
return new ServerStatRequest(); return new ServerStatRequest();
} }
@Override @Override
public TopStatRequest topStatRequest(int topListSize) { public RequestGenerator<LinkedHashMap<String, Integer>> topStatRequest(int topListSize) {
return new TopStatRequest(topListSize); return new TopStatRequest(topListSize);
} }
@Override @Override
public TopStatRequest totalTopStatRequest() { public RequestGenerator<LinkedHashMap<String, Integer>> totalTopStatRequest() {
int playerCount = offlinePlayerHandler.getOfflinePlayerCount(); int playerCount = offlinePlayerHandler.getOfflinePlayerCount();
return topStatRequest(playerCount); return topStatRequest(playerCount);
} }

View File

@ -1,6 +1,6 @@
package com.artemis.the.gr8.playerstats.api; package com.artemis.the.gr8.playerstats.api;
import com.artemis.the.gr8.playerstats.statistic.StatCalculator; import com.artemis.the.gr8.playerstats.statistic.RequestProcessor;
import com.artemis.the.gr8.playerstats.statistic.request.StatRequest; import com.artemis.the.gr8.playerstats.statistic.request.StatRequest;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Statistic; import org.bukkit.Statistic;
@ -9,7 +9,7 @@ import org.jetbrains.annotations.NotNull;
/** /**
* Creates an executable {@link StatRequest}. This Request holds all * Creates an executable {@link StatRequest}. This Request holds all
* the information PlayerStats needs to work with, and is used by the {@link StatCalculator} * the information PlayerStats needs to work with, and is used by the {@link RequestProcessor}
* to get the desired statistic data. * to get the desired statistic data.
*/ */
public interface RequestGenerator<T> { public interface RequestGenerator<T> {

View File

@ -16,7 +16,7 @@ import org.jetbrains.annotations.Nullable;
* @see StatResult * @see StatResult
*/ */
public interface ApiFormatter { public interface StatFormatter {
/** /**
* Turns a TextComponent into its String representation. This method is equipped * Turns a TextComponent into its String representation. This method is equipped

View File

@ -5,7 +5,8 @@ import com.artemis.the.gr8.playerstats.statistic.request.StatRequest;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
/** /**
* Turns user input into a {@link StatRequest} that can be used to get statistic data * Turns user input into a {@link StatRequest} that can be
* used to get statistic data.
*/ */
public interface StatManager { public interface StatManager {

View File

@ -5,6 +5,7 @@ import com.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.artemis.the.gr8.playerstats.enums.Target; import com.artemis.the.gr8.playerstats.enums.Target;
import com.artemis.the.gr8.playerstats.msg.OutputManager; import com.artemis.the.gr8.playerstats.msg.OutputManager;
import com.artemis.the.gr8.playerstats.statistic.request.*; import com.artemis.the.gr8.playerstats.statistic.request.*;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Statistic; import org.bukkit.Statistic;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
@ -31,11 +32,11 @@ public class StatCommand implements CommandExecutor {
outputManager.sendExamples(sender); outputManager.sendExamples(sender);
} }
else { else {
InternalStatRequest request = new InternalStatRequest(sender, args); StatRequest<TextComponent> request = new InternalStatRequest(sender, args);
if (request.isValid()) { if (request.isValid()) {
threadManager.startStatThread(request); threadManager.startStatThread(request);
} else { } else {
sendFeedback(request); sendFeedback(sender, request);
return false; return false;
} }
} }
@ -52,11 +53,11 @@ public class StatCommand implements CommandExecutor {
* <li>If the <code>target</code> is Player, is a valid <code>playerName</code> provided? * <li>If the <code>target</code> is Player, is a valid <code>playerName</code> provided?
* </ul> * </ul>
* *
* @param sender the CommandSender to send feedback to
* @param request the StatRequest to give feedback on * @param request the StatRequest to give feedback on
*/ */
private void sendFeedback(InternalStatRequest request) { private void sendFeedback(CommandSender sender, StatRequest<?> request) {
StatRequest.Settings settings = request.getSettings(); StatRequest.Settings settings = request.getSettings();
CommandSender sender = settings.getCommandSender();
if (settings.getStatistic() == null) { if (settings.getStatistic() == null) {
outputManager.sendFeedbackMsg(sender, StandardMessage.MISSING_STAT_NAME); outputManager.sendFeedbackMsg(sender, StandardMessage.MISSING_STAT_NAME);

View File

@ -1,7 +1,7 @@
package com.artemis.the.gr8.playerstats.msg; package com.artemis.the.gr8.playerstats.msg;
import com.artemis.the.gr8.playerstats.Main; import com.artemis.the.gr8.playerstats.Main;
import com.artemis.the.gr8.playerstats.api.ApiFormatter; import com.artemis.the.gr8.playerstats.api.StatFormatter;
import com.artemis.the.gr8.playerstats.msg.components.ComponentFactory; import com.artemis.the.gr8.playerstats.msg.components.ComponentFactory;
import com.artemis.the.gr8.playerstats.msg.components.ExampleMessage; import com.artemis.the.gr8.playerstats.msg.components.ExampleMessage;
import com.artemis.the.gr8.playerstats.msg.components.HelpMessage; import com.artemis.the.gr8.playerstats.msg.components.HelpMessage;
@ -39,7 +39,7 @@ import static net.kyori.adventure.text.Component.*;
* @see PrideComponentFactory * @see PrideComponentFactory
* @see BukkitConsoleComponentFactory * @see BukkitConsoleComponentFactory
*/ */
public final class MessageBuilder implements ApiFormatter { public final class MessageBuilder implements StatFormatter {
private static ConfigHandler config; private static ConfigHandler config;
private boolean useHoverText; private boolean useHoverText;

View File

@ -54,6 +54,12 @@ public final class OutputManager {
getMessageBuilders(); getMessageBuilders();
} }
public MessageBuilder getCurrentMainMessageBuilder() {
return messageBuilder;
}
//TODO separate formatting from internal saving for sharing
/** @return a TextComponent with the following parts: /** @return a TextComponent with the following parts:
* <br>[player-name]: [number] [stat-name] {sub-stat-name} * <br>[player-name]: [number] [stat-name] {sub-stat-name}
*/ */

View File

@ -5,7 +5,7 @@ import com.artemis.the.gr8.playerstats.ShareManager;
import com.artemis.the.gr8.playerstats.enums.StandardMessage; import com.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.artemis.the.gr8.playerstats.msg.OutputManager; import com.artemis.the.gr8.playerstats.msg.OutputManager;
import com.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler; import com.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler;
import com.artemis.the.gr8.playerstats.statistic.StatCalculator; import com.artemis.the.gr8.playerstats.statistic.RequestProcessor;
import com.artemis.the.gr8.playerstats.statistic.StatThread; import com.artemis.the.gr8.playerstats.statistic.StatThread;
import com.artemis.the.gr8.playerstats.utils.MyLogger; import com.artemis.the.gr8.playerstats.utils.MyLogger;
import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
@ -40,7 +40,7 @@ public final class ReloadThread extends Thread {
* Then, it will reload the config, update the {@link LanguageKeyHandler}, * Then, it will reload the config, update the {@link LanguageKeyHandler},
* the {@link OfflinePlayerHandler}, the {@link DebugLevel}, update * the {@link OfflinePlayerHandler}, the {@link DebugLevel}, update
* the share-settings in {@link ShareManager} and topListSize-settings * the share-settings in {@link ShareManager} and topListSize-settings
* in {@link StatCalculator}, and update the MessageBuilders in the * in {@link RequestProcessor}, and update the MessageBuilders in the
* {@link OutputManager}. * {@link OutputManager}.
*/ */
@Override @Override

View File

@ -1,27 +1,68 @@
package com.artemis.the.gr8.playerstats.statistic; package com.artemis.the.gr8.playerstats.statistic;
import com.artemis.the.gr8.playerstats.ThreadManager; import com.artemis.the.gr8.playerstats.ThreadManager;
import com.artemis.the.gr8.playerstats.msg.OutputManager;
import com.artemis.the.gr8.playerstats.msg.components.ComponentUtils;
import com.artemis.the.gr8.playerstats.statistic.request.StatRequest; import com.artemis.the.gr8.playerstats.statistic.request.StatRequest;
import com.artemis.the.gr8.playerstats.statistic.result.StatResult;
import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import com.artemis.the.gr8.playerstats.utils.MyLogger; import com.artemis.the.gr8.playerstats.utils.MyLogger;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.*; import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public final class StatCalculator { public final class RequestProcessor {
private final OfflinePlayerHandler offlinePlayerHandler; private final OfflinePlayerHandler offlinePlayerHandler;
private static OutputManager outputManager;
public StatCalculator(OfflinePlayerHandler offlinePlayerHandler) { public RequestProcessor(OfflinePlayerHandler offlinePlayerHandler, OutputManager outputManager) {
this.offlinePlayerHandler = offlinePlayerHandler; this.offlinePlayerHandler = offlinePlayerHandler;
RequestProcessor.outputManager = outputManager;
} }
public int getPlayerStat(StatRequest.Settings requestSettings) { public @NotNull StatResult<TextComponent> getInternalResult(@NotNull StatRequest.Settings requestSettings) {
StatResult<?> result = switch (requestSettings.getTarget()) {
case PLAYER -> getPlayerResult(requestSettings);
case SERVER -> getServerResult(requestSettings);
case TOP -> getTopResult(requestSettings);
};
return new StatResult<>(result.formattedComponent(), result.formattedComponent(), result.formattedString());
}
public @NotNull StatResult<Integer> getPlayerResult(StatRequest.Settings requestSettings) {
int stat = getPlayerStat(requestSettings);
TextComponent result = outputManager.formatAndSavePlayerStat(requestSettings, stat);
String serializedResult = ComponentUtils.getTranslatableComponentSerializer().serialize(result);
return new StatResult<>(stat, result, serializedResult);
}
public @NotNull StatResult<Long> getServerResult(StatRequest.Settings requestSettings) {
long stat = getServerStat(requestSettings);
TextComponent result = outputManager.formatAndSaveServerStat(requestSettings, stat);
String serializedResult = ComponentUtils.getTranslatableComponentSerializer().serialize(result);
return new StatResult<>(stat, result, serializedResult);
}
public @NotNull StatResult<LinkedHashMap<String, Integer>> getTopResult(StatRequest.Settings requestSettings) {
LinkedHashMap<String, Integer> stats = getTopStats(requestSettings);
TextComponent result = outputManager.formatAndSaveTopStat(requestSettings, stats);
String serializedResult = ComponentUtils.getTranslatableComponentSerializer().serialize(result);
return new StatResult<>(stats, result, serializedResult);
}
private int getPlayerStat(@NotNull StatRequest.Settings requestSettings) {
OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(requestSettings.getPlayerName()); OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(requestSettings.getPlayerName());
return switch (requestSettings.getStatistic().getType()) { return switch (requestSettings.getStatistic().getType()) {
case UNTYPED -> player.getStatistic(requestSettings.getStatistic()); case UNTYPED -> player.getStatistic(requestSettings.getStatistic());
@ -31,14 +72,7 @@ public final class StatCalculator {
}; };
} }
public LinkedHashMap<String, Integer> getTopStats(StatRequest.Settings requestSettings) { private long getServerStat(StatRequest.Settings requestSettings) {
return getAllStatsAsync(requestSettings).entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(requestSettings.getTopListSize())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}
public long getServerStat(StatRequest.Settings requestSettings) {
List<Integer> numbers = getAllStatsAsync(requestSettings) List<Integer> numbers = getAllStatsAsync(requestSettings)
.values() .values()
.parallelStream() .parallelStream()
@ -46,6 +80,13 @@ public final class StatCalculator {
return numbers.parallelStream().mapToLong(Integer::longValue).sum(); return numbers.parallelStream().mapToLong(Integer::longValue).sum();
} }
private LinkedHashMap<String, Integer> getTopStats(StatRequest.Settings requestSettings) {
return getAllStatsAsync(requestSettings).entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(requestSettings.getTopListSize())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}
/** /**
* Invokes a bunch of worker pool threads to get the statistics for * Invokes a bunch of worker pool threads to get the statistics for
* all players that are stored in the {@link OfflinePlayerHandler}). * all players that are stored in the {@link OfflinePlayerHandler}).
@ -72,7 +113,7 @@ public final class StatCalculator {
return allStats; return allStats;
} }
private StatAction getStatTask(StatRequest.Settings requestSettings) { private @NotNull StatAction getStatTask(StatRequest.Settings requestSettings) {
int size = offlinePlayerHandler.getOfflinePlayerCount() != 0 ? offlinePlayerHandler.getOfflinePlayerCount() : 16; int size = offlinePlayerHandler.getOfflinePlayerCount() != 0 ? offlinePlayerHandler.getOfflinePlayerCount() : 16;
ConcurrentHashMap<String, Integer> allStats = new ConcurrentHashMap<>(size); ConcurrentHashMap<String, Integer> allStats = new ConcurrentHashMap<>(size);
ImmutableList<String> playerNames = ImmutableList.copyOf(offlinePlayerHandler.getOfflinePlayerNames()); ImmutableList<String> playerNames = ImmutableList.copyOf(offlinePlayerHandler.getOfflinePlayerNames());

View File

@ -3,11 +3,11 @@ package com.artemis.the.gr8.playerstats.statistic;
import com.artemis.the.gr8.playerstats.ThreadManager; import com.artemis.the.gr8.playerstats.ThreadManager;
import com.artemis.the.gr8.playerstats.msg.OutputManager; import com.artemis.the.gr8.playerstats.msg.OutputManager;
import com.artemis.the.gr8.playerstats.statistic.request.StatRequest; import com.artemis.the.gr8.playerstats.statistic.request.StatRequest;
import com.artemis.the.gr8.playerstats.statistic.result.StatResult;
import com.artemis.the.gr8.playerstats.utils.MyLogger; import com.artemis.the.gr8.playerstats.utils.MyLogger;
import com.artemis.the.gr8.playerstats.enums.StandardMessage; import com.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.artemis.the.gr8.playerstats.enums.Target;
import com.artemis.the.gr8.playerstats.reload.ReloadThread; import com.artemis.the.gr8.playerstats.reload.ReloadThread;
import net.kyori.adventure.text.TextComponent; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.*; import java.util.*;
@ -18,14 +18,14 @@ import java.util.*;
public final class StatThread extends Thread { public final class StatThread extends Thread {
private static OutputManager outputManager; private static OutputManager outputManager;
private static StatCalculator statCalculator; private static RequestProcessor requestProcessor;
private final ReloadThread reloadThread; private final ReloadThread reloadThread;
private final StatRequest<?> statRequest; private final StatRequest<?> statRequest;
public StatThread(OutputManager m, StatCalculator t, int ID, StatRequest<?> s, @Nullable ReloadThread r) { public StatThread(OutputManager m, RequestProcessor t, int ID, StatRequest<?> s, @Nullable ReloadThread r) {
outputManager = m; outputManager = m;
statCalculator = t; requestProcessor = t;
reloadThread = r; reloadThread = r;
statRequest = s; statRequest = s;
@ -35,16 +35,14 @@ public final class StatThread extends Thread {
} }
@Override @Override
public void run() throws IllegalStateException, NullPointerException { public void run() throws IllegalStateException {
MyLogger.logHighLevelMsg(this.getName() + " started!"); MyLogger.logHighLevelMsg(this.getName() + " started!");
CommandSender statRequester = statRequest.getSettings().getCommandSender();
if (statRequest == null) {
throw new NullPointerException("No statistic requestSettings was found!");
}
if (reloadThread != null && reloadThread.isAlive()) { if (reloadThread != null && reloadThread.isAlive()) {
try { try {
MyLogger.logLowLevelMsg(this.getName() + ": Waiting for " + reloadThread.getName() + " to finish up..."); MyLogger.logLowLevelMsg(this.getName() + ": Waiting for " + reloadThread.getName() + " to finish up...");
outputManager.sendFeedbackMsg(statRequest.getSettings().getCommandSender(), StandardMessage.STILL_RELOADING); outputManager.sendFeedbackMsg(statRequester, StandardMessage.STILL_RELOADING);
reloadThread.join(); reloadThread.join();
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -55,21 +53,16 @@ public final class StatThread extends Thread {
long lastCalc = ThreadManager.getLastRecordedCalcTime(); long lastCalc = ThreadManager.getLastRecordedCalcTime();
if (lastCalc > 2000) { if (lastCalc > 2000) {
outputManager.sendFeedbackMsgWaitAMoment(statRequest.getSettings().getCommandSender(), lastCalc > 20000); outputManager.sendFeedbackMsgWaitAMoment(statRequester, lastCalc > 20000);
} }
Target selection = statRequest.getSettings().getTarget();
try { try {
TextComponent statResult = switch (selection) { StatResult<?> result = statRequest.execute();
case PLAYER -> outputManager.formatAndSavePlayerStat(statRequest.getSettings(), statCalculator.getPlayerStat(statRequest.getSettings())); outputManager.sendToCommandSender(statRequester, result.formattedComponent());
case TOP -> outputManager.formatAndSaveTopStat(statRequest.getSettings(), statCalculator.getTopStats(statRequest.getSettings()));
case SERVER -> outputManager.formatAndSaveServerStat(statRequest.getSettings(), statCalculator.getServerStat(statRequest.getSettings()));
};
outputManager.sendToCommandSender(statRequest.getSettings().getCommandSender(), statResult);
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
if (!statRequest.getSettings().isConsoleSender()) { if (!statRequest.getSettings().isConsoleSender()) {
outputManager.sendFeedbackMsg(statRequest.getSettings().getCommandSender(), StandardMessage.UNKNOWN_ERROR); outputManager.sendFeedbackMsg(statRequester, StandardMessage.UNKNOWN_ERROR);
} }
} }
} }

View File

@ -4,6 +4,7 @@ import com.artemis.the.gr8.playerstats.Main;
import com.artemis.the.gr8.playerstats.statistic.result.StatResult; import com.artemis.the.gr8.playerstats.statistic.result.StatResult;
import com.artemis.the.gr8.playerstats.utils.EnumHandler; import com.artemis.the.gr8.playerstats.utils.EnumHandler;
import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Statistic; import org.bukkit.Statistic;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -18,7 +19,7 @@ import java.util.Arrays;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
public final class InternalStatRequest extends StatRequest<Object> { public final class InternalStatRequest extends StatRequest<TextComponent> {
private final OfflinePlayerHandler offlinePlayerHandler; private final OfflinePlayerHandler offlinePlayerHandler;
private final EnumHandler enumHandler; private final EnumHandler enumHandler;
@ -30,14 +31,17 @@ public final class InternalStatRequest extends StatRequest<Object> {
enumHandler = Main.getEnumHandler(); enumHandler = Main.getEnumHandler();
targetPattern = Pattern.compile("top|server|me|player"); targetPattern = Pattern.compile("top|server|me|player");
String[] argsMinusTarget = extractAndStoreTarget(sender, args); processArgs(sender, args);
String[] argsMinusStatistic = extractAndStoreStatistic(argsMinusTarget);
findAndStoreSubStat(argsMinusStatistic);
} }
@Override @Override
public StatResult<Object> execute() { public @NotNull StatResult<TextComponent> execute() {
return null; return Main.getRequestProcessor().getInternalResult(settings);
}
private void processArgs(CommandSender sender, String[] args) {
String[] argsMinusTarget = extractAndStoreTarget(sender, args);
findStatAndSubStat(argsMinusTarget);
} }
private String[] extractAndStoreTarget(CommandSender sender, @NotNull String[] leftoverArgs) { private String[] extractAndStoreTarget(CommandSender sender, @NotNull String[] leftoverArgs) {
@ -80,18 +84,17 @@ public final class InternalStatRequest extends StatRequest<Object> {
return leftoverArgs; return leftoverArgs;
} }
private String[] extractAndStoreStatistic(@NotNull String[] leftoverArgs) { private void findStatAndSubStat(@NotNull String[] leftoverArgs) {
for (String arg : leftoverArgs) { for (String arg : leftoverArgs) {
if (enumHandler.isStatistic(arg)) { if (enumHandler.isStatistic(arg)) {
super.settings.setStatistic(EnumHandler.getStatEnum(arg)); Statistic stat = EnumHandler.getStatEnum(arg);
return removeArg(leftoverArgs, arg); String[] argsWithoutStat = removeArg(leftoverArgs, arg);
findAndStoreSubStat(argsWithoutStat, stat);
} }
} }
return leftoverArgs;
} }
private void findAndStoreSubStat(@NotNull String[] leftoverArgs) { private void findAndStoreSubStat(String[] leftoverArgs, Statistic statistic) {
Statistic statistic = super.settings.getStatistic();
if (statistic == null || leftoverArgs.length == 0) { if (statistic == null || leftoverArgs.length == 0) {
return; return;
} }
@ -139,4 +142,5 @@ public final class InternalStatRequest extends StatRequest<Object> {
currentArgs.remove(argToRemove); currentArgs.remove(argToRemove);
return currentArgs.toArray(String[]::new); return currentArgs.toArray(String[]::new);
} }
} }

View File

@ -2,9 +2,7 @@ package com.artemis.the.gr8.playerstats.statistic.request;
import com.artemis.the.gr8.playerstats.Main; import com.artemis.the.gr8.playerstats.Main;
import com.artemis.the.gr8.playerstats.api.RequestGenerator; import com.artemis.the.gr8.playerstats.api.RequestGenerator;
import com.artemis.the.gr8.playerstats.msg.components.ComponentUtils;
import com.artemis.the.gr8.playerstats.statistic.result.StatResult; import com.artemis.the.gr8.playerstats.statistic.result.StatResult;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Statistic; import org.bukkit.Statistic;
@ -40,18 +38,6 @@ public final class PlayerStatRequest extends StatRequest<Integer> implements Req
@Override @Override
public @NotNull StatResult<Integer> execute() { public @NotNull StatResult<Integer> execute() {
int stat = Main return Main.getRequestProcessor().getPlayerResult(settings);
.getStatCalculator()
.getPlayerStat(settings);
TextComponent prettyComponent = Main
.getOutputManager()
.formatAndSavePlayerStat(settings, stat);
String prettyString = ComponentUtils
.getTranslatableComponentSerializer()
.serialize(prettyComponent);
return new StatResult<>(stat, prettyComponent, prettyString);
} }
} }

View File

@ -2,9 +2,7 @@ package com.artemis.the.gr8.playerstats.statistic.request;
import com.artemis.the.gr8.playerstats.Main; import com.artemis.the.gr8.playerstats.Main;
import com.artemis.the.gr8.playerstats.api.RequestGenerator; import com.artemis.the.gr8.playerstats.api.RequestGenerator;
import com.artemis.the.gr8.playerstats.msg.components.ComponentUtils;
import com.artemis.the.gr8.playerstats.statistic.result.StatResult; import com.artemis.the.gr8.playerstats.statistic.result.StatResult;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Statistic; import org.bukkit.Statistic;
@ -40,18 +38,6 @@ public final class ServerStatRequest extends StatRequest<Long> implements Reques
@Override @Override
public @NotNull StatResult<Long> execute() { public @NotNull StatResult<Long> execute() {
long stat = Main return Main.getRequestProcessor().getServerResult(settings);
.getStatCalculator()
.getServerStat(settings);
TextComponent prettyComponent = Main
.getOutputManager()
.formatAndSaveServerStat(settings, stat);
String prettyString = ComponentUtils
.getTranslatableComponentSerializer()
.serialize(prettyComponent);
return new StatResult<>(stat, prettyComponent, prettyString);
} }
} }

View File

@ -28,43 +28,9 @@ public abstract class StatRequest<T> {
settings = new Settings(requester); settings = new Settings(requester);
} }
protected StatRequest<T> configureUntyped(@NotNull Statistic statistic) {
if (statistic.getType() == Statistic.Type.UNTYPED) {
settings.setStatistic(statistic);
return this;
}
throw new IllegalArgumentException("This statistic is not of Type.Untyped");
}
protected StatRequest<T> configureBlockOrItemType(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException {
Statistic.Type type = statistic.getType();
if (type == Statistic.Type.BLOCK && material.isBlock()) {
settings.setBlock(material);
}
else if (type == Statistic.Type.ITEM && material.isItem()){
settings.setItem(material);
}
else {
throw new IllegalArgumentException("Either this statistic is not of Type.Block or Type.Item, or no valid block or item has been provided");
}
settings.setStatistic(statistic);
settings.setSubStatEntryName(material.toString());
return this;
}
protected StatRequest<T> configureEntityType(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException {
if (statistic.getType() == Statistic.Type.ENTITY) {
settings.setStatistic(statistic);
settings.setSubStatEntryName(entityType.toString());
settings.setEntity(entityType);
return this;
}
throw new IllegalArgumentException("This statistic is not of Type.Entity");
}
/** /**
* Executes this StatRequest. For a Top- or ServerRequest, this can * Executes this StatRequest. This calculation can take some time,
* take some time! * so don't call this from the main Thread if you can help it!
* *
* @return a StatResult containing the value of this lookup, both as * @return a StatResult containing the value of this lookup, both as
* numerical value and as formatted message * numerical value and as formatted message
@ -111,6 +77,40 @@ public abstract class StatRequest<T> {
} }
} }
protected StatRequest<T> configureUntyped(@NotNull Statistic statistic) {
if (statistic.getType() == Statistic.Type.UNTYPED) {
settings.statistic = statistic;
return this;
}
throw new IllegalArgumentException("This statistic is not of Type.Untyped");
}
protected StatRequest<T> configureBlockOrItemType(@NotNull Statistic statistic, @NotNull Material material) throws IllegalArgumentException {
Statistic.Type type = statistic.getType();
if (type == Statistic.Type.BLOCK && material.isBlock()) {
settings.block = material;
}
else if (type == Statistic.Type.ITEM && material.isItem()){
settings.item = material;
}
else {
throw new IllegalArgumentException("Either this statistic is not of Type.Block or Type.Item, or no valid block or item has been provided");
}
settings.statistic = statistic;
settings.subStatEntryName = material.toString();
return this;
}
protected StatRequest<T> configureEntityType(@NotNull Statistic statistic, @NotNull EntityType entityType) throws IllegalArgumentException {
if (statistic.getType() == Statistic.Type.ENTITY) {
settings.statistic = statistic;
settings.entity = entityType;
settings.subStatEntryName = entityType.toString();
return this;
}
throw new IllegalArgumentException("This statistic is not of Type.Entity");
}
public static final class Settings { public static final class Settings {
private final CommandSender sender; private final CommandSender sender;
private Statistic statistic; private Statistic statistic;
@ -158,18 +158,10 @@ public abstract class StatRequest<T> {
return sender instanceof ConsoleCommandSender; return sender instanceof ConsoleCommandSender;
} }
void setStatistic(Statistic statistic) {
this.statistic = statistic;
}
public Statistic getStatistic() { public Statistic getStatistic() {
return statistic; return statistic;
} }
private void setSubStatEntryName(String subStatEntry) {
this.subStatEntryName = subStatEntry;
}
public @Nullable String getSubStatEntryName() { public @Nullable String getSubStatEntryName() {
return subStatEntryName; return subStatEntryName;
} }
@ -186,26 +178,14 @@ public abstract class StatRequest<T> {
return this.topListSize; return this.topListSize;
} }
void setEntity(EntityType entity) {
this.entity = entity;
}
public EntityType getEntity() { public EntityType getEntity() {
return entity; return entity;
} }
void setBlock(Material block) {
this.block = block;
}
public Material getBlock() { public Material getBlock() {
return block; return block;
} }
void setItem(Material item) {
this.item = item;
}
public Material getItem() { public Material getItem() {
return item; return item;
} }

View File

@ -3,8 +3,6 @@ package com.artemis.the.gr8.playerstats.statistic.request;
import com.artemis.the.gr8.playerstats.Main; import com.artemis.the.gr8.playerstats.Main;
import com.artemis.the.gr8.playerstats.statistic.result.StatResult; import com.artemis.the.gr8.playerstats.statistic.result.StatResult;
import com.artemis.the.gr8.playerstats.api.RequestGenerator; import com.artemis.the.gr8.playerstats.api.RequestGenerator;
import com.artemis.the.gr8.playerstats.msg.components.ComponentUtils;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.Statistic; import org.bukkit.Statistic;
@ -42,18 +40,6 @@ public final class TopStatRequest extends StatRequest<LinkedHashMap<String, Inte
@Override @Override
public @NotNull StatResult<LinkedHashMap<String, Integer>> execute() { public @NotNull StatResult<LinkedHashMap<String, Integer>> execute() {
LinkedHashMap<String, Integer> stat = Main return Main.getRequestProcessor().getTopResult(settings);
.getStatCalculator()
.getTopStats(settings);
TextComponent prettyComponent = Main
.getOutputManager()
.formatAndSaveTopStat(settings, stat);
String prettyString = ComponentUtils
.getTranslatableComponentSerializer()
.serialize(prettyComponent);
return new StatResult<>(stat, prettyComponent, prettyString);
} }
} }

View File

@ -1,6 +1,6 @@
package com.artemis.the.gr8.playerstats.statistic.result; package com.artemis.the.gr8.playerstats.statistic.result;
import com.artemis.the.gr8.playerstats.api.ApiFormatter; import com.artemis.the.gr8.playerstats.api.StatFormatter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
@ -31,7 +31,7 @@ import net.kyori.adventure.text.TextComponent;
* To send a Component, you need to get a {@link BukkitAudiences} object, * To send a Component, you need to get a {@link BukkitAudiences} object,
* and use that to send the desired Component. Normally you would have to add * and use that to send the desired Component. Normally you would have to add
* Adventure as a dependency to your project, but since the library is included * Adventure as a dependency to your project, but since the library is included
* in PlayerStats, you can access it through the PlayerStatsAPI. Information * in PlayerStats, you can access it through the PlayerStatsImpl. Information
* on how to get and use the BukkitAudiences object can be found on * on how to get and use the BukkitAudiences object can be found on
* <a href="https://docs.adventure.kyori.net/platform/bukkit.html">Adventure's website</a>. * <a href="https://docs.adventure.kyori.net/platform/bukkit.html">Adventure's website</a>.
* *
@ -39,7 +39,7 @@ import net.kyori.adventure.text.TextComponent;
* same information in String-format. Don't use Adventure's <code>#content()</code> * same information in String-format. Don't use Adventure's <code>#content()</code>
* or <code>#toString()</code> methods on the Components - those won't get the actual * or <code>#toString()</code> methods on the Components - those won't get the actual
* message. And finally, if you want the results to be formatted differently, * message. And finally, if you want the results to be formatted differently,
* you can get an instance of the {@link ApiFormatter}. * you can get an instance of the {@link StatFormatter}.
*/ */
public record StatResult<T>(T value, TextComponent formattedComponent, String formattedString) { public record StatResult<T>(T value, TextComponent formattedComponent, String formattedString) {