Made /reload and loading the offline players in onEnable asynchronous, added some more feedback (#33, #23)

This commit is contained in:
Artemis-the-gr8 2022-05-31 01:03:58 +02:00
parent 29d775e959
commit 519a13f9aa
9 changed files with 192 additions and 59 deletions

View File

@ -36,20 +36,20 @@ public class Main extends JavaPlugin {
ConfigHandler config = new ConfigHandler(this); ConfigHandler config = new ConfigHandler(this);
MessageFactory messageFactory = new MessageFactory(config, this); MessageFactory messageFactory = new MessageFactory(config, this);
OfflinePlayerHandler offlinePlayerHandler = new OfflinePlayerHandler(config); OfflinePlayerHandler offlinePlayerHandler = new OfflinePlayerHandler(config);
getLogger().info("Amount of offline players: " + offlinePlayerHandler.getOfflinePlayerCount()); ThreadManager threadManager = new ThreadManager(this, adventure(), config, offlinePlayerHandler, messageFactory);
//register the commands //register the commands
PluginCommand statcmd = this.getCommand("statistic"); PluginCommand statcmd = this.getCommand("statistic");
if (statcmd != null) { if (statcmd != null) {
statcmd.setExecutor(new StatCommand(adventure(), config, offlinePlayerHandler, messageFactory, this)); statcmd.setExecutor(new StatCommand(threadManager, adventure(),offlinePlayerHandler, messageFactory));
statcmd.setTabCompleter(new TabCompleter(offlinePlayerHandler)); statcmd.setTabCompleter(new TabCompleter(offlinePlayerHandler));
} }
PluginCommand reloadcmd = this.getCommand("statisticreload"); PluginCommand reloadcmd = this.getCommand("statisticreload");
if (reloadcmd != null) reloadcmd.setExecutor(new ReloadCommand(config, offlinePlayerHandler, this)); if (reloadcmd != null) reloadcmd.setExecutor(new ReloadCommand(threadManager));
//register the listener //register the listener
Bukkit.getPluginManager().registerEvents(new JoinListener(offlinePlayerHandler), this); Bukkit.getPluginManager().registerEvents(new JoinListener(offlinePlayerHandler), this);
logTimeTaken("Time", "taken", time); logTimeTaken("onEnable", "time taken", time);
this.getLogger().info("Enabled PlayerStats!"); this.getLogger().info("Enabled PlayerStats!");
} }
@ -59,12 +59,11 @@ public class Main extends JavaPlugin {
adventure.close(); adventure.close();
adventure = null; adventure = null;
} }
this.getLogger().info("Disabled PlayerStats!"); this.getLogger().info("Disabled PlayerStats!");
} }
public long logTimeTaken(String className, String methodName, long previousTime) { public long logTimeTaken(String className, String methodName, long previousTime) {
getLogger().info(className + " " + methodName + ": " + (System.currentTimeMillis() - previousTime) + "ms"); getLogger().info(className + ", " + methodName + ": " + (System.currentTimeMillis() - previousTime) + "ms");
return System.currentTimeMillis(); return System.currentTimeMillis();
} }
} }

View File

@ -0,0 +1,66 @@
package com.gmail.artemis.the.gr8.playerstats;
import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
import com.gmail.artemis.the.gr8.playerstats.utils.MessageFactory;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.Nullable;
public class ReloadThread extends Thread {
private final ConfigHandler config;
private final OfflinePlayerHandler offlinePlayerHandler;
private final Main plugin;
private final StatThread statThread;
private final CommandSender sender;
private final boolean firstTimeLoading;
public ReloadThread(ConfigHandler c, OfflinePlayerHandler o, Main p, @Nullable StatThread s, @Nullable CommandSender se, boolean firstTime) {
config = c;
offlinePlayerHandler = o;
plugin = p;
statThread = s;
sender = se;
firstTimeLoading = firstTime;
plugin.getLogger().info("ReloadThread created");
}
@Override
public void run() {
long time = System.currentTimeMillis();
if (!firstTimeLoading) {
if (statThread != null && statThread.isAlive()) {
try {
plugin.getLogger().info("Waiting for statThread to finish up...");
statThread.join();
} catch (InterruptedException e) {
plugin.getLogger().warning(e.toString());
throw new RuntimeException(e);
}
}
plugin.getLogger().info("Reloading!");
if (config.reloadConfig()) {
offlinePlayerHandler.updateOfflinePlayerList();
plugin.getLogger().info("Amount of relevant players: " + offlinePlayerHandler.getOfflinePlayerCount());
plugin.logTimeTaken("ReloadThread", "loading offline players", time);
if (sender != null) {
sender.sendMessage(MessageFactory.getPluginPrefix() + ChatColor.GREEN + "Config reloaded!");
}
}
}
else {
plugin.getLogger().info("Loading offline players...");
offlinePlayerHandler.updateOfflinePlayerList();
plugin.getLogger().info("Amount of relevant players: " + offlinePlayerHandler.getOfflinePlayerCount());
plugin.logTimeTaken("ReloadThread", "loading offline players", time);
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
}
}
}

