Got rough version of API to work!

This commit is contained in:
Artemis-the-gr8 2022-07-23 16:08:35 +02:00
parent 54040c4e3d
commit de150bfe51
11 changed files with 179 additions and 182 deletions

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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,

View File

@ -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);

View File

@ -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.

View File

@ -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);
}

View File

@ -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() {

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}