Made LanguageKeyHandler static, improved MyLogger, added a while statement in StatThread in an attempt to enable ReloadThread to kill it (#64), Re-enabled PrideComponentFactory

This commit is contained in:
Artemis-the-gr8 2022-06-29 17:11:43 +02:00
parent 82a0196214
commit 9ca234975b
13 changed files with 217 additions and 134 deletions

View File

@ -31,12 +31,11 @@ public class Main extends JavaPlugin {
//initialize the Adventure library //initialize the Adventure library
adventure = BukkitAudiences.create(this); adventure = BukkitAudiences.create(this);
//first get an instance of the ConfigHandler and LanguageKeyHandler //first get an instance of the ConfigHandler
ConfigHandler config = new ConfigHandler(this); ConfigHandler config = new ConfigHandler(this);
LanguageKeyHandler language = new LanguageKeyHandler();
//for now always use the PrideComponentFactory (it'll use the regular formatting when needed) //for now always use the PrideComponentFactory (it'll use the regular formatting when needed)
MessageWriter messageWriter = new MessageWriter(config, language); MessageWriter messageWriter = new MessageWriter(config);
//initialize the threadManager //initialize the threadManager
ThreadManager threadManager = new ThreadManager(adventure(), config, messageWriter, this); ThreadManager threadManager = new ThreadManager(adventure(), config, messageWriter, this);

View File

@ -41,7 +41,7 @@ public class ThreadManager {
if (reloadThread == null || !reloadThread.isAlive()) { if (reloadThread == null || !reloadThread.isAlive()) {
reloadThreadID += 1; reloadThreadID += 1;
reloadThread = new ReloadThread(adventure, config, messageWriter, plugin, threshold, reloadThreadID, statThread, sender); reloadThread = new ReloadThread(adventure, config, messageWriter, threshold, reloadThreadID, statThread, sender);
reloadThread.start(); reloadThread.start();
} }
else { else {

View File

@ -26,7 +26,7 @@ public class ConfigHandler {
configVersion = 4; configVersion = 4;
checkConfigVersion(); checkConfigVersion();
MyLogger.setDebugLevel(debugLevel()); MyLogger.setDebugLevel(getDebugLevel());
} }
/** Checks the number that "config-version" returns to see if the config needs updating, and if so, send it to the Updater. /** Checks the number that "config-version" returns to see if the config needs updating, and if so, send it to the Updater.
@ -56,13 +56,14 @@ public class ConfigHandler {
} }
try { try {
config = YamlConfiguration.loadConfiguration(configFile); config = YamlConfiguration.loadConfiguration(configFile);
MyLogger.setDebugLevel(debugLevel());
return true;
} }
catch (IllegalArgumentException e) { catch (IllegalArgumentException e) {
MyLogger.logException(e, "ConfigHandler", "reloadConfig"); MyLogger.logException(e, "ConfigHandler", "reloadConfig");
return false; return false;
} }
//TODO Move this to ReloadThread
MyLogger.setDebugLevel(getDebugLevel());
return true;
} }
/** Returns the desired debugging level. /** Returns the desired debugging level.
@ -70,7 +71,7 @@ public class ConfigHandler {
<p>2 = medium (detail all encountered exceptions, log main tasks and show time taken)</p> <p>2 = medium (detail all encountered exceptions, log main tasks and show time taken)</p>
<p>3 = high (log all tasks and time taken)</p> <p>3 = high (log all tasks and time taken)</p>
<p>Default: 1</p>*/ <p>Default: 1</p>*/
public int debugLevel() { public int getDebugLevel() {
return config.getInt("debug-level", 1); return config.getInt("debug-level", 1);
} }
@ -88,7 +89,7 @@ public class ConfigHandler {
/** Returns the number of maximum days since a player has last been online. /** Returns the number of maximum days since a player has last been online.
<p>Default: 0 (which signals not to use this limit)</p>*/ <p>Default: 0 (which signals not to use this limit)</p>*/
public int lastPlayedLimit() { public int getLastPlayedLimit() {
return config.getInt("number-of-days-since-last-joined", 0); return config.getInt("number-of-days-since-last-joined", 0);
} }
@ -112,7 +113,7 @@ public class ConfigHandler {
/** Whether to use rainbow colors for the [PlayerStats] prefix rather than the default gold/purple. /** Whether to use rainbow colors for the [PlayerStats] prefix rather than the default gold/purple.
<p>Default: false</p> */ <p>Default: false</p> */
public boolean useRainbowPrefix() { public boolean useRainbowMode() {
return config.getBoolean("rainbow-mode", false); return config.getBoolean("rainbow-mode", false);
} }

View File

@ -21,15 +21,15 @@ import org.jetbrains.annotations.Nullable;
import static net.kyori.adventure.text.Component.*; import static net.kyori.adventure.text.Component.*;
import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.text;
/** Constructs Components with */ /** Creates Components with the desired formatting. This class can put Strings
into formatted Components with TextColor and TextDecoration, and turn
certain Strings into appropriate LanguageKeys to return a TranslatableComponent.*/
public class ComponentFactory { public class ComponentFactory {
private static ConfigHandler config; private static ConfigHandler config;
private final LanguageKeyHandler language;
public ComponentFactory(ConfigHandler c, LanguageKeyHandler l) { public ComponentFactory(ConfigHandler c) {
config = c; config = c;
language = l;
} }
/** Returns [PlayerStats] followed by a single space. */ /** Returns [PlayerStats] followed by a single space. */
@ -188,11 +188,11 @@ public class ComponentFactory {
subStatName = getPrettyName(subStatName); subStatName = getPrettyName(subStatName);
} }
else { else {
statName = language.getStatKey(request.getStatistic()); statName = LanguageKeyHandler.getStatKey(request.getStatistic());
switch (request.getStatistic().getType()) { switch (request.getStatistic().getType()) {
case BLOCK -> subStatName = language.getBlockKey(request.getBlock()); case BLOCK -> subStatName = LanguageKeyHandler.getBlockKey(request.getBlock());
case ENTITY -> subStatName = language.getEntityKey(request.getEntity()); case ENTITY -> subStatName = LanguageKeyHandler.getEntityKey(request.getEntity());
case ITEM -> subStatName = language.getItemKey(request.getItem()); case ITEM -> subStatName = LanguageKeyHandler.getItemKey(request.getItem());
case UNTYPED -> { case UNTYPED -> {
} }
} }

View File

@ -12,14 +12,17 @@ import java.util.HashMap;
public class LanguageKeyHandler { public class LanguageKeyHandler {
private final HashMap<Statistic, String> statNameKeys; private final static HashMap<Statistic, String> statNameKeys;
public LanguageKeyHandler() { static {
statNameKeys = new HashMap<>(); statNameKeys = new HashMap<>();
generateStatNameKeys(); generateStatNameKeys();
} }
public String getStatKey(@NotNull Statistic statistic) { private LanguageKeyHandler() {
}
public static String getStatKey(@NotNull Statistic statistic) {
if (statistic.getType() == Statistic.Type.UNTYPED) { if (statistic.getType() == Statistic.Type.UNTYPED) {
return "stat.minecraft." + statNameKeys.get(statistic); return "stat.minecraft." + statNameKeys.get(statistic);
} }
@ -30,7 +33,7 @@ public class LanguageKeyHandler {
/** Get the official Key from the NameSpacedKey for this entityType, /** Get the official Key from the NameSpacedKey for this entityType,
or return null if no enum constant can be retrieved or entityType is UNKNOWN.*/ or return null if no enum constant can be retrieved or entityType is UNKNOWN.*/
public @Nullable String getEntityKey(EntityType entity) { public static @Nullable String getEntityKey(EntityType entity) {
if (entity == null || entity == EntityType.UNKNOWN) return null; if (entity == null || entity == EntityType.UNKNOWN) return null;
else { else {
return "entity.minecraft." + entity.getKey().getKey(); return "entity.minecraft." + entity.getKey().getKey();
@ -39,7 +42,7 @@ public class LanguageKeyHandler {
/** Get the official Key from the NameSpacedKey for this item Material, /** Get the official Key from the NameSpacedKey for this item Material,
or return null if no enum constant can be retrieved.*/ or return null if no enum constant can be retrieved.*/
public @Nullable String getItemKey(Material item) { public static @Nullable String getItemKey(Material item) {
if (item == null) return null; if (item == null) return null;
else if (item.isBlock()) { else if (item.isBlock()) {
return getBlockKey(item); return getBlockKey(item);
@ -51,7 +54,7 @@ public class LanguageKeyHandler {
/** Returns the official Key from the NameSpacedKey for the block Material provided, /** Returns the official Key from the NameSpacedKey for the block Material provided,
or return null if no enum constant can be retrieved.*/ or return null if no enum constant can be retrieved.*/
public @Nullable String getBlockKey(Material block) { public static @Nullable String getBlockKey(Material block) {
if (block == null) return null; if (block == null) return null;
else if (block.toString().toLowerCase().contains("wall_banner")) { //replace wall_banner with regular banner, since there is no key for wall banners else if (block.toString().toLowerCase().contains("wall_banner")) { //replace wall_banner with regular banner, since there is no key for wall banners
String blockName = block.toString().toLowerCase().replace("wall_", ""); String blockName = block.toString().toLowerCase().replace("wall_", "");
@ -63,11 +66,11 @@ public class LanguageKeyHandler {
} }
} }
private void generateDefaultKeys() { private static void generateDefaultKeys() {
Arrays.stream(Statistic.values()).forEach(statistic -> statNameKeys.put(statistic, statistic.toString().toLowerCase())); Arrays.stream(Statistic.values()).forEach(statistic -> statNameKeys.put(statistic, statistic.toString().toLowerCase()));
} }
private void generateStatNameKeys() { private static void generateStatNameKeys() {
//get the enum names for all statistics first //get the enum names for all statistics first
generateDefaultKeys(); generateDefaultKeys();

View File

@ -25,9 +25,23 @@ public class MessageWriter {
private static ConfigHandler config; private static ConfigHandler config;
private static ComponentFactory componentFactory; private static ComponentFactory componentFactory;
public MessageWriter(ConfigHandler c, LanguageKeyHandler l) { public MessageWriter(ConfigHandler c) {
config = c; config = c;
componentFactory = new ComponentFactory(c, l); getComponentFactory();
}
//TODO Make ReloadThread do an update
public static void updateComponentFactory() {
getComponentFactory();
}
private static void getComponentFactory() {
if (config.useFestiveFormatting() || config.useRainbowMode()) {
componentFactory = new PrideComponentFactory(config);
}
else {
componentFactory = new ComponentFactory(config);
}
} }
public TextComponent reloadedConfig(boolean isBukkitConsole) { public TextComponent reloadedConfig(boolean isBukkitConsole) {
@ -37,7 +51,7 @@ public class MessageWriter {
public TextComponent stillReloading(boolean isBukkitConsole) { public TextComponent stillReloading(boolean isBukkitConsole) {
return componentFactory.msg( return componentFactory.msg(
"The plugin is still (re)loading, " + "The plugin is (re)loading, " +
"your request will be processed when it is done!", isBukkitConsole); "your request will be processed when it is done!", isBukkitConsole);
} }
@ -48,6 +62,10 @@ public class MessageWriter {
"please reload PlayerStats again to fix it!", isBukkitConsole); "please reload PlayerStats again to fix it!", isBukkitConsole);
} }
public TextComponent interruptedRequest() {
return componentFactory.msg("Your request was interrupted, please try again in a moment!", false);
}
public TextComponent waitAMoment(boolean longWait, boolean isBukkitConsole) { public TextComponent waitAMoment(boolean longWait, boolean isBukkitConsole) {
String msg = longWait ? "Calculating statistics, this may take a minute..." : String msg = longWait ? "Calculating statistics, this may take a minute..." :
"Calculating statistics, this may take a few moments..."; "Calculating statistics, this may take a few moments...";

View File

@ -4,7 +4,6 @@ import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.Bukkit;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.Month; import java.time.Month;
@ -16,8 +15,8 @@ public class PrideComponentFactory extends ComponentFactory {
private static ConfigHandler config; private static ConfigHandler config;
public PrideComponentFactory(ConfigHandler c, LanguageKeyHandler l) { public PrideComponentFactory(ConfigHandler c) {
super(c, l); super(c);
config = c; config = c;
} }
@ -62,7 +61,7 @@ public class PrideComponentFactory extends ComponentFactory {
if festive formatting is disabled or it is not pride month, if festive formatting is disabled or it is not pride month,
or the commandsender is a Bukkit or Spigot console.*/ or the commandsender is a Bukkit or Spigot console.*/
private boolean cancelRainbow(boolean isBukkitConsole) { private boolean cancelRainbow(boolean isBukkitConsole) {
return !(config.useRainbowPrefix() || (config.useFestiveFormatting() && LocalDate.now().getMonth().equals(Month.JUNE))) || return !(config.useRainbowMode() || (config.useFestiveFormatting() && LocalDate.now().getMonth().equals(Month.JUNE))) ||
(isBukkitConsole); (isBukkitConsole);
} }
} }

View File

@ -1,8 +1,8 @@
package com.gmail.artemis.the.gr8.playerstats.reload; package com.gmail.artemis.the.gr8.playerstats.reload;
import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.ThreadManager; import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler; import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.enums.DebugLevel;
import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter; import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread; import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger; import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
@ -30,19 +30,17 @@ public class ReloadThread extends Thread {
private final BukkitAudiences adventure; private final BukkitAudiences adventure;
private static ConfigHandler config; private static ConfigHandler config;
private static MessageWriter messageWriter; private static MessageWriter messageWriter;
private final Main plugin;
private final StatThread statThread; private final StatThread statThread;
private final CommandSender sender; private final CommandSender sender;
public ReloadThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, Main p, int threshold, int ID, @Nullable StatThread s, @Nullable CommandSender se) { public ReloadThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, int threshold, int ID, @Nullable StatThread s, @Nullable CommandSender se) {
this.threshold = threshold; this.threshold = threshold;
reloadThreadID = ID; reloadThreadID = ID;
adventure = a; adventure = a;
config = c; config = c;
messageWriter = m; messageWriter = m;
plugin = p;
statThread = s; statThread = s;
sender = se; sender = se;
@ -59,6 +57,10 @@ public class ReloadThread extends Thread {
//if reload is triggered by /statreload (aka this thread does not have ID number 1)... //if reload is triggered by /statreload (aka this thread does not have ID number 1)...
if (reloadThreadID != 1) { if (reloadThreadID != 1) {
if (statThread != null && statThread.isAlive()) { if (statThread != null && statThread.isAlive()) {
statThread.stopThread();
return;
/*
try { try {
MyLogger.waitingForOtherThread(this.getName(), statThread.getName()); MyLogger.waitingForOtherThread(this.getName(), statThread.getName());
statThread.join(); statThread.join();
@ -66,8 +68,9 @@ public class ReloadThread extends Thread {
MyLogger.logException(e, "ReloadThread", "run(), trying to join" + statThread.getName()); MyLogger.logException(e, "ReloadThread", "run(), trying to join" + statThread.getName());
throw new RuntimeException(e); throw new RuntimeException(e);
} }
*/
} }
plugin.getLogger().info("Reloading!"); MyLogger.logMsg("Reloading!", false);
if (config.reloadConfig()) { if (config.reloadConfig()) {
boolean isBukkitConsole = sender instanceof ConsoleCommandSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit"); boolean isBukkitConsole = sender instanceof ConsoleCommandSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit");
@ -81,7 +84,7 @@ public class ReloadThread extends Thread {
} }
} }
MyLogger.logTimeTakenDefault("ReloadThread", ("loaded " + OfflinePlayerHandler.getOfflinePlayerCount() + " offline players"), time); MyLogger.logTimeTaken("ReloadThread", ("loaded " + OfflinePlayerHandler.getOfflinePlayerCount() + " offline players"), time);
if (sender != null) { if (sender != null) {
adventure.sender(sender).sendMessage(messageWriter.reloadedConfig(isBukkitConsole)); adventure.sender(sender).sendMessage(messageWriter.reloadedConfig(isBukkitConsole));
} }
@ -92,7 +95,7 @@ public class ReloadThread extends Thread {
try { try {
OfflinePlayerHandler.updateOfflinePlayerList(getPlayerMap()); OfflinePlayerHandler.updateOfflinePlayerList(getPlayerMap());
ThreadManager.recordCalcTime(System.currentTimeMillis() - time); ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
MyLogger.logTimeTakenDefault("ReloadThread", MyLogger.logTimeTaken("ReloadThread",
("loaded " + OfflinePlayerHandler.getOfflinePlayerCount() + " offline players"), time); ("loaded " + OfflinePlayerHandler.getOfflinePlayerCount() + " offline players"), time);
} }
catch (ConcurrentModificationException e) { catch (ConcurrentModificationException e) {
@ -107,29 +110,42 @@ public class ReloadThread extends Thread {
OfflinePlayer[] offlinePlayers; OfflinePlayer[] offlinePlayers;
if (config.whitelistOnly()) { if (config.whitelistOnly()) {
offlinePlayers = Bukkit.getWhitelistedPlayers().toArray(OfflinePlayer[]::new); offlinePlayers = Bukkit.getWhitelistedPlayers().toArray(OfflinePlayer[]::new);
MyLogger.logTimeTaken("ReloadThread", "retrieved whitelist", time); MyLogger.logTimeTaken("ReloadThread", "retrieved whitelist", time, DebugLevel.MEDIUM);
} }
else if (config.excludeBanned()) { else if (config.excludeBanned()) {
Set<OfflinePlayer> bannedPlayers = Bukkit.getBannedPlayers(); Set<OfflinePlayer> bannedPlayers = Bukkit.getBannedPlayers();
offlinePlayers = Arrays.stream(Bukkit.getOfflinePlayers()) offlinePlayers = Arrays.stream(Bukkit.getOfflinePlayers())
.parallel() .parallel()
.filter(offlinePlayer -> !bannedPlayers.contains(offlinePlayer)).toArray(OfflinePlayer[]::new); .filter(offlinePlayer -> !bannedPlayers.contains(offlinePlayer)).toArray(OfflinePlayer[]::new);
MyLogger.logTimeTaken("ReloadThread", "retrieved banlist", time); MyLogger.logTimeTaken("ReloadThread", "retrieved banlist", time, DebugLevel.MEDIUM);
} }
else { else {
offlinePlayers = Bukkit.getOfflinePlayers(); offlinePlayers = Bukkit.getOfflinePlayers();
MyLogger.logTimeTaken("ReloadThread", "retrieved list of Offline Players", time); MyLogger.logTimeTaken("ReloadThread", "retrieved list of Offline Players", time, DebugLevel.MEDIUM);
} }
int size = offlinePlayers != null ? offlinePlayers.length : 16; int size = offlinePlayers != null ? offlinePlayers.length : 16;
ConcurrentHashMap<String, UUID> playerMap = new ConcurrentHashMap<>(size); ConcurrentHashMap<String, UUID> playerMap = new ConcurrentHashMap<>(size);
ReloadAction task = new ReloadAction(threshold, offlinePlayers, config.lastPlayedLimit(), playerMap); ReloadAction task = new ReloadAction(threshold, offlinePlayers, config.getLastPlayedLimit(), playerMap);
MyLogger.actionCreated((offlinePlayers != null) ? offlinePlayers.length : 0); MyLogger.actionCreated((offlinePlayers != null) ? offlinePlayers.length : 0);
ForkJoinPool.commonPool().invoke(task); ForkJoinPool.commonPool().invoke(task);
MyLogger.actionFinished(1); MyLogger.actionFinished(1);
return playerMap; return generateFakeExtraPlayers(playerMap, 10);
}
//generate fake extra players for PlayerStats, by looping over the real offlinePlayers multiple times
private @NotNull ConcurrentHashMap<String, UUID> generateFakeExtraPlayers(@NotNull ConcurrentHashMap<String, UUID> realPlayers, int loops) {
if (loops == 0 || loops == 1) return realPlayers;
ConcurrentHashMap<String, UUID> newPlayerMap = new ConcurrentHashMap<>(realPlayers.size() * loops);
for (int i = 0; i < loops; i++) {
for (String key : realPlayers.keySet()) {
newPlayerMap.put(key + i, realPlayers.get(key));
}
}
return newPlayerMap;
} }
} }

View File

@ -24,6 +24,7 @@ import java.util.stream.Collectors;
public class StatThread extends Thread { public class StatThread extends Thread {
private volatile boolean keepRunning = true;
private final int threshold; private final int threshold;
private final StatRequest request; private final StatRequest request;
@ -50,6 +51,12 @@ public class StatThread extends Thread {
MyLogger.threadCreated(this.getName()); MyLogger.threadCreated(this.getName());
} }
public void stopThread() {
MyLogger.logMsg("stopThread() is being executed by: " + Thread.currentThread().getName(), false);
keepRunning = false;
MyLogger.logMsg("StatThread has been forced to stop!", true);
}
//what the thread will do once started //what the thread will do once started
@Override @Override
public void run() throws IllegalStateException, NullPointerException { public void run() throws IllegalStateException, NullPointerException {
@ -71,52 +78,48 @@ public class StatThread extends Thread {
.sendMessage(messageWriter .sendMessage(messageWriter
.stillReloading(isBukkitConsole)); .stillReloading(isBukkitConsole));
reloadThread.join(); reloadThread.join();
//TODO Add timeout after which it checks again and potentially aborts mission
} catch (InterruptedException e) { } catch (InterruptedException e) {
plugin.getLogger().warning(e.toString()); MyLogger.logException(e, "StatThread", "Trying to join" + reloadThread.getName());
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
Target selection = request.getSelection(); //TODO Evaluate if this really does something
while (keepRunning) {
if (selection == Target.TOP || selection == Target.SERVER) { Target selection = request.getSelection();
if (ThreadManager.getLastRecordedCalcTime() > 20000) { if (selection == Target.PLAYER) {
adventure.sender(sender).sendMessage(messageWriter.waitAMoment(true, isBukkitConsole));
}
else if (ThreadManager.getLastRecordedCalcTime() > 2000) {
adventure.sender(sender).sendMessage(messageWriter.waitAMoment(false, isBukkitConsole));
}
try {
if (selection == Target.TOP) {
adventure.sender(sender).sendMessage(messageWriter.formatTopStats(getTopStats(), request));
}
else {
adventure.sender(sender).sendMessage(messageWriter.formatServerStat(getServerTotal(), request));
}
} catch (ConcurrentModificationException e) {
if (!isBukkitConsole) {
adventure.sender(sender).sendMessage(messageWriter.unknownError(false));
}
} catch (Exception e) {
adventure.sender(sender).sendMessage(messageWriter.formatExceptions(e.toString(), isBukkitConsole));
MyLogger.logException(e, "StatThread", "run(), trying to calculate or format a top or server statistic");
}
}
else if (selection == Target.PLAYER) {
try {
adventure.sender(sender).sendMessage( adventure.sender(sender).sendMessage(
messageWriter.formatPlayerStat(getIndividualStat(), request)); messageWriter.formatPlayerStat(getIndividualStat(), request));
} catch (UnsupportedOperationException | NullPointerException e) {
adventure.sender(sender).sendMessage(messageWriter.formatExceptions(e.toString(), isBukkitConsole));
} }
else {
if (ThreadManager.getLastRecordedCalcTime() > 2000) {
adventure.sender(sender).sendMessage(
messageWriter.waitAMoment(ThreadManager.getLastRecordedCalcTime() > 20000, isBukkitConsole));
}
try {
if (selection == Target.TOP) {
adventure.sender(sender).sendMessage(
messageWriter.formatTopStats(getTopStats(), request));
} else {
adventure.sender(sender).sendMessage(
messageWriter.formatServerStat(getServerTotal(), request));
}
} catch (ConcurrentModificationException e) {
if (!isBukkitConsole) {
adventure.sender(sender).sendMessage(
messageWriter.unknownError(false));
}
}
}
return;
} }
MyLogger.logMsg("(" + Thread.currentThread().getName() + ") shutting down...", false);
adventure.sender(sender).sendMessage(messageWriter.interruptedRequest());
} }
private LinkedHashMap<String, Integer> getTopStats() throws ConcurrentModificationException, NullPointerException { private LinkedHashMap<String, Integer> getTopStats() throws ConcurrentModificationException {
return getAllStats().entrySet().stream() return getAllStats().entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(config.getTopListMaxSize()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); .limit(config.getTopListMaxSize()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
@ -128,7 +131,7 @@ public class StatThread extends Thread {
} }
//invokes a bunch of worker pool threads to divide and conquer (get the statistics for all players in the list) //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, NullPointerException { private @NotNull ConcurrentHashMap<String, Integer> getAllStats() throws ConcurrentModificationException {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
int size = OfflinePlayerHandler.getOfflinePlayerCount() != 0 ? (int) (OfflinePlayerHandler.getOfflinePlayerCount() * 1.05) : 16; int size = OfflinePlayerHandler.getOfflinePlayerCount() != 0 ? (int) (OfflinePlayerHandler.getOfflinePlayerCount() * 1.05) : 16;
@ -142,20 +145,21 @@ public class StatThread extends Thread {
try { try {
commonPool.invoke(task); commonPool.invoke(task);
} catch (ConcurrentModificationException e) { } catch (ConcurrentModificationException e) {
plugin.getLogger().warning("The request could not be executed due to a ConcurrentModificationException. " + 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!"); "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()); throw new ConcurrentModificationException(e.toString());
} }
MyLogger.actionFinished(2); MyLogger.actionFinished(2);
ThreadManager.recordCalcTime(System.currentTimeMillis() - time); ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
MyLogger.logTimeTakenDefault("StatThread", "calculated all stats", time); MyLogger.logTimeTaken("StatThread", "calculated all stats", time);
return playerStats; return playerStats;
} }
//gets the actual statistic data for an individual player /** Gets the statistic data for an individual player. If somehow the player
private int getIndividualStat() throws UnsupportedOperationException, NullPointerException { cannot be found, this returns 0.*/
private int getIndividualStat() {
OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(request.getPlayerName()); OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(request.getPlayerName());
if (player != null) { if (player != null) {
switch (request.getStatistic().getType()) { switch (request.getStatistic().getType()) {
@ -173,6 +177,6 @@ public class StatThread extends Thread {
} }
} }
} }
throw new NullPointerException("The player you are trying to request either does not exist, or is not on the list for statistic lookups!"); return 0;
} }
} }

View File

@ -50,29 +50,26 @@ public class TopStatAction extends RecursiveAction {
} }
} }
private void getStatsDirectly() throws UnsupportedOperationException { private void getStatsDirectly() {
try { Iterator<String> iterator = playerNames.iterator();
Iterator<String> iterator = playerNames.iterator(); if (iterator.hasNext()) {
if (iterator.hasNext()) { do {
do { String playerName = iterator.next();
String playerName = iterator.next(); MyLogger.actionRunning(Thread.currentThread().getName(), playerName, 2);
MyLogger.actionRunning(Thread.currentThread().getName(), playerName, 2); OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName);
OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName); if (player != null) {
if (player != null) { int statistic = 0;
int statistic = 0; switch (request.getStatistic().getType()) {
switch (request.getStatistic().getType()) { case UNTYPED -> statistic = player.getStatistic(request.getStatistic());
case UNTYPED -> statistic = player.getStatistic(request.getStatistic()); case ENTITY -> statistic = player.getStatistic(request.getStatistic(), request.getEntity());
case ENTITY -> statistic = player.getStatistic(request.getStatistic(), request.getEntity()); case BLOCK -> statistic = player.getStatistic(request.getStatistic(), request.getBlock());
case BLOCK -> statistic = player.getStatistic(request.getStatistic(), request.getBlock()); case ITEM -> statistic = player.getStatistic(request.getStatistic(), request.getItem());
case ITEM -> statistic = player.getStatistic(request.getStatistic(), request.getItem());
}
if (statistic > 0) {
playerStats.put(playerName, statistic);
}
} }
} while (iterator.hasNext()); if (statistic > 0) {
} playerStats.put(playerName, statistic);
} catch (NoSuchElementException ignored) { }
}
} while (iterator.hasNext());
} }
} }
} }

View File

@ -21,7 +21,7 @@ public class EnumHandler {
private final static List<String> entitySubStatNames; private final static List<String> entitySubStatNames;
private final static List<String> subStatNames; private final static List<String> subStatNames;
static{ static {
blockNames = Arrays.stream(Material.values()) blockNames = Arrays.stream(Material.values())
.filter(Material::isBlock) .filter(Material::isBlock)
.map(Material::toString) .map(Material::toString)

View File

@ -22,7 +22,7 @@ public class MyLogger {
private static final AtomicInteger playersIndex; private static final AtomicInteger playersIndex;
private static ConcurrentHashMap<String, Integer> threadNames; private static ConcurrentHashMap<String, Integer> threadNames;
static{ static {
Plugin plugin = Bukkit.getPluginManager().getPlugin("PlayerStats"); Plugin plugin = Bukkit.getPluginManager().getPlugin("PlayerStats");
logger = (plugin != null) ? plugin.getLogger() : Bukkit.getLogger(); logger = (plugin != null) ? plugin.getLogger() : Bukkit.getLogger();
debugLevel = DebugLevel.LOW; debugLevel = DebugLevel.LOW;
@ -35,17 +35,6 @@ public class MyLogger {
private MyLogger() { private MyLogger() {
} }
/** Accesses the playersIndex to up it by 1 and return its previous value. */
private static int nextPlayersIndex() {
return playersIndex.getAndIncrement();
}
/** Returns true if the playersIndex is 10, or any subsequent increment of 10. */
private static boolean incrementOfTen() {
return (playersIndex.get() == 10 || (playersIndex.get() > 10 && playersIndex.get() % 10 == 0));
}
/** Sets the desired debugging level. /** Sets the desired debugging level.
<p>1 = low (only show unexpected errors)</p> <p>1 = low (only show unexpected errors)</p>
<p>2 = medium (detail all encountered exceptions, log main tasks and show time taken)</p> <p>2 = medium (detail all encountered exceptions, log main tasks and show time taken)</p>
@ -63,6 +52,30 @@ public class MyLogger {
} }
} }
public static void logMsg(String content, boolean logAsWarning) {
logMsg(content, DebugLevel.LOW, logAsWarning);
}
public static void logMsg(String content, DebugLevel logThreshold) {
logMsg(content, logThreshold, false);
}
public static void logMsg(String content, DebugLevel logThreshold, boolean logAsWarning) {
switch (logThreshold) {
case LOW -> log(content, logAsWarning);
case MEDIUM -> {
if (debugLevel != DebugLevel.LOW) {
log(content, logAsWarning);
}
}
case HIGH -> {
if (debugLevel == DebugLevel.HIGH) {
log(content, logAsWarning);
}
}
}
}
/** Log the encountered exception as a warning to console, /** Log the encountered exception as a warning to console,
with some information about which class/method caught it with some information about which class/method caught it
and with a printStackTrace if DebugLevel is HIGH. and with a printStackTrace if DebugLevel is HIGH.
@ -171,21 +184,54 @@ public class MyLogger {
} }
} }
/** Output to console how long a certain task has taken if DebugLevel is MEDIUM or HIGH.
@param className Name of the class executing the task
@param methodName Name or description of the task
@param startTime Timestamp marking the beginning of the task */
public static void logTimeTaken(String className, String methodName, long startTime) {
if (debugLevel != DebugLevel.LOW) {
logger.info(className + " " + methodName + ": " + (System.currentTimeMillis() - startTime) + "ms");
}
}
/** Output to console how long a certain task has taken (regardless of DebugLevel). /** Output to console how long a certain task has taken (regardless of DebugLevel).
@param className Name of the class executing the task @param className Name of the class executing the task
@param methodName Name or description of the task @param methodName Name or description of the task
@param startTime Timestamp marking the beginning of the task */ @param startTime Timestamp marking the beginning of the task */
public static void logTimeTakenDefault(String className, String methodName, long startTime) { public static void logTimeTaken(String className, String methodName, long startTime) {
logTimeTaken(className, methodName, startTime, DebugLevel.LOW);
}
/** Output to console how long a certain task has taken if DebugLevel is equal to or higher than the specified threshold.
@param className Name of the class executing the task
@param methodName Name or description of the task
@param startTime Timestamp marking the beginning of the task
@param logThreshold the DebugLevel threshold */
public static void logTimeTaken(String className, String methodName, long startTime, DebugLevel logThreshold) {
switch (logThreshold) {
case LOW -> printTime(className, methodName, startTime);
case MEDIUM -> {
if (debugLevel != DebugLevel.LOW) {
printTime(className, methodName, startTime);
}
}
case HIGH -> {
if (debugLevel == DebugLevel.HIGH) {
printTime(className, methodName, startTime);
}
}
}
}
private static void log(String content, boolean logAsWarning) {
if (logAsWarning) {
logger.warning(content);
} else {
logger.info(content);
}
}
private static void printTime(String className, String methodName, long startTime) {
logger.info(className + " " + methodName + ": " + (System.currentTimeMillis() - startTime) + "ms"); logger.info(className + " " + methodName + ": " + (System.currentTimeMillis() - startTime) + "ms");
} }
/** Accesses the playersIndex to up it by 1 and return its previous value. */
private static int nextPlayersIndex() {
return playersIndex.getAndIncrement();
}
/** Returns true if the playersIndex is 10, or any subsequent increment of 10. */
private static boolean incrementOfTen() {
return (playersIndex.get() == 10 || (playersIndex.get() > 10 && playersIndex.get() % 10 == 0));
}
} }

View File

@ -12,7 +12,7 @@ public class OfflinePlayerHandler {
private static ConcurrentHashMap<String, UUID> offlinePlayerUUIDs; private static ConcurrentHashMap<String, UUID> offlinePlayerUUIDs;
private static ArrayList<String> playerNames; private static ArrayList<String> playerNames;
static{ static {
offlinePlayerUUIDs = new ConcurrentHashMap<>(); offlinePlayerUUIDs = new ConcurrentHashMap<>();
playerNames = new ArrayList<>(); playerNames = new ArrayList<>();
} }