View File

@ -0,0 +1,53 @@
package com.gmail.artemis.the.gr8.playerstats;
import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
import com.gmail.artemis.the.gr8.playerstats.utils.MessageFactory;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.command.CommandSender;
public class ThreadManager {
private final Main plugin;
private final BukkitAudiences adventure;
private final ConfigHandler config;
private final OfflinePlayerHandler offlinePlayerHandler;
private final MessageFactory messageFactory;
private ReloadThread reloadThread;
private StatThread statThread;
private static long lastRecordedCalcTime;
public ThreadManager(Main p, BukkitAudiences b, ConfigHandler c, OfflinePlayerHandler o, MessageFactory m) {
plugin = p;
adventure = b;
config = c;
offlinePlayerHandler = o;
messageFactory = m;
startReloadThread(null, true);
}
public void startReloadThread(CommandSender sender, boolean firstTimeLoading) {
reloadThread = new ReloadThread(config, offlinePlayerHandler, plugin, statThread, sender, firstTimeLoading);
reloadThread.start();
}
public void startStatThread(StatRequest request) {
statThread = new StatThread(request, reloadThread, adventure, config, offlinePlayerHandler, messageFactory, plugin);
statThread.start();
}
//store the time in milliseconds that the last top-stat-lookup took (or loading the offline-player-list if no look-ups have been done yet)
public static void recordCalcTime(long time) {
lastRecordedCalcTime = time;
}
//returns the time in milliseconds the last top-stat-lookup took (or loading the offline-player-list if no look-ups have been done yet)
public static long getLastRecordedCalcTime() {
return lastRecordedCalcTime;
}
}

View File

@ -1,11 +1,7 @@
package com.gmail.artemis.the.gr8.playerstats.commands; package com.gmail.artemis.the.gr8.playerstats.commands;
import com.gmail.artemis.the.gr8.playerstats.Main; import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.MessageFactory;
import org.bukkit.ChatColor;
import org.bukkit.command.Command; import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
@ -13,26 +9,15 @@ import org.jetbrains.annotations.NotNull;
public class ReloadCommand implements CommandExecutor { public class ReloadCommand implements CommandExecutor {
private final ConfigHandler config; private final ThreadManager threadManager;
private final OfflinePlayerHandler offlinePlayerHandler;
private final Main plugin;
public ReloadCommand(ThreadManager t) {
public ReloadCommand(ConfigHandler c, OfflinePlayerHandler of, Main p) { threadManager = t;
offlinePlayerHandler = of;
config = c;
plugin = p;
} }
@Override @Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (config.reloadConfig()) { threadManager.startReloadThread(sender, false);
offlinePlayerHandler.updateOfflinePlayerList(); return true;
plugin.getLogger().info("Amount of players: " + offlinePlayerHandler.getOfflinePlayerCount());
sender.sendMessage(MessageFactory.getPluginPrefix() + ChatColor.GREEN + "Config reloaded!");
return true;
}
return false;
} }
} }

View File

