Separated initial plugin set-up from reloading and organized responsibilities better for the OfflinePlayerHandler, ThreadManager and the classes implementing FileHandler

This commit is contained in:
Artemis-the-gr8 2022-10-03 15:40:45 +02:00
parent 3417233dec
commit 9ac77a365b
10 changed files with 95 additions and 114 deletions

View File

@ -168,7 +168,7 @@ public final class Main extends JavaPlugin {
config = new ConfigHandler();
enumHandler = new EnumHandler();
languageKeyHandler = new LanguageKeyHandler();
offlinePlayerHandler = new OfflinePlayerHandler();
offlinePlayerHandler = new OfflinePlayerHandler(config);
shareManager = new ShareManager(config);
statCalculator = new StatCalculator(offlinePlayerHandler);

View File

@ -45,8 +45,6 @@ public final class ThreadManager {
statThreadID = 0;
reloadThreadID = 0;
lastRecordedCalcTime = 0;
startReloadThread(null);
}
public static int getTaskThreshold() {

View File

@ -512,6 +512,7 @@ public final class MessageBuilder implements ApiFormatter {
return componentFactory.statAndSubStatNameTranslatable(statKey, subStatKey, target);
}
//TODO turn key into custom input from file
String prettyStatName = StringUtils.prettify(statistic.toString());
String prettySubStatName = StringUtils.prettify(subStatName);
return componentFactory.statAndSubStatName(prettyStatName, prettySubStatName, target);

View File

@ -13,7 +13,7 @@ import java.util.concurrent.RecursiveAction;
/**
* The action that is executed when a reload-command is triggered.
*/
final class ReloadAction extends RecursiveAction {
public final class PlayerLoadAction extends RecursiveAction {
private static int threshold;
@ -33,14 +33,14 @@ final class ReloadAction extends RecursiveAction {
* @param offlinePlayerUUIDs the ConcurrentHashMap to put playerNames and UUIDs in
* @see OfflinePlayerHandler
*/
public ReloadAction(OfflinePlayer[] players,
int lastPlayedLimit, ConcurrentHashMap<String, UUID> offlinePlayerUUIDs) {
public PlayerLoadAction(OfflinePlayer[] players,
int lastPlayedLimit, ConcurrentHashMap<String, UUID> offlinePlayerUUIDs) {
this(players, 0, players.length, lastPlayedLimit, offlinePlayerUUIDs);
}
private ReloadAction(OfflinePlayer[] players, int start, int end,
int lastPlayedLimit, ConcurrentHashMap<String, UUID> offlinePlayerUUIDs) {
private PlayerLoadAction(OfflinePlayer[] players, int start, int end,
int lastPlayedLimit, ConcurrentHashMap<String, UUID> offlinePlayerUUIDs) {
threshold = ThreadManager.getTaskThreshold();
this.players = players;
@ -61,9 +61,9 @@ final class ReloadAction extends RecursiveAction {
}
else {
final int split = length / 2;
final ReloadAction subTask1 = new ReloadAction(players, start, (start + split),
final PlayerLoadAction subTask1 = new PlayerLoadAction(players, start, (start + split),
lastPlayedLimit, offlinePlayerUUIDs);
final ReloadAction subTask2 = new ReloadAction(players, (start + split), end,
final PlayerLoadAction subTask2 = new PlayerLoadAction(players, (start + split), end,
lastPlayedLimit, offlinePlayerUUIDs);
//queue and compute all subtasks in the right order

View File

@ -2,25 +2,17 @@ package com.artemis.the.gr8.playerstats.reload;
import com.artemis.the.gr8.playerstats.Main;
import com.artemis.the.gr8.playerstats.ShareManager;
import com.artemis.the.gr8.playerstats.ThreadManager;
import com.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.artemis.the.gr8.playerstats.msg.OutputManager;
import com.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler;
import com.artemis.the.gr8.playerstats.statistic.StatCalculator;
import com.artemis.the.gr8.playerstats.statistic.StatThread;
import com.artemis.the.gr8.playerstats.utils.MyLogger;
import com.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import com.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.artemis.the.gr8.playerstats.enums.DebugLevel;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Predicate;
/** The Thread that is in charge of reloading PlayerStats. */
public final class ReloadThread extends Thread {
@ -28,35 +20,31 @@ public final class ReloadThread extends Thread {
private static ConfigHandler config;
private static OutputManager outputManager;
private final int reloadThreadID;
private final StatThread statThread;
private final CommandSender sender;
public ReloadThread(ConfigHandler c, OutputManager m, int ID, @Nullable StatThread s, @Nullable CommandSender se) {
config = c;
outputManager = m;
reloadThreadID = ID;
statThread = s;
sender = se;
this.setName("ReloadThread-" + reloadThreadID);
this.setName("ReloadThread-" + ID);
MyLogger.logHighLevelMsg(this.getName() + " created!");
}
/**
* 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
* Then, it will reload the config, update the {@link LanguageKeyHandler},
* the {@link OfflinePlayerHandler}, the {@link DebugLevel}, update
* the share-settings in {@link ShareManager} and topListSize-settings
* in {@link StatCalculator}, and update the MessageBuilders in the
* {@link OutputManager}.
*/
@Override
public void run() {
long time = System.currentTimeMillis();
MyLogger.logHighLevelMsg(this.getName() + " started!");
if (statThread != null && statThread.isAlive()) {
@ -69,18 +57,11 @@ public final class ReloadThread extends Thread {
}
}
if (reloadThreadID != 1) { //during a reload
MyLogger.logLowLevelMsg("Reloading!");
reloadEverything();
MyLogger.logLowLevelMsg("Reloading!");
reloadEverything();
if (sender != null) {
outputManager.sendFeedbackMsg(sender, StandardMessage.RELOADED_CONFIG);
}
}
else { //during first start-up
MyLogger.setDebugLevel(config.getDebugLevel());
OfflinePlayerHandler.updateOfflinePlayerList(loadOfflinePlayers());
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
if (sender != null) {
outputManager.sendFeedbackMsg(sender, StandardMessage.RELOADED_CONFIG);
}
}
@ -89,51 +70,8 @@ public final class ReloadThread extends Thread {
MyLogger.setDebugLevel(config.getDebugLevel());
Main.getLanguageKeyHandler().reload();
Main.getOfflinePlayerHandler().reload();
OutputManager.updateMessageBuilders();
OfflinePlayerHandler.updateOfflinePlayerList(loadOfflinePlayers());
ShareManager.updateSettings(config);
}
private ConcurrentHashMap<String, UUID> loadOfflinePlayers() {
long time = System.currentTimeMillis();
OfflinePlayer[] offlinePlayers;
if (config.whitelistOnly()) {
offlinePlayers = Bukkit.getWhitelistedPlayers().toArray(OfflinePlayer[]::new);
MyLogger.logMediumLevelTask("ReloadThread",
"retrieved whitelist", time);
}
else if (config.excludeBanned()) {
if (Bukkit.getPluginManager().getPlugin("LiteBans") != null) {
offlinePlayers = Arrays.stream(Bukkit.getOfflinePlayers())
.parallel()
.filter(Predicate.not(OfflinePlayer::isBanned))
.toArray(OfflinePlayer[]::new);
} else {
Set<OfflinePlayer> bannedPlayers = Bukkit.getBannedPlayers();
offlinePlayers = Arrays.stream(Bukkit.getOfflinePlayers())
.parallel()
.filter(offlinePlayer -> !bannedPlayers.contains(offlinePlayer)).toArray(OfflinePlayer[]::new);
}
MyLogger.logMediumLevelTask("ReloadThread",
"retrieved banlist", time);
}
else {
offlinePlayers = Bukkit.getOfflinePlayers();
MyLogger.logMediumLevelTask("ReloadThread",
"retrieved list of Offline Players", time);
}
int size = offlinePlayers != null ? offlinePlayers.length : 16;
ConcurrentHashMap<String, UUID> playerMap = new ConcurrentHashMap<>(size);
ReloadAction task = new ReloadAction(offlinePlayers, config.getLastPlayedLimit(), playerMap);
MyLogger.actionCreated((offlinePlayers != null) ? offlinePlayers.length : 0);
ForkJoinPool.commonPool().invoke(task);
MyLogger.actionFinished();
MyLogger.logLowLevelTask("ReloadThread",
("loaded " + playerMap.size() + " offline players"), time);
return playerMap;
}
}

View File

@ -67,7 +67,7 @@ public final class StatCalculator {
MyLogger.actionFinished();
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
MyLogger.logMediumLevelTask("StatThread", "calculated all stats", time);
MyLogger.logMediumLevelTask("Calculated all stats", time);
return allStats;
}

View File

@ -53,12 +53,22 @@ public final class MyLogger {
logger.info(content);
}
public static void logLowLevelTask(String taskName, long startTime) {
printTime(taskName, startTime);
}
public static void logMediumLevelMsg(String content) {
if (debugLevel != DebugLevel.LOW) {
logger.info(content);
}
}
public static void logMediumLevelTask(String taskName, long startTime) {
if (debugLevel != DebugLevel.LOW) {
printTime(taskName, startTime);
}
}
public static void logHighLevelMsg(String content) {
if (debugLevel == DebugLevel.HIGH) {
logger.info(content);
@ -146,24 +156,13 @@ public final class MyLogger {
}
}
public static void logMediumLevelTask(String className, String methodName, long startTime) {
if (debugLevel != DebugLevel.LOW) {
printTime(className, methodName, startTime);
}
}
public static void logLowLevelTask(String className, String methodName, long startTime) {
printTime(className, methodName, startTime);
}
/**
* Output to console how long a certain task has taken.
*
* @param className Name of the class executing the task
* @param methodName Name or description of the task
* @param taskName name of the task that has been executed
* @param startTime Timestamp marking the beginning of the task
*/
private static void printTime(String className, String methodName, long startTime) {
logger.info(className + " " + methodName + ": " + (System.currentTimeMillis() - startTime) + "ms");
private static void printTime(String taskName, long startTime) {
logger.info(taskName + " (" + (System.currentTimeMillis() - startTime) + "ms)");
}
}

View File

@ -1,10 +1,15 @@
package com.artemis.the.gr8.playerstats.utils;
import com.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.artemis.the.gr8.playerstats.reload.PlayerLoadAction;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Predicate;
/**
* A utility class that deals with OfflinePlayers. It stores a list
@ -14,30 +19,20 @@ import java.util.concurrent.ConcurrentHashMap;
*/
public final class OfflinePlayerHandler extends FileHandler {
private static ConcurrentHashMap<String, UUID> offlinePlayerUUIDs;
private static ArrayList<String> playerNames;
private static ConfigHandler config;
private ConcurrentHashMap<String, UUID> offlinePlayerUUIDs;
private ArrayList<String> playerNames;
public OfflinePlayerHandler() {
public OfflinePlayerHandler(ConfigHandler configHandler) {
super("excluded_players.yml");
offlinePlayerUUIDs = new ConcurrentHashMap<>();
playerNames = new ArrayList<>();
config = configHandler;
loadOfflinePlayers();
}
@Override
public void reload() {
super.reload();
}
/**
* Get a new HashMap that stores the players to include in stat calculations.
* This HashMap is stored as a private variable in OfflinePlayerHandler.
*
* @param playerList ConcurrentHashMap with keys: playerNames and values: UUIDs
*/
public static void updateOfflinePlayerList(ConcurrentHashMap<String, UUID> playerList) {
offlinePlayerUUIDs = playerList;
playerNames = Collections.list(offlinePlayerUUIDs.keys());
loadOfflinePlayers();
}
/**
@ -91,4 +86,51 @@ public final class OfflinePlayerHandler extends FileHandler {
throw new IllegalArgumentException("Cannot convert this player-name into a valid Player to calculate statistics for");
}
}
private void loadOfflinePlayers() {
Executors.newSingleThreadExecutor().execute(() -> {
long time = System.currentTimeMillis();
OfflinePlayer[] offlinePlayers;
if (config.whitelistOnly()) {
offlinePlayers = getWhitelistedPlayers();
}
else if (config.excludeBanned()) {
offlinePlayers = getNonBannedPlayers();
}
else {
offlinePlayers = Bukkit.getOfflinePlayers();
}
int size = offlinePlayerUUIDs != null ? offlinePlayerUUIDs.size() : 16;
offlinePlayerUUIDs = new ConcurrentHashMap<>(size);
PlayerLoadAction task = new PlayerLoadAction(offlinePlayers, config.getLastPlayedLimit(), offlinePlayerUUIDs);
MyLogger.actionCreated(offlinePlayers != null ? offlinePlayers.length : 0);
ForkJoinPool.commonPool().invoke(task);
MyLogger.actionFinished();
playerNames = Collections.list(offlinePlayerUUIDs.keys());
MyLogger.logLowLevelTask(("Loaded " + offlinePlayerUUIDs.size() + " offline players"), time);
});
}
private OfflinePlayer[] getWhitelistedPlayers() {
return Bukkit.getWhitelistedPlayers().toArray(OfflinePlayer[]::new);
}
private OfflinePlayer[] getNonBannedPlayers() {
if (Bukkit.getPluginManager().isPluginEnabled("LiteBans")) {
return Arrays.stream(Bukkit.getOfflinePlayers())
.parallel()
.filter(Predicate.not(OfflinePlayer::isBanned))
.toArray(OfflinePlayer[]::new);
}
Set<OfflinePlayer> banList = Bukkit.getBannedPlayers();
return Arrays.stream(Bukkit.getOfflinePlayers())
.parallel()
.filter(Predicate.not(banList::contains))
.toArray(OfflinePlayer[]::new);
}
}

View File

@ -4,7 +4,7 @@
# Players whose UUIDs are stored in this file, will not be included in /statistic results.
# This can be used for more fine-grained filtering, for example to exclude alt accounts.
# For more general filtering settings, see the config.yml
# For more general filtering settings, see the config.yml (section 'General')
excluded:
-

View File

@ -2,6 +2,9 @@
# PlayerStats Language File #
# ------------------------------------------------------------------------------------------------------ #
# If "translate-to-client-language" in the config.yml is set to false (section 'Format & Display'),
# values from this file will be used instead
stat_type.minecraft.mined: "Times Mined"
stat_type.minecraft.crafted: "Times Crafted"
stat_type.minecraft.used: "Times Used"