mirror of
https://github.com/itHotL/PlayerStats.git
synced 2024-11-29 13:05:32 +01:00
Got rough version of API to work!
This commit is contained in:
parent
54040c4e3d
commit
de150bfe51
@ -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);
|
||||
}
|
||||
}
|
@ -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<String, Instant> shareTimeStamp;
|
||||
private ArrayBlockingQueue<UUID> 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;
|
||||
}
|
||||
|
@ -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<String, Thread> 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();
|
||||
}
|
||||
|
@ -27,7 +27,8 @@ public interface PlayerStats {
|
||||
<p>- if applicable, a sub-stat-name (example: diorite)(</p>
|
||||
<p>- a target for this lookup: can be "top", "server", "player" (or "me" to indicate the current CommandSender)</p>
|
||||
<p>- if "player" was chosen, include a player-name</p>
|
||||
@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,
|
||||
|
@ -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,17 +37,17 @@ 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<String, Integer> stats = statManager.getTopStats(request);
|
||||
return statFormatter.formatTopStat(request, stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("This is not a valid stat-request!");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String componentToString(TextComponent component) {
|
||||
return statFormatter.toString(component);
|
||||
|
@ -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.
|
@ -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<StandardMessage, Function<MessageBuilder, TextComponent>> 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);
|
||||
}
|
||||
|
@ -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<String, UUID> loadOfflinePlayers() {
|
||||
|
@ -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<ConcurrentHashMap<String, Integer>> {
|
||||
|
||||
private static int threshold;
|
||||
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
private final ImmutableList<String> playerNames;
|
||||
private final StatRequest request;
|
||||
private final ConcurrentHashMap<String, Integer> playerStats;
|
||||
private final ConcurrentHashMap<String, Integer> 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<String> playerNames, StatRequest statRequest, ConcurrentHashMap<String, Integer> playerStats) {
|
||||
public StatAction(OfflinePlayerHandler offlinePlayerHandler, ImmutableList<String> playerNames, StatRequest statRequest, ConcurrentHashMap<String, Integer> 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<String, Integer> 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<String, Integer> getStatsDirectly() {
|
||||
Iterator<String> 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;
|
||||
}
|
||||
}
|
@ -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<String, Integer> 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<Integer> 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<String, Integer> getAllStatsAsync(StatRequest request) {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
ForkJoinPool commonPool = ForkJoinPool.commonPool();
|
||||
ConcurrentHashMap<String, Integer> 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<String, Integer> allStats = new ConcurrentHashMap<>(size);
|
||||
ImmutableList<String> playerNames = ImmutableList.copyOf(offlinePlayerHandler.getOfflinePlayerNames());
|
||||
|
||||
StatAction task = new StatAction(offlinePlayerHandler, playerNames, request, allStats);
|
||||
MyLogger.actionCreated(playerNames.size());
|
||||
|
||||
return task;
|
||||
}
|
||||
}
|
@ -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<String, Integer> 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<Integer> 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<String, Integer> getAllStats() throws ConcurrentModificationException {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
int size = offlinePlayerHandler.getOfflinePlayerCount() != 0 ? offlinePlayerHandler.getOfflinePlayerCount() : 16;
|
||||
ConcurrentHashMap<String, Integer> playerStats = new ConcurrentHashMap<>(size);
|
||||
ImmutableList<String> 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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user