@ -1,10 +1,10 @@
package com.gmail.artemis.the.gr8.playerstats.commands; package com.gmail.artemis.the.gr8.playerstats.commands;
import com.gmail.artemis.the.gr8.playerstats.Main; import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler; import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler;
import com.gmail.artemis.the.gr8.playerstats.StatRequest; import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.StatThread;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.MessageFactory; import com.gmail.artemis.the.gr8.playerstats.utils.MessageFactory;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
@ -19,18 +19,16 @@ import org.jetbrains.annotations.NotNull;
public class StatCommand implements CommandExecutor { public class StatCommand implements CommandExecutor {
private final ThreadManager threadManager;
private final BukkitAudiences adventure; private final BukkitAudiences adventure;
private final ConfigHandler config;
private final OfflinePlayerHandler offlinePlayerHandler; private final OfflinePlayerHandler offlinePlayerHandler;
private final MessageFactory messageFactory; private final MessageFactory messageFactory;
private final Main plugin;
public StatCommand(BukkitAudiences b, ConfigHandler c, OfflinePlayerHandler of, MessageFactory o, Main p) { public StatCommand(ThreadManager t, BukkitAudiences b, OfflinePlayerHandler of, MessageFactory o) {
threadManager = t;
adventure = b; adventure = b;
config = c;
offlinePlayerHandler = of; offlinePlayerHandler = of;
messageFactory = o; messageFactory = o;
plugin = p;
} }
@Override @Override
@ -41,14 +39,12 @@ public class StatCommand implements CommandExecutor {
//part 2: sending the information to the StatThread, or give feedback if request is invalid //part 2: sending the information to the StatThread, or give feedback if request is invalid
if (isValidStatRequest(request)) { if (isValidStatRequest(request)) {
StatThread statThread = new StatThread(request, adventure, config, offlinePlayerHandler, messageFactory, plugin); threadManager.startStatThread(request);
statThread.start();
return true; return true;
} }
else { else {
adventure.sender(sender).sendMessage(getRelevantFeedback(request)); adventure.sender(sender).sendMessage(getRelevantFeedback(request));
//adventure.sender(sender).sendMessage(messageFactory.getHelpMsg());
return false; return false;
} }
} }
@ -60,7 +56,7 @@ public class StatCommand implements CommandExecutor {
} }
} }
private TextComponent getRelevantFeedback(StatRequest request) { private TextComponent getRelevantFeedback(@NotNull StatRequest request) {
if (request.getStatName() == null) { if (request.getStatName() == null) {
return messageFactory.missingStatName(); return messageFactory.missingStatName();
} }
@ -102,7 +98,6 @@ public class StatCommand implements CommandExecutor {
if (request.getSubStatEntry() == null) request.setSubStatEntry(arg); if (request.getSubStatEntry() == null) request.setSubStatEntry(arg);
} }
} }
else if (arg.equalsIgnoreCase("top")) { else if (arg.equalsIgnoreCase("top")) {
request.setTopFlag(true); request.setTopFlag(true);
} }

View File

@ -1,4 +1,4 @@
package com.gmail.artemis.the.gr8.playerstats; package com.gmail.artemis.the.gr8.playerstats.statistic;
import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler;
import org.bukkit.Statistic; import org.bukkit.Statistic;

View File

