diff --git a/src/main/java/net/coreprotect/CoreProtect.java b/src/main/java/net/coreprotect/CoreProtect.java index 226fabe..1815e80 100755 --- a/src/main/java/net/coreprotect/CoreProtect.java +++ b/src/main/java/net/coreprotect/CoreProtect.java @@ -1,37 +1,18 @@ package net.coreprotect; import java.io.File; -import java.util.Iterator; -import java.util.Map.Entry; -import org.bstats.bukkit.MetricsLite; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.block.data.BlockData; -import org.bukkit.entity.Player; -import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.java.JavaPlugin; -import net.coreprotect.command.CommandHandler; -import net.coreprotect.command.TabHandler; -import net.coreprotect.config.Config; import net.coreprotect.config.ConfigHandler; -import net.coreprotect.consumer.Consumer; -import net.coreprotect.consumer.process.Process; -import net.coreprotect.language.Language; import net.coreprotect.language.Phrase; -import net.coreprotect.listener.ListenerHandler; -import net.coreprotect.listener.player.PlayerQuitListener; -import net.coreprotect.paper.PaperAdapter; -import net.coreprotect.thread.CacheHandler; -import net.coreprotect.thread.NetworkHandler; -import net.coreprotect.thread.Scheduler; +import net.coreprotect.services.PluginInitializationService; +import net.coreprotect.services.ShutdownService; import net.coreprotect.utility.Chat; -import net.coreprotect.utility.ChatUtils; -import net.coreprotect.utility.Color; -import net.coreprotect.utility.Teleport; -import net.coreprotect.utility.VersionUtils; +/** + * Main class for the CoreProtect plugin + */ public final class CoreProtect extends JavaPlugin { private static CoreProtect instance; @@ -45,7 +26,7 @@ public final class CoreProtect extends JavaPlugin { return instance; } - private CoreProtectAPI api = new CoreProtectAPI(); + private final CoreProtectAPI api = new CoreProtectAPI(); /** * Get the CoreProtect API @@ -58,73 +39,15 @@ public final class CoreProtect extends JavaPlugin { @Override public void onEnable() { + // Set plugin instance and data folder path instance = this; ConfigHandler.path = this.getDataFolder().getPath() + File.separator; - Language.loadPhrases(); - boolean start = performVersionChecks(); - if (start) { - try { - Consumer.initialize(); // Prepare consumer (keep this here) - new ListenerHandler(this); - getCommand("coreprotect").setExecutor(CommandHandler.getInstance()); - getCommand("coreprotect").setTabCompleter(new TabHandler()); - getCommand("core").setExecutor(CommandHandler.getInstance()); - getCommand("core").setTabCompleter(new TabHandler()); - getCommand("co").setExecutor(CommandHandler.getInstance()); - getCommand("co").setTabCompleter(new TabHandler()); + // Initialize plugin using the initialization service + boolean initialized = PluginInitializationService.initializePlugin(this); - boolean exists = (new File(ConfigHandler.path)).exists(); - if (!exists) { - new File(ConfigHandler.path).mkdir(); - } - start = ConfigHandler.performInitialization(true); // Perform any necessary initialization - } - catch (Exception e) { - e.printStackTrace(); - start = false; - } - } - - if (start) { - PluginDescriptionFile pluginDescription = this.getDescription(); - ChatUtils.sendConsoleComponentStartup(Bukkit.getServer().getConsoleSender(), Phrase.build(Phrase.ENABLE_SUCCESS, ConfigHandler.EDITION_NAME)); - if (Config.getGlobal().MYSQL) { - Chat.console(Phrase.build(Phrase.USING_MYSQL)); - } - else { - Chat.console(Phrase.build(Phrase.USING_SQLITE)); - } - - Chat.console("--------------------"); - Chat.console(Phrase.build(Phrase.ENJOY_COREPROTECT, pluginDescription.getName())); - Chat.console(Phrase.build(Phrase.LINK_DISCORD, "www.coreprotect.net/discord/")); - Chat.console("--------------------"); - - Scheduler.scheduleSyncDelayedTask(this, () -> { - try { - Thread networkHandler = new Thread(new NetworkHandler(true, true)); - networkHandler.start(); - } - catch (Exception e) { - e.printStackTrace(); - } - }, 0); - - Thread cacheCleanUpThread = new Thread(new CacheHandler()); - cacheCleanUpThread.start(); - - Consumer.startConsumer(); - - // Enabling bStats - try { - new MetricsLite(this, 2876); - } - catch (Exception e) { - // Failed to connect to bStats server or something else went wrong. - } - } - else { + // Disable plugin if initialization failed + if (!initialized) { Chat.console(Phrase.build(Phrase.ENABLE_FAILED, ConfigHandler.EDITION_NAME)); getServer().getPluginManager().disablePlugin(this); } @@ -132,102 +55,6 @@ public final class CoreProtect extends JavaPlugin { @Override public void onDisable() { - safeShutdown(this); + ShutdownService.safeShutdown(this); } - - private static boolean performVersionChecks() { - try { - String[] bukkitVersion = Bukkit.getServer().getBukkitVersion().split("[-.]"); - if (VersionUtils.newVersion(bukkitVersion[0] + "." + bukkitVersion[1], ConfigHandler.MINECRAFT_VERSION)) { - Chat.console(Phrase.build(Phrase.VERSION_REQUIRED, "Minecraft", ConfigHandler.MINECRAFT_VERSION)); - return false; - } - if (VersionUtils.newVersion(ConfigHandler.LATEST_VERSION, bukkitVersion[0] + "." + bukkitVersion[1]) && VersionUtils.isBranch("master")) { - Chat.console(Phrase.build(Phrase.VERSION_INCOMPATIBLE, "Minecraft", bukkitVersion[0] + "." + bukkitVersion[1])); - return false; - } - String[] javaVersion = (System.getProperty("java.version").replaceAll("[^0-9.]", "") + ".0").split("\\."); - if (VersionUtils.newVersion(javaVersion[0] + "." + javaVersion[1], ConfigHandler.JAVA_VERSION)) { - Chat.console(Phrase.build(Phrase.VERSION_REQUIRED, "Java", ConfigHandler.JAVA_VERSION)); - return false; - } - - if (ConfigHandler.EDITION_BRANCH.length() == 0) { - Chat.sendConsoleMessage(Color.RED + "[CoreProtect] " + Phrase.build(Phrase.INVALID_BRANCH_1)); - Chat.sendConsoleMessage(Color.GREY + "[CoreProtect] " + Phrase.build(Phrase.INVALID_BRANCH_2)); - Chat.sendConsoleMessage(Color.GREY + "[CoreProtect] " + Phrase.build(Phrase.INVALID_BRANCH_3)); - return false; - } - - ConfigHandler.SERVER_VERSION = Integer.parseInt(bukkitVersion[1]); - } - catch (Exception e) { - e.printStackTrace(); - return false; - } - - return true; - } - - private static void safeShutdown(CoreProtect plugin) { - try { - /* if server is stopping, log disconnections of online players */ - if (ConfigHandler.serverRunning && PaperAdapter.ADAPTER.isStopping(plugin.getServer())) { - for (Player player : plugin.getServer().getOnlinePlayers()) { - PlayerQuitListener.queuePlayerQuit(player); - } - } - - if (!ConfigHandler.isFolia) { - Iterator> iterator = Teleport.revertBlocks.entrySet().iterator(); - while (iterator.hasNext()) { - Entry entry = iterator.next(); - entry.getKey().getBlock().setBlockData(entry.getValue()); - iterator.remove(); - } - } - - ConfigHandler.serverRunning = false; - long shutdownTime = System.currentTimeMillis(); - long alertTime = shutdownTime + (10 * 1000); - if (ConfigHandler.converterRunning) { - Chat.console(Phrase.build(Phrase.FINISHING_CONVERSION)); - } - else { - Chat.console(Phrase.build(Phrase.FINISHING_LOGGING)); - } - - if (ConfigHandler.migrationRunning) { - ConfigHandler.purgeRunning = false; - } - while ((Consumer.isRunning() || ConfigHandler.converterRunning) && !ConfigHandler.purgeRunning) { - long time = System.currentTimeMillis(); - if (time >= alertTime) { - if (!ConfigHandler.converterRunning) { - int consumerId = (Consumer.currentConsumer == 1) ? 1 : 0; - int consumerCount = Consumer.getConsumerSize(consumerId) + Process.getCurrentConsumerSize(); - Chat.console(Phrase.build(Phrase.LOGGING_ITEMS, String.format("%,d", consumerCount))); - } - alertTime = alertTime + (30 * 1000); - } - else if (!ConfigHandler.databaseReachable && (time - shutdownTime) >= (5 * 60 * 1000)) { - Chat.console(Phrase.build(Phrase.DATABASE_UNREACHABLE)); - break; - } - else if ((time - shutdownTime) >= (15 * 60 * 1000)) { - Chat.console(Phrase.build(Phrase.LOGGING_TIME_LIMIT)); - break; - } - - Thread.sleep(100); - } - - ConfigHandler.performDisable(); - Chat.console(Phrase.build(Phrase.DISABLE_SUCCESS, "CoreProtect v" + plugin.getDescription().getVersion())); - } - catch (Exception e) { - e.printStackTrace(); - } - } - } diff --git a/src/main/java/net/coreprotect/services/PluginInitializationService.java b/src/main/java/net/coreprotect/services/PluginInitializationService.java new file mode 100644 index 0000000..f79c7f7 --- /dev/null +++ b/src/main/java/net/coreprotect/services/PluginInitializationService.java @@ -0,0 +1,172 @@ +package net.coreprotect.services; + +import java.io.File; + +import org.bstats.bukkit.MetricsLite; +import org.bukkit.Bukkit; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.java.JavaPlugin; + +import net.coreprotect.CoreProtect; +import net.coreprotect.command.CommandHandler; +import net.coreprotect.command.TabHandler; +import net.coreprotect.config.Config; +import net.coreprotect.config.ConfigHandler; +import net.coreprotect.consumer.Consumer; +import net.coreprotect.language.Language; +import net.coreprotect.language.Phrase; +import net.coreprotect.listener.ListenerHandler; +import net.coreprotect.thread.CacheHandler; +import net.coreprotect.thread.NetworkHandler; +import net.coreprotect.thread.Scheduler; +import net.coreprotect.utility.Chat; +import net.coreprotect.utility.ChatUtils; + +/** + * Service responsible for plugin initialization tasks + */ +public class PluginInitializationService { + + private PluginInitializationService() { + throw new IllegalStateException("Utility class"); + } + + /** + * Initializes plugin components and configurations + * + * @param plugin + * The CoreProtect plugin instance + * @return true if initialization was successful, false otherwise + */ + public static boolean initializePlugin(CoreProtect plugin) { + // Load language phrases + Language.loadPhrases(); + + // Perform version checks + boolean start = VersionCheckService.performVersionChecks(); + if (!start) { + return false; + } + + try { + // Initialize core components + Consumer.initialize(); + new ListenerHandler(plugin); + + // Register commands + registerCommands(plugin); + + // Ensure data directory exists + createDataDirectory(); + + // Initialize configuration + start = ConfigHandler.performInitialization(true); + } + catch (Exception e) { + e.printStackTrace(); + return false; + } + + if (start) { + // Display startup messages + displayStartupMessages(plugin); + + // Start background services + startBackgroundServices(plugin); + + // Start metrics + enableMetrics(plugin); + } + + return start; + } + + /** + * Registers plugin commands + * + * @param plugin + * The CoreProtect plugin instance + */ + private static void registerCommands(JavaPlugin plugin) { + plugin.getCommand("coreprotect").setExecutor(CommandHandler.getInstance()); + plugin.getCommand("coreprotect").setTabCompleter(new TabHandler()); + plugin.getCommand("core").setExecutor(CommandHandler.getInstance()); + plugin.getCommand("core").setTabCompleter(new TabHandler()); + plugin.getCommand("co").setExecutor(CommandHandler.getInstance()); + plugin.getCommand("co").setTabCompleter(new TabHandler()); + } + + /** + * Creates the plugin data directory if it doesn't exist + */ + private static void createDataDirectory() { + boolean exists = (new File(ConfigHandler.path)).exists(); + if (!exists) { + new File(ConfigHandler.path).mkdir(); + } + } + + /** + * Displays startup messages in the console + * + * @param plugin + * The CoreProtect plugin instance + */ + private static void displayStartupMessages(JavaPlugin plugin) { + PluginDescriptionFile pluginDescription = plugin.getDescription(); + ChatUtils.sendConsoleComponentStartup(Bukkit.getServer().getConsoleSender(), Phrase.build(Phrase.ENABLE_SUCCESS, ConfigHandler.EDITION_NAME)); + + if (Config.getGlobal().MYSQL) { + Chat.console(Phrase.build(Phrase.USING_MYSQL)); + } + else { + Chat.console(Phrase.build(Phrase.USING_SQLITE)); + } + + Chat.console("--------------------"); + Chat.console(Phrase.build(Phrase.ENJOY_COREPROTECT, pluginDescription.getName())); + Chat.console(Phrase.build(Phrase.LINK_DISCORD, "www.coreprotect.net/discord/")); + Chat.console("--------------------"); + } + + /** + * Starts background services + * + * @param plugin + * The CoreProtect plugin instance + */ + private static void startBackgroundServices(CoreProtect plugin) { + // Start network handler + Scheduler.scheduleSyncDelayedTask(plugin, () -> { + try { + Thread networkHandler = new Thread(new NetworkHandler(true, true)); + networkHandler.start(); + } + catch (Exception e) { + e.printStackTrace(); + } + }, 0); + + // Start cache cleanup thread + Thread cacheCleanUpThread = new Thread(new CacheHandler()); + cacheCleanUpThread.start(); + + // Start consumer + Consumer.startConsumer(); + } + + /** + * Enables metrics reporting + * + * @param plugin + * The CoreProtect plugin instance + */ + private static void enableMetrics(JavaPlugin plugin) { + try { + new MetricsLite(plugin, 2876); + } + catch (Exception e) { + // Failed to connect to bStats server or something else went wrong + } + } +} diff --git a/src/main/java/net/coreprotect/services/ShutdownService.java b/src/main/java/net/coreprotect/services/ShutdownService.java new file mode 100644 index 0000000..a9548b2 --- /dev/null +++ b/src/main/java/net/coreprotect/services/ShutdownService.java @@ -0,0 +1,122 @@ +package net.coreprotect.services; + +import java.util.Iterator; +import java.util.Map.Entry; + +import org.bukkit.Location; +import org.bukkit.block.data.BlockData; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +import net.coreprotect.config.ConfigHandler; +import net.coreprotect.consumer.Consumer; +import net.coreprotect.consumer.process.Process; +import net.coreprotect.language.Phrase; +import net.coreprotect.listener.player.PlayerQuitListener; +import net.coreprotect.paper.PaperAdapter; +import net.coreprotect.utility.Chat; +import net.coreprotect.utility.Teleport; + +/** + * Service responsible for handling plugin shutdown operations + */ +public class ShutdownService { + + private static final long ALERT_INTERVAL_MS = 30 * 1000; // 30 seconds + private static final long MAX_SHUTDOWN_WAIT_MS = 15 * 60 * 1000; // 15 minutes + private static final long DB_UNREACHABLE_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes + + private ShutdownService() { + throw new IllegalStateException("Utility class"); + } + + /** + * Safely shuts down the plugin, ensuring all data is saved + * + * @param plugin + * The CoreProtect plugin instance + */ + public static void safeShutdown(Plugin plugin) { + try { + // Log disconnections of online players if server is stopping + if (ConfigHandler.serverRunning && PaperAdapter.ADAPTER.isStopping(plugin.getServer())) { + for (Player player : plugin.getServer().getOnlinePlayers()) { + PlayerQuitListener.queuePlayerQuit(player); + } + } + + // Revert any teleport blocks if not using Folia + if (!ConfigHandler.isFolia) { + revertTeleportBlocks(); + } + + ConfigHandler.serverRunning = false; + long shutdownTime = System.currentTimeMillis(); + long nextAlertTime = shutdownTime + ALERT_INTERVAL_MS; + + if (ConfigHandler.converterRunning) { + Chat.console(Phrase.build(Phrase.FINISHING_CONVERSION)); + } + else { + Chat.console(Phrase.build(Phrase.FINISHING_LOGGING)); + } + + if (ConfigHandler.migrationRunning) { + ConfigHandler.purgeRunning = false; + } + + waitForPendingOperations(shutdownTime, nextAlertTime); + + ConfigHandler.performDisable(); + Chat.console(Phrase.build(Phrase.DISABLE_SUCCESS, "CoreProtect v" + plugin.getDescription().getVersion())); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Waits for pending operations (consumer tasks or conversions) to complete + * + * @param shutdownTime + * The time when shutdown began + * @param nextAlertTime + * The time for the next status message + */ + private static void waitForPendingOperations(long shutdownTime, long nextAlertTime) throws InterruptedException { + while ((Consumer.isRunning() || ConfigHandler.converterRunning) && !ConfigHandler.purgeRunning) { + long currentTime = System.currentTimeMillis(); + + if (currentTime >= nextAlertTime) { + if (!ConfigHandler.converterRunning) { + int consumerId = (Consumer.currentConsumer == 1) ? 1 : 0; + int consumerCount = Consumer.getConsumerSize(consumerId) + Process.getCurrentConsumerSize(); + Chat.console(Phrase.build(Phrase.LOGGING_ITEMS, String.format("%,d", consumerCount))); + } + nextAlertTime = currentTime + ALERT_INTERVAL_MS; + } + else if (!ConfigHandler.databaseReachable && (currentTime - shutdownTime) >= DB_UNREACHABLE_TIMEOUT_MS) { + Chat.console(Phrase.build(Phrase.DATABASE_UNREACHABLE)); + break; + } + else if ((currentTime - shutdownTime) >= MAX_SHUTDOWN_WAIT_MS) { + Chat.console(Phrase.build(Phrase.LOGGING_TIME_LIMIT)); + break; + } + + Thread.sleep(100); + } + } + + /** + * Reverts any blocks that were temporarily changed during player teleports + */ + private static void revertTeleportBlocks() { + Iterator> iterator = Teleport.revertBlocks.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); + entry.getKey().getBlock().setBlockData(entry.getValue()); + iterator.remove(); + } + } +} diff --git a/src/main/java/net/coreprotect/services/VersionCheckService.java b/src/main/java/net/coreprotect/services/VersionCheckService.java new file mode 100644 index 0000000..f3cdf24 --- /dev/null +++ b/src/main/java/net/coreprotect/services/VersionCheckService.java @@ -0,0 +1,65 @@ +package net.coreprotect.services; + +import org.bukkit.Bukkit; + +import net.coreprotect.config.ConfigHandler; +import net.coreprotect.language.Phrase; +import net.coreprotect.utility.Chat; +import net.coreprotect.utility.Color; +import net.coreprotect.utility.VersionUtils; + +/** + * Service responsible for checking compatibility of Minecraft, Java versions, + * and plugin branch validation. + */ +public class VersionCheckService { + + private VersionCheckService() { + throw new IllegalStateException("Utility class"); + } + + /** + * Performs all necessary version checks during plugin startup + * + * @return true if all version checks pass, false otherwise + */ + public static boolean performVersionChecks() { + try { + // Check Minecraft version compatibility + String[] bukkitVersion = Bukkit.getServer().getBukkitVersion().split("[-.]"); + if (VersionUtils.newVersion(bukkitVersion[0] + "." + bukkitVersion[1], ConfigHandler.MINECRAFT_VERSION)) { + Chat.console(Phrase.build(Phrase.VERSION_REQUIRED, "Minecraft", ConfigHandler.MINECRAFT_VERSION)); + return false; + } + + if (VersionUtils.newVersion(ConfigHandler.LATEST_VERSION, bukkitVersion[0] + "." + bukkitVersion[1]) && VersionUtils.isBranch("master")) { + Chat.console(Phrase.build(Phrase.VERSION_INCOMPATIBLE, "Minecraft", bukkitVersion[0] + "." + bukkitVersion[1])); + return false; + } + + // Check Java version compatibility + String[] javaVersion = (System.getProperty("java.version").replaceAll("[^0-9.]", "") + ".0").split("\\."); + if (VersionUtils.newVersion(javaVersion[0] + "." + javaVersion[1], ConfigHandler.JAVA_VERSION)) { + Chat.console(Phrase.build(Phrase.VERSION_REQUIRED, "Java", ConfigHandler.JAVA_VERSION)); + return false; + } + + // Branch validation + if (ConfigHandler.EDITION_BRANCH.length() == 0) { + Chat.sendConsoleMessage(Color.RED + "[CoreProtect] " + Phrase.build(Phrase.INVALID_BRANCH_1)); + Chat.sendConsoleMessage(Color.GREY + "[CoreProtect] " + Phrase.build(Phrase.INVALID_BRANCH_2)); + Chat.sendConsoleMessage(Color.GREY + "[CoreProtect] " + Phrase.build(Phrase.INVALID_BRANCH_3)); + return false; + } + + // Store Minecraft server version for later use + ConfigHandler.SERVER_VERSION = Integer.parseInt(bukkitVersion[1]); + } + catch (Exception e) { + e.printStackTrace(); + return false; + } + + return true; + } +}