Added Closable interface to deal with classes that need closing during onDisable, wrote register methods for Closable and Reloadable, and wrote logic to reload the datasource PlayerStats uses during /reload

This commit is contained in:
Artemis-the-gr8 2023-03-31 15:57:01 +02:00
parent 0057b2c530
commit 9d2c22bff6
14 changed files with 138 additions and 68 deletions

View File

@ -71,8 +71,8 @@
<shadedPattern>com.artemis.the.gr8.playerstats.lib.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>com.artemis.the.gr8.database</pattern>
<shadedPattern>com.artemis.the.gr8.playerstats.lib.database</shadedPattern>
<pattern>com.artemis.the.gr8.databasemanager</pattern>
<shadedPattern>com.artemis.the.gr8.playerstats.lib.databasemanager</shadedPattern>
</relocation>
</relocations>
<filters>
@ -103,7 +103,7 @@
</excludes>
</filter>
<filter>
<artifact>com.artemis.the.gr8:Database:*</artifact>
<artifact>com.artemis.the.gr8:DatabaseManager:*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>

View File

@ -89,7 +89,7 @@
<dependency>
<groupId>com.artemis.the.gr8</groupId>
<artifactId>Database</artifactId>
<artifactId>DatabaseManager</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
@ -171,8 +171,8 @@
<shadedPattern>com.artemis.the.gr8.playerstats.lib.bstats</shadedPattern>
</relocation>
<relocation>
<pattern>com.artemis.the.gr8.database</pattern>
<shadedPattern>com.artemis.the.gr8.playerstats.lib.database</shadedPattern>
<pattern>com.artemis.the.gr8.databasemanager</pattern>
<shadedPattern>com.artemis.the.gr8.playerstats.lib.databasemanager</shadedPattern>
</relocation>
</relocations>
<filters>
@ -203,7 +203,7 @@
</excludes>
</filter>
<filter>
<artifact>com.artemis.the.gr8:Database:*</artifact>
<artifact>com.artemis.the.gr8:DatabaseManager:*</artifact>
<excludes>
<exclude>META-INF/**</exclude>
</excludes>

View File

@ -13,10 +13,11 @@ import com.artemis.the.gr8.playerstats.core.listeners.JoinListener;
import com.artemis.the.gr8.playerstats.core.msg.msgutils.LanguageKeyHandler;
import com.artemis.the.gr8.playerstats.core.sharing.ShareManager;
import com.artemis.the.gr8.playerstats.core.statistic.StatRequestManager;
import com.artemis.the.gr8.playerstats.core.utils.Closable;
import com.artemis.the.gr8.playerstats.core.utils.OfflinePlayerHandler;
import com.artemis.the.gr8.playerstats.core.utils.Reloadable;
import me.clip.placeholderapi.PlaceholderAPIPlugin;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
@ -26,6 +27,9 @@ import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
/**
* PlayerStats' Main class
*/
@ -33,20 +37,20 @@ public final class Main extends JavaPlugin implements PlayerStats {
private static JavaPlugin pluginInstance;
private static PlayerStats playerStatsAPI;
private static BukkitAudiences adventure;
private static ConfigHandler config;
private static ThreadManager threadManager;
private static LanguageKeyHandler languageKeyHandler;
private static OfflinePlayerHandler offlinePlayerHandler;
private static ThreadManager threadManager;
private static StatRequestManager statManager;
private static OutputManager outputManager;
private static ShareManager shareManager;
private static List<Reloadable> reloadables;
private static List<Closable> closables;
@Override
public void onEnable() {
initializeMainClasses();
reloadables = new ArrayList<>();
closables = new ArrayList<>();
initializeMainClassesInOrder();
registerCommands();
setupMetrics();
@ -59,19 +63,22 @@ public final class Main extends JavaPlugin implements PlayerStats {
@Override
public void onDisable() {
if (adventure != null) {
adventure.close();
adventure = null;
}
closables.forEach(Closable::close);
this.getLogger().info("Disabled PlayerStats!");
}
public void reloadPlugin() {
//config is not registered as reloadable to ensure it can be reloaded before everything else
config.reload();
languageKeyHandler.reload();
offlinePlayerHandler.reload();
outputManager.reload();
shareManager.reload();
reloadables.forEach(Reloadable::reload);
}
public static void registerReloadable(Reloadable reloadable) {
reloadables.add(reloadable);
}
public static void registerClosable(Closable closable) {
closables.add(closable);
}
/**
@ -98,19 +105,18 @@ public final class Main extends JavaPlugin implements PlayerStats {
* and store references to classes that are
* needed for the Command classes or the API.
*/
private void initializeMainClasses() {
private void initializeMainClassesInOrder() {
pluginInstance = this;
playerStatsAPI = this;
adventure = BukkitAudiences.create(this);
config = ConfigHandler.getInstance();
languageKeyHandler = LanguageKeyHandler.getInstance();
offlinePlayerHandler = OfflinePlayerHandler.getInstance();
shareManager = ShareManager.getInstance();
outputManager = new OutputManager(adventure);
statManager = new StatRequestManager(outputManager);
threadManager = new ThreadManager(this, outputManager);
LanguageKeyHandler.getInstance();
OfflinePlayerHandler.getInstance();
OutputManager.getInstance();
ShareManager.getInstance();
statManager = new StatRequestManager();
threadManager = new ThreadManager(this);
}
/**
@ -122,12 +128,12 @@ public final class Main extends JavaPlugin implements PlayerStats {
PluginCommand statcmd = this.getCommand("statistic");
if (statcmd != null) {
statcmd.setExecutor(new StatCommand(outputManager, threadManager));
statcmd.setExecutor(new StatCommand(threadManager));
statcmd.setTabCompleter(tabCompleter);
}
PluginCommand excludecmd = this.getCommand("statisticexclude");
if (excludecmd != null) {
excludecmd.setExecutor(new ExcludeCommand(outputManager));
excludecmd.setExecutor(new ExcludeCommand());
excludecmd.setTabCompleter(tabCompleter);
}
@ -137,7 +143,7 @@ public final class Main extends JavaPlugin implements PlayerStats {
}
PluginCommand sharecmd = this.getCommand("statisticshare");
if (sharecmd != null) {
sharecmd.setExecutor(new ShareCommand(outputManager));
sharecmd.setExecutor(new ShareCommand());
}
}
@ -176,7 +182,7 @@ public final class Main extends JavaPlugin implements PlayerStats {
@Override
public StatTextFormatter getStatTextFormatter() {
return outputManager.getMainMessageBuilder();
return OutputManager.getInstance().getMainMessageBuilder();
}
@Contract(" -> new")

View File

@ -15,8 +15,8 @@ public final class ExcludeCommand implements CommandExecutor {
private static OutputManager outputManager;
private final OfflinePlayerHandler offlinePlayerHandler;
public ExcludeCommand(OutputManager outputManager) {
ExcludeCommand.outputManager = outputManager;
public ExcludeCommand() {
outputManager = OutputManager.getInstance();
this.offlinePlayerHandler = OfflinePlayerHandler.getInstance();
}

View File

@ -15,8 +15,8 @@ public final class ShareCommand implements CommandExecutor {
private static OutputManager outputManager;
private static ShareManager shareManager;
public ShareCommand(OutputManager outputManager) {
ShareCommand.outputManager = outputManager;
public ShareCommand() {
outputManager = OutputManager.getInstance();
shareManager = ShareManager.getInstance();
}

View File

@ -39,9 +39,9 @@ public final class StatCommand implements CommandExecutor {
private final EnumHandler enumHandler;
private final OfflinePlayerHandler offlinePlayerHandler;
public StatCommand(OutputManager outputManager, ThreadManager threadManager) {
public StatCommand(ThreadManager threadManager) {
StatCommand.threadManager = threadManager;
StatCommand.outputManager = outputManager;
outputManager = OutputManager.getInstance();
config = ConfigHandler.getInstance();
enumHandler = EnumHandler.getInstance();

View File

@ -1,9 +1,8 @@
package com.artemis.the.gr8.playerstats.core.database;
import com.artemis.the.gr8.database.DatabaseManager;
import com.artemis.the.gr8.database.models.MyStatType;
import com.artemis.the.gr8.database.models.MyStatistic;
import com.artemis.the.gr8.playerstats.core.Main;
import com.artemis.the.gr8.databasemanager.DatabaseManager;
import com.artemis.the.gr8.databasemanager.models.MyStatType;
import com.artemis.the.gr8.databasemanager.models.MyStatistic;
import com.artemis.the.gr8.playerstats.core.utils.EnumHandler;
import org.bukkit.Statistic;
import org.jetbrains.annotations.Contract;
@ -15,16 +14,25 @@ import java.util.List;
public class Database {
private DatabaseManager databaseManager;
private static DatabaseManager databaseManager;
public Database() {
connect();
private Database() {
setUp();
}
private void connect() {
File pluginFolder = Main.getPluginInstance().getDataFolder();
@Contract("_, _, _ -> new")
public static @NotNull Database getMySQLDatabase(String URL, String username, String password) {
databaseManager = DatabaseManager.getMySQLManager(URL, username, password);
return new Database();
}
@Contract("_ -> new")
public static @NotNull Database getSQLiteDatabase(File pluginFolder) {
databaseManager = DatabaseManager.getSQLiteManager(pluginFolder);
// databaseManager = DatabaseManager.getMySQLManager("jdbc:mysql://localhost:3306/minecraftstatdb", "myuser", "myuser");
return new Database();
}
private void setUp() {
databaseManager.setUp(getStats(), null);
}
@ -47,7 +55,7 @@ public class Database {
}
@Contract(pure = true)
private MyStatType getType(Statistic statistic) {
private MyStatType getType(@NotNull Statistic statistic) {
return switch (statistic.getType()) {
case UNTYPED -> MyStatType.CUSTOM;
case BLOCK -> MyStatType.BLOCK;

View File

@ -1,11 +1,13 @@
package com.artemis.the.gr8.playerstats.core.msg;
import com.artemis.the.gr8.playerstats.api.StatTextFormatter;
import com.artemis.the.gr8.playerstats.core.Main;
import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
import com.artemis.the.gr8.playerstats.core.enums.StandardMessage;
import com.artemis.the.gr8.playerstats.core.msg.components.*;
import com.artemis.the.gr8.playerstats.core.msg.msgutils.FormattingFunction;
import com.artemis.the.gr8.playerstats.api.StatRequest;
import com.artemis.the.gr8.playerstats.core.utils.Closable;
import com.artemis.the.gr8.playerstats.core.utils.Reloadable;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.TextComponent;
@ -30,8 +32,9 @@ import static com.artemis.the.gr8.playerstats.core.enums.StandardMessage.*;
* for Players (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 Reloadable {
public final class OutputManager implements Reloadable, Closable {
private static volatile OutputManager instance;
private static BukkitAudiences adventure;
private static EnumMap<StandardMessage, Function<MessageBuilder, TextComponent>> standardMessages;
@ -39,18 +42,44 @@ public final class OutputManager implements Reloadable {
private MessageBuilder messageBuilder;
private MessageBuilder consoleMessageBuilder;
public OutputManager(BukkitAudiences adventure) {
OutputManager.adventure = adventure;
private OutputManager() {
adventure = BukkitAudiences.create(Main.getPluginInstance());
config = ConfigHandler.getInstance();
getMessageBuilders();
prepareFunctions();
Main.registerReloadable(this);
Main.registerClosable(this);
}
public static OutputManager getInstance() {
OutputManager localVar = instance;
if (localVar != null) {
return localVar;
}
synchronized (OutputManager.class) {
if (instance == null) {
instance = new OutputManager();
}
return instance;
}
}
@Override
public void reload() {
getMessageBuilders();
}
@Override
public void close() {
if (adventure != null) {
adventure.close();
adventure = null;
}
}
public StatTextFormatter getMainMessageBuilder() {
return messageBuilder;
}

View File

@ -1,5 +1,6 @@
package com.artemis.the.gr8.playerstats.core.msg.msgutils;
import com.artemis.the.gr8.playerstats.core.Main;
import com.artemis.the.gr8.playerstats.core.utils.EnumHandler;
import com.artemis.the.gr8.playerstats.core.utils.FileHandler;
import com.artemis.the.gr8.playerstats.api.enums.Unit;
@ -31,6 +32,7 @@ public final class LanguageKeyHandler extends FileHandler {
super("language.yml");
statisticKeys = generateStatisticKeys();
subStatKey = Pattern.compile("(item|entity|block)\\.minecraft\\.");
Main.registerReloadable(this);
}
public static LanguageKeyHandler getInstance() {

View File

@ -40,10 +40,10 @@ public final class ThreadManager {
private final HashMap<String, Thread> statThreads;
private static long lastRecordedCalcTime;
public ThreadManager(Main main, OutputManager outputManager) {
public ThreadManager(Main main) {
this.main = main;
this.config = ConfigHandler.getInstance();
ThreadManager.outputManager = outputManager;
outputManager = OutputManager.getInstance();
statThreads = new HashMap<>();
statThreadID = 0;

View File

@ -1,5 +1,6 @@
package com.artemis.the.gr8.playerstats.core.sharing;
import com.artemis.the.gr8.playerstats.core.Main;
import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
import com.artemis.the.gr8.playerstats.core.utils.MyLogger;
import com.artemis.the.gr8.playerstats.core.utils.Reloadable;
@ -37,6 +38,7 @@ public final class ShareManager implements Reloadable {
private ShareManager() {
reload();
Main.registerReloadable(this);
}
public static ShareManager getInstance() {

View File

@ -4,11 +4,12 @@ import com.artemis.the.gr8.playerstats.api.RequestGenerator;
import com.artemis.the.gr8.playerstats.api.StatManager;
import com.artemis.the.gr8.playerstats.api.StatRequest;
import com.artemis.the.gr8.playerstats.api.StatResult;
import com.artemis.the.gr8.playerstats.core.Main;
import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
import com.artemis.the.gr8.playerstats.core.database.Database;
import com.artemis.the.gr8.playerstats.core.msg.OutputManager;
import com.artemis.the.gr8.playerstats.core.utils.MyLogger;
import com.artemis.the.gr8.playerstats.core.utils.OfflinePlayerHandler;
import com.artemis.the.gr8.playerstats.core.utils.Reloadable;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -18,22 +19,36 @@ import java.util.*;
* Turns user input into a {@link StatRequest} that can be
* executed to get statistic data.
*/
public final class StatRequestManager implements StatManager {
public final class StatRequestManager implements StatManager, Reloadable {
private static RequestProcessor processor;
private final OfflinePlayerHandler offlinePlayerHandler;
public StatRequestManager(OutputManager outputManager) {
public StatRequestManager() {
offlinePlayerHandler = OfflinePlayerHandler.getInstance();
processor = getProcessor();
Main.registerReloadable(this);
}
@Override
public void reload() {
processor = getProcessor();
}
private @NotNull RequestProcessor getProcessor() {
OutputManager outputManager = OutputManager.getInstance();
ConfigHandler config = ConfigHandler.getInstance();
if (config.useDatabase()) {
String[] mySQLcredentials = config.getMySQLCredentials();
MyLogger.logLowLevelMsg(Arrays.toString(mySQLcredentials));
processor = new DatabaseProcessor(outputManager, new Database());
Database database;
String[] credentials = config.getMySQLCredentials();
if (credentials.length == 3 & Arrays.stream(credentials).noneMatch(String::isEmpty)) {
database = Database.getMySQLDatabase(credentials[0], credentials[1], credentials[2]);
} else {
processor = new BukkitProcessor(outputManager);
database = Database.getSQLiteDatabase(Main.getPluginInstance().getDataFolder());
}
return new DatabaseProcessor(outputManager, database);
}
return new BukkitProcessor(outputManager);
}
public static StatResult<?> execute(@NotNull StatRequest<?> request) {

View File

@ -0,0 +1,6 @@
package com.artemis.the.gr8.playerstats.core.utils;
public interface Closable {
void close();
}

View File

@ -1,5 +1,6 @@
package com.artemis.the.gr8.playerstats.core.utils;
import com.artemis.the.gr8.playerstats.core.Main;
import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
import com.artemis.the.gr8.playerstats.core.multithreading.ThreadManager;
import org.bukkit.Bukkit;
@ -31,6 +32,7 @@ public final class OfflinePlayerHandler extends FileHandler {
config = ConfigHandler.getInstance();
loadOfflinePlayers();
Main.registerReloadable(this);
}
public static OfflinePlayerHandler getInstance() {