@ -1,5 +1,8 @@
package com.gmail.artemis.the.gr8.playerstats; package com.gmail.artemis.the.gr8.playerstats.statistic;
import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.ReloadThread;
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler; import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler; import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler; import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
@ -9,6 +12,7 @@ import org.bukkit.OfflinePlayer;
import org.bukkit.Statistic; import org.bukkit.Statistic;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@ -19,6 +23,7 @@ import java.util.stream.Collectors;
public class StatThread extends Thread { public class StatThread extends Thread {
private final StatRequest request; private final StatRequest request;
private final ReloadThread reloadThread;
private final BukkitAudiences adventure; private final BukkitAudiences adventure;
private final ConfigHandler config; private final ConfigHandler config;
@ -27,8 +32,9 @@ public class StatThread extends Thread {
private final Main plugin; private final Main plugin;
//constructor (called on thread creation) //constructor (called on thread creation)
public StatThread(StatRequest s, BukkitAudiences b, ConfigHandler c, OfflinePlayerHandler of, MessageFactory o, Main p) { public StatThread(StatRequest s, @Nullable ReloadThread r, BukkitAudiences b, ConfigHandler c, OfflinePlayerHandler of, MessageFactory o, Main p) {
request = s; request = s;
reloadThread = r;
adventure = b; adventure = b;
config = c; config = c;
@ -49,6 +55,16 @@ public class StatThread extends Thread {
if (request == null) { if (request == null) {
throw new NullPointerException("No statistic request was found!"); throw new NullPointerException("No statistic request was found!");
} }
if (reloadThread != null && reloadThread.isAlive()) {
try {
plugin.getLogger().info("Waiting for reloadThread to finish up...");
adventure.sender(request.getCommandSender()).sendMessage(messageFactory.stillReloading());
reloadThread.join();
} catch (InterruptedException e) {
plugin.getLogger().warning(e.toString());
throw new RuntimeException(e);
}
}
CommandSender sender = request.getCommandSender(); CommandSender sender = request.getCommandSender();
String playerName = request.getPlayerName(); String playerName = request.getPlayerName();
@ -62,7 +78,7 @@ public class StatThread extends Thread {
messageFactory.formatPlayerStat( messageFactory.formatPlayerStat(
playerName, statName, subStatEntry, getStatistic( playerName, statName, subStatEntry, getStatistic(
statName, subStatEntry, playerName))); statName, subStatEntry, playerName)));
plugin.logTimeTaken("StatThread", "calculated individual stat", time); plugin.logTimeTaken("StatThread", "calculating individual stat", time);
} catch (Exception e) { } catch (Exception e) {
sender.sendMessage(messageFactory.formatExceptions(e.toString())); sender.sendMessage(messageFactory.formatExceptions(e.toString()));
@ -70,11 +86,19 @@ public class StatThread extends Thread {
} }
} else if (topFlag) { } else if (topFlag) {
if (ThreadManager.getLastRecordedCalcTime() > 30000) {
adventure.sender(sender).sendMessage(messageFactory.waitAMoment(true));
}
else if (ThreadManager.getLastRecordedCalcTime() > 2000) {
adventure.sender(sender).sendMessage(messageFactory.waitAMoment(false));
}
try { try {
adventure.sender(sender).sendMessage(messageFactory.formatTopStats( adventure.sender(sender).sendMessage(messageFactory.formatTopStats(
getTopStatistics(statName, subStatEntry), statName, subStatEntry)); getTopStatistics(statName, subStatEntry), statName, subStatEntry));
plugin.logTimeTaken("StatThread", "calculated top stat", time); plugin.logTimeTaken("StatThread", "calculating top stat", time);
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
} catch (Exception e) { } catch (Exception e) {
sender.sendMessage(messageFactory.formatExceptions(e.toString())); sender.sendMessage(messageFactory.formatExceptions(e.toString()));
@ -95,7 +119,6 @@ public class StatThread extends Thread {
} }
} }
private LinkedHashMap<String, Integer> getTopStatistics(String statName, String subStatEntry) { private LinkedHashMap<String, Integer> getTopStatistics(String statName, String subStatEntry) {
try { try {
Statistic stat = EnumHandler.getStatEnum(statName); Statistic stat = EnumHandler.getStatEnum(statName);

View File

@ -22,7 +22,7 @@ public class MessageFactory {
private static ConfigHandler config; private static ConfigHandler config;
private final Main plugin; private final Main plugin;
private static final TextColor errorColor = TextColor.fromHexString("#55aaff"); private static final TextColor msgColor = TextColor.fromHexString("#55aaff");
private static final String pluginPrefix = ChatColor.GRAY + "[" + ChatColor.GOLD + "PlayerStats" + ChatColor.GRAY + "] " + ChatColor.RESET; private static final String pluginPrefix = ChatColor.GRAY + "[" + ChatColor.GOLD + "PlayerStats" + ChatColor.GRAY + "] " + ChatColor.RESET;
public MessageFactory(ConfigHandler c, Main p) { public MessageFactory(ConfigHandler c, Main p) {
@ -34,12 +34,21 @@ public class MessageFactory {
return pluginPrefix; return pluginPrefix;
} }
public TextComponent stillReloading() {
return text(getPluginPrefix()).append(text("The plugin is reloading, your request will be processed when it is done!").color(msgColor));
}
public TextComponent waitAMoment(boolean longWait) {
return longWait ? text(getPluginPrefix()).append(text("Calculating statistics, this may take a minute...").color(msgColor))
: text(getPluginPrefix()).append(text("Calculating statistics, this may take a few moments...").color(msgColor));
}
public String formatExceptions(String exception) { public String formatExceptions(String exception) {
return getPluginPrefix() + exception; return getPluginPrefix() + exception;
} }
public TextComponent missingStatName() { public TextComponent missingStatName() {
return text(getPluginPrefix()).append(text("Please provide a valid statistic name!").color(errorColor)); return text(getPluginPrefix()).append(text("Please provide a valid statistic name!").color(msgColor));
} }
public TextComponent missingSubStatName(Statistic.Type statType) { public TextComponent missingSubStatName(Statistic.Type statType) {
@ -48,15 +57,15 @@ public class MessageFactory {
.append(text("Please add a valid ") .append(text("Please add a valid ")
.append(text(subStat)) .append(text(subStat))
.append(text(" to look up this statistic!"))) .append(text(" to look up this statistic!")))
.color(errorColor); .color(msgColor);
} }
public TextComponent missingTarget() { public TextComponent missingTarget() {
return text(getPluginPrefix()).append(text("Please add \"me\", \"player\" or \"top\"").color(errorColor)); return text(getPluginPrefix()).append(text("Please add \"me\", \"player\" or \"top\"").color(msgColor));
} }
public TextComponent missingPlayerName() { public TextComponent missingPlayerName() {
return text(getPluginPrefix()).append(text("Please specify a valid player-name!").color(errorColor)); return text(getPluginPrefix()).append(text("Please specify a valid player-name!").color(msgColor));
} }
public TextComponent wrongSubStatType(Statistic.Type statType, String subStatEntry) { public TextComponent wrongSubStatType(Statistic.Type statType, String subStatEntry) {
@ -67,11 +76,11 @@ public class MessageFactory {
.append(text("\"")) .append(text("\""))
.append(text(" is not a valid ")) .append(text(" is not a valid "))
.append(text(subStat))) .append(text(subStat)))
.color(errorColor); .color(msgColor);
} }
public TextComponent unknownError() { public TextComponent unknownError() {
return text(getPluginPrefix()).append(text("Something went wrong with your input. Check /statistic for a usage explanation").color(errorColor)); return text(getPluginPrefix()).append(text("Something went wrong with your input. Check /statistic for a usage explanation").color(msgColor));
} }
public TextComponent helpMsg() { public TextComponent helpMsg() {

View File

@ -13,7 +13,7 @@ public class OfflinePlayerHandler {
public OfflinePlayerHandler(ConfigHandler c) { public OfflinePlayerHandler(ConfigHandler c) {
config = c; config = c;
updateOfflinePlayerList(); //updateOfflinePlayerList();
} }
public boolean isOfflinePlayerName(String playerName) { public boolean isOfflinePlayerName(String playerName) {
@ -40,12 +40,15 @@ public class OfflinePlayerHandler {
offlinePlayerUUIDs.clear(); offlinePlayerUUIDs.clear();
} }
Arrays.stream(Bukkit.getOfflinePlayers()).filter(offlinePlayer -> for (int i = 0; i <100; i++) {
offlinePlayer.getName() != null && int finalI = i;
(!excludeBanned || !offlinePlayer.isBanned()) && Arrays.stream(Bukkit.getOfflinePlayers()).filter(offlinePlayer ->
(!whitelistOnly || offlinePlayer.isWhitelisted()) && offlinePlayer.getName() != null &&
(lastPlayedLimit == 0 || UnixTimeHandler.hasPlayedSince(lastPlayedLimit, offlinePlayer.getLastPlayed()))) (!excludeBanned || !offlinePlayer.isBanned()) &&
.forEach(offlinePlayer -> offlinePlayerUUIDs.put(offlinePlayer.getName(), offlinePlayer.getUniqueId())); (!whitelistOnly || offlinePlayer.isWhitelisted()) &&
(lastPlayedLimit == 0 || UnixTimeHandler.hasPlayedSince(lastPlayedLimit, offlinePlayer.getLastPlayed())))
.forEach(offlinePlayer -> offlinePlayerUUIDs.put((offlinePlayer.getName() + finalI), offlinePlayer.getUniqueId()));
}
} }
public OfflinePlayer getOfflinePlayer(String playerName) { public OfflinePlayer getOfflinePlayer(String playerName) {