From 72f018991a6449be5bfb4a6aec8a19645db9cd72 Mon Sep 17 00:00:00 2001 From: Articdive <13535885+Articdive@users.noreply.github.com> Date: Wed, 29 Sep 2021 21:52:33 +0200 Subject: [PATCH] Improve logging (#5299) * Improve Towny's Logging System. * Improve encapsulation of Towny and TownyUniverse. * Remove the old outpost checker. * Undo re-ordered imports. * A couple more import diffs. * Fix errors with the new logger. * Fixes & Improvements. - TIE-specific messages not being logged to console. - Skip changelog/updatingconfigversion when we're isError()'d. - Javadoc not building. - Specific TIEs for loading/saving database vs loading/saving database.yml file. - A typo. * Drop the \'s down a notch. Co-authored-by: Llm Dl --- resources/outpostschecked.txt | 7 - .../bukkit/config/CommentedConfiguration.java | 6 +- src/com/palmergames/bukkit/towny/Towny.java | 299 ++++++++++++++---- .../bukkit/towny/TownySettings.java | 5 +- .../bukkit/towny/TownyUniverse.java | 231 +++----------- .../towny/command/TownyAdminCommand.java | 37 +-- .../bukkit/towny/db/DatabaseConfig.java | 6 +- .../bukkit/towny/db/TownyDatabaseHandler.java | 19 -- .../initialization/TownyInitException.java | 42 +++ .../bukkit/towny/permissions/TownyPerms.java | 28 +- 10 files changed, 354 insertions(+), 326 deletions(-) delete mode 100644 resources/outpostschecked.txt create mode 100644 src/com/palmergames/bukkit/towny/exceptions/initialization/TownyInitException.java diff --git a/resources/outpostschecked.txt b/resources/outpostschecked.txt deleted file mode 100644 index 3d4e316541..0000000000 --- a/resources/outpostschecked.txt +++ /dev/null @@ -1,7 +0,0 @@ -If this file doesn't exist (you delete it), outposts will be checked (again) for validity. -Towny will automatically fix any outpostspawns that exist in townblocks not claimed by any town. -There may be other outpostspawns in a town, which cannot be removed automatically. -This is because they are still claimed by a town but are not recorded properly as outposts. -This is done per town and meant to fix a long-running bug that is limited to MySQL databases only. -Because you're reading this now, the check has already been completed once. -If this is not the case then please report the issue on Github or IRC. \ No newline at end of file diff --git a/src/com/palmergames/bukkit/config/CommentedConfiguration.java b/src/com/palmergames/bukkit/config/CommentedConfiguration.java index f0f5950326..3c9dca8fec 100644 --- a/src/com/palmergames/bukkit/config/CommentedConfiguration.java +++ b/src/com/palmergames/bukkit/config/CommentedConfiguration.java @@ -35,18 +35,16 @@ public CommentedConfiguration(File file) { public boolean load() { - boolean loaded = true; - try { this.load(file); } catch (InvalidConfigurationException | IOException e) { Towny.getPlugin().getLogger().warning(String.format("Loading error: Failed to load file %s (does it pass a yaml parser?).", file.getPath())); Towny.getPlugin().getLogger().warning("https://jsonformatter.org/yaml-parser"); Towny.getPlugin().getLogger().warning(e.getMessage()); - loaded = false; + return false; } - return loaded; + return true; } public void save() { diff --git a/src/com/palmergames/bukkit/towny/Towny.java b/src/com/palmergames/bukkit/towny/Towny.java index ca166835dd..42361d5cca 100644 --- a/src/com/palmergames/bukkit/towny/Towny.java +++ b/src/com/palmergames/bukkit/towny/Towny.java @@ -1,7 +1,9 @@ package com.palmergames.bukkit.towny; import com.earth2me.essentials.Essentials; +import com.palmergames.bukkit.config.CommentedConfiguration; import com.palmergames.bukkit.config.ConfigNodes; +import com.palmergames.bukkit.config.migration.ConfigMigrator; import com.palmergames.bukkit.towny.chat.TNCRegister; import com.palmergames.bukkit.towny.command.InviteCommand; import com.palmergames.bukkit.towny.command.NationCommand; @@ -15,8 +17,10 @@ import com.palmergames.bukkit.towny.command.commandobjects.CancelCommand; import com.palmergames.bukkit.towny.command.commandobjects.ConfirmCommand; import com.palmergames.bukkit.towny.command.commandobjects.DenyCommand; +import com.palmergames.bukkit.towny.db.DatabaseConfig; import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; import com.palmergames.bukkit.towny.exceptions.TownyException; +import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; import com.palmergames.bukkit.towny.hooks.LuckPermsContexts; import com.palmergames.bukkit.towny.huds.HUDManager; import com.palmergames.bukkit.towny.invites.InviteHandler; @@ -33,7 +37,9 @@ import com.palmergames.bukkit.towny.object.Coord; import com.palmergames.bukkit.towny.object.PlayerCache; import com.palmergames.bukkit.towny.object.Resident; +import com.palmergames.bukkit.towny.object.Translation; import com.palmergames.bukkit.towny.object.WorldCoord; +import com.palmergames.bukkit.towny.object.metadata.MetadataLoader; import com.palmergames.bukkit.towny.permissions.BukkitPermSource; import com.palmergames.bukkit.towny.permissions.GroupManagerSource; import com.palmergames.bukkit.towny.permissions.TownyPerms; @@ -46,6 +52,7 @@ import com.palmergames.bukkit.towny.war.common.WarZoneListener; import com.palmergames.bukkit.util.BukkitTools; import com.palmergames.bukkit.util.Version; +import com.palmergames.util.FileMgmt; import com.palmergames.util.JavaUtil; import com.palmergames.util.StringMgmt; @@ -66,6 +73,7 @@ import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; @@ -75,6 +83,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.logging.Level; /** * Towny Plugin for Bukkit @@ -107,8 +116,8 @@ public class Towny extends JavaPlugin { private Essentials essentials = null; private boolean citizens2 = false; - - private boolean error = false; + + private final List errors = new ArrayList<>(); private static Towny plugin; @@ -126,8 +135,7 @@ public void onEnable() { townyUniverse = TownyUniverse.getInstance(); - - // Setup classes + // Setup static classes BukkitTools.initialize(this); TownyTimerHandler.initialize(this); TownyEconomyHandler.initialize(this); @@ -135,33 +143,55 @@ public void onEnable() { PlayerCacheUtil.initialize(this); TownyPerms.initialize(this); InviteHandler.initialize(this); + TownyTimerHandler.toggleGatherResidentUUIDTask(false); - - if (load()) { - // Initialize SpawnUtil only after the Translation class has figured out a language, - // to avoid ExceptionInInitializerError exceptions from SpawnType. + try { + // Load the foundation of Towny, containing config, locales, database. + loadFoundation(false); + + // Check for plugins that we use or we develop. + // N.B. Includes the hook for TownyChat + checkPlugins(); + // Make sure the timers are stopped for a reset, then started. + cycleTimers(); + // Reset the player cache. + resetCache(); + // Check for plugin updates + TownyUpdateChecker.checkForUpdates(this); + // Initialize SpawnUtil only after the Translation class has figured out a language. + // N.B. Important that localization loaded correctly for this step. SpawnUtil.initialize(this); - // Setup bukkit command interfaces registerSpecialCommands(); registerCommands(); - // Add custom metrics charts. addMetricsCharts(); + } catch (TownyInitException tie) { + addError(tie.getError()); + getLogger().log(Level.SEVERE, tie.getMessage(), tie.getStackTrace()); + } + + // NOTE: Runs regardless if Towny errors out! + // Important for safe mode. - adventure = BukkitAudiences.create(this); + adventure = BukkitAudiences.create(this); - if (TownySettings.isTownyUpdating(getVersion())) { - - printChangelogToConsole(); - // Update config with new version. - TownySettings.setLastRunVersion(getVersion()); - // Save database. - townyUniverse.getDataSource().saveAll(); - // cleanup() updates SQL schema for any changes. - townyUniverse.getDataSource().cleanup(); - } + // If we aren't going to enter safe mode, do the following: + if (!isError() &&TownySettings.isTownyUpdating(getVersion())) { + printChangelogToConsole(); + // Update config with new version. + TownySettings.setLastRunVersion(getVersion()); + // Save database. + townyUniverse.getDataSource().saveAll(); + // cleanup() updates SQL schema for any changes. + townyUniverse.getDataSource().cleanup(); + } + + // It is probably a good idea to always handle permissions + // However, this would spit out an ugly Exception if perms or the config are bugged. + // Hence, these if checks. + if (!isError(TownyInitException.TownyError.MAIN_CONFIG) && !isError(TownyInitException.TownyError.PERMISSIONS)) { // Register all child permissions for ranks TownyPerms.registerPermissionNodes(); } @@ -195,12 +225,174 @@ public void onEnable() { } } + public void loadFoundation(boolean reload) { + // Before anything can continue we must load the config files, database and set the foundation for Towny. + loadConfig(reload); + // Then load the language files. + loadLocalization(reload); + // Then load the database. + loadDatabaseConfig(reload); + // Then load permissions + loadPermissions(reload); + + // Initialize the special log4j hook logger. + TownyLogger.getInstance(); + + // Clear all objects from the TownyUniverse class. + townyUniverse.clearAllObjects(); + + // Try to load and save the database. + townyUniverse.loadAndSaveDatabase(TownySettings.getLoadDatabase(), TownySettings.getSaveDatabase()); + + // Schedule metadata to be loaded + MetadataLoader.getInstance().scheduleDeserialization(); + + // Try migrating the config and world files if the version has changed. + if (!TownySettings.getLastRunVersion().equals(getVersion())) { + ConfigMigrator migrator = new ConfigMigrator(TownySettings.getConfig(), "config-migration.json"); + migrator.migrate(); + } + + // Loads Town and Nation Levels after migration has occured. + loadTownAndNationLevels(); + + // Run both the cleanup and backup async. + townyUniverse.performCleanupAndBackup(); + } + + private void loadConfig(boolean reload) { + // TODO: Rewrite CommentedConfiguration to take java.nio.Path instead of File. + // There is probably a lot of performance improvements possible for the CommentedConfiguration - Articdive. + TownySettings.loadConfig(getDataFolder().toPath().resolve("settings").resolve("config.yml").toString(), getVersion()); + if (reload) { + // If Towny is in Safe Mode (for the main config) turn off Safe Mode. + if (isError(TownyInitException.TownyError.MAIN_CONFIG)) { + removeError(TownyInitException.TownyError.MAIN_CONFIG); + } + } + } + + private void loadLocalization(boolean reload) { + Translation.loadTranslationRegistry(); + if (reload) { + // If Towny is in Safe Mode (because of localization) turn off Safe Mode. + if (isError(TownyInitException.TownyError.LOCALIZATION)) { + removeError(TownyInitException.TownyError.LOCALIZATION); + } + } + } + + private void loadDatabaseConfig(boolean reload) { + if (!checkForLegacyDatabaseConfig()) { + throw new TownyInitException("Unable to migrate old database settings to Towny\\data\\settings\\database.yml", TownyInitException.TownyError.DATABASE_CONFIG); + } + DatabaseConfig.loadDatabaseConfig(getDataFolder().toPath().resolve("settings").resolve("database.yml").toString()); + if (reload) { + // If Towny is in Safe Mode (because of localization) turn off Safe Mode. + if (isError(TownyInitException.TownyError.DATABASE_CONFIG)) { + removeError(TownyInitException.TownyError.DATABASE_CONFIG); + } + } + } + + public void loadPermissions(boolean reload) { + TownyPerms.loadPerms(getDataFolder().toPath().resolve("settings").toString(), "townyperms.yml"); + // This will only run if permissions is fine. + if (reload) { + // If Towny is in Safe Mode (for Permissions) turn off Safe Mode. + if (isError(TownyInitException.TownyError.PERMISSIONS)) { + removeError(TownyInitException.TownyError.PERMISSIONS); + } + // Update everyone who is online with the changes made. + TownyPerms.updateOnlinePerms(); + } + } + + /** + * Loads the Town and Nation Levels from the config.yml + * + * @return true if they have the required elements. + */ + private void loadTownAndNationLevels() { + // Load Nation & Town level data into maps. + try { + TownySettings.loadTownLevelConfig(); + } catch (IOException e) { + throw new TownyInitException("Failed to load town level config", TownyInitException.TownyError.MAIN_CONFIG); + } + try { + TownySettings.loadNationLevelConfig(); + } catch (IOException e) { + throw new TownyInitException("Failed to load nation level config", TownyInitException.TownyError.MAIN_CONFIG); + } + } + + /** + * Converts the older config.yml's database settings into the database.yml file. + * @return true if successful + * @since 0.97.0.24 + */ + private boolean checkForLegacyDatabaseConfig() { + File file = getDataFolder().toPath().resolve("settings").resolve("config.yml").toFile(); + // Bail if the config doesn't exist at all yet. + if (!file.exists()) + return true; + + CommentedConfiguration config = new CommentedConfiguration(file); + // return false if the config cannot be loaded. + if (!config.load()) + return false; + if (config.contains("plugin.database.database_load")) { + /* + * Get old settings from config. + */ + String dbload = config.getString("plugin.database.database_load"); + String dbsave = config.getString("plugin.database.database_save"); + String hostname = config.getString("plugin.database.sql.hostname"); + String port = config.getString("plugin.database.sql.port"); + String dbname = config.getString("plugin.database.sql.dbname"); + String tableprefix = config.getString("plugin.database.sql.table_prefix"); + String username = config.getString("plugin.database.sql.username"); + String password = config.getString("plugin.database.sql.password"); + String flags = config.getString("plugin.database.sql.flags"); + String max_pool = config.getString("plugin.database.sql.pooling.max_pool_size"); + String max_lifetime = config.getString("plugin.database.sql.pooling.max_lifetime"); + String connection_timeout = config.getString("plugin.database.sql.pooling.connection_timeout"); + + /* + * Create database.yml if it doesn't exist yet, with new settings. + */ + String databaseFilePath = getDataFolder().toPath().resolve("settings").resolve("database.yml").toString(); + if (FileMgmt.checkOrCreateFile(databaseFilePath)) { + CommentedConfiguration databaseConfig = new CommentedConfiguration(new File(databaseFilePath)); + databaseConfig.set("database.database_load", dbload); + databaseConfig.set("database.database_save", dbsave); + databaseConfig.set("database.sql.hostname", hostname); + databaseConfig.set("database.sql.port", port); + databaseConfig.set("database.sql.dbname", dbname); + databaseConfig.set("database.sql.table_prefix", tableprefix); + databaseConfig.set("database.sql.username", username); + databaseConfig.set("database.sql.password", password); + databaseConfig.set("database.sql.flags", flags); + databaseConfig.set("database.sql.pooling.max_pool_size", max_pool); + databaseConfig.set("database.sql.pooling.max_lifetime", max_lifetime); + databaseConfig.set("database.sql.pooling.connection_timeout", connection_timeout); + databaseConfig.save(); + getLogger().info("Database settings migrated to towny\\data\\settings\\database.yml"); + } else { + getLogger().severe("Unable to migrate old database settings to towny\\data\\settings\\database.yml"); + return false; + } + } + return true; + } + @Override public void onDisable() { Bukkit.getLogger().info("=============================================================="); TownyUniverse townyUniverse = TownyUniverse.getInstance(); - if (townyUniverse.getDataSource() != null && !error) { + if (townyUniverse.getDataSource() != null && !isError()) { townyUniverse.getDataSource().saveQueues(); } @@ -235,39 +427,6 @@ public void onDisable() { Bukkit.getLogger().info("============================================================="); } - /** - * Attempts to load language, config and database files. - * Checks for plugins, ends and begins timers, resets player cache. - * - * @return true if things have loaded without error. - */ - public boolean load() { - - // Things which have to be done first. - checkCitizens(); - TownyTimerHandler.toggleGatherResidentUUIDTask(false); - - // Load Config, Language Files, Database. - if (!townyUniverse.loadSettings()) { - setError(true); - return false; - } - - // Check for plugins that we use, we develop. - checkPlugins(); - - // Make sure the timers are stopped for a reset, then started. - cycleTimers(); - - // Reset player cache. - resetCache(); - - //Check for plugin updates - TownyUpdateChecker.checkForUpdates(this); - - return true; - } - public void checkCitizens() { /* * Test for Citizens2 so we can avoid removing their NPC's @@ -276,7 +435,8 @@ public void checkCitizens() { } private void checkPlugins() { - + + checkCitizens(); plugin.getLogger().info("Searching for third-party plugins..."); String ecowarn = ""; List addons = new ArrayList<>(); @@ -520,16 +680,24 @@ public String getVersion() { * @return the error */ public boolean isError() { - - return error; + return !errors.isEmpty(); + } + + private boolean isError(@NotNull TownyInitException.TownyError error) { + return errors.contains(error); + } + + public void addError(@NotNull TownyInitException.TownyError error) { + errors.add(error); + } + + private void removeError(@NotNull TownyInitException.TownyError error) { + errors.remove(error); } - /** - * @param error the error to set - */ - public void setError(boolean error) { - - this.error = error; + @NotNull + public List getErrors() { + return errors; } // is Essentials active @@ -822,7 +990,7 @@ private void registerSpecialCommands() { commandMap.registerAll("towny", commands); } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); + throw new TownyInitException("An issue has occured while registering custom commands.", TownyInitException.TownyError.OTHER, e); } } @@ -888,4 +1056,5 @@ private static boolean isSpigotOrDerivative() { } } + } diff --git a/src/com/palmergames/bukkit/towny/TownySettings.java b/src/com/palmergames/bukkit/towny/TownySettings.java index 95a16a27d7..40e4f5cbd8 100644 --- a/src/com/palmergames/bukkit/towny/TownySettings.java +++ b/src/com/palmergames/bukkit/towny/TownySettings.java @@ -8,6 +8,7 @@ import com.palmergames.bukkit.towny.event.TownUpkeepCalculationEvent; import com.palmergames.bukkit.towny.event.TownUpkeepPenalityCalculationEvent; import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; +import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.NationSpawnLevel.NSpawnLevel; import com.palmergames.bukkit.towny.object.Resident; @@ -284,14 +285,14 @@ public static int calcNationLevel(Nation nation) { return 0; } - public static void loadConfig(String filepath, String version) throws IOException { + public static void loadConfig(String filepath, String version) { if (FileMgmt.checkOrCreateFile(filepath)) { File file = new File(filepath); // read the config.yml into memory config = new CommentedConfiguration(file); if (!config.load()) { - Towny.getPlugin().getLogger().severe("Failed to load Config!"); + throw new TownyInitException("Failed to load Towny's config.yml.", TownyInitException.TownyError.MAIN_CONFIG); } setDefaults(version, file); diff --git a/src/com/palmergames/bukkit/towny/TownyUniverse.java b/src/com/palmergames/bukkit/towny/TownyUniverse.java index af09dd348f..e7a6c23a85 100644 --- a/src/com/palmergames/bukkit/towny/TownyUniverse.java +++ b/src/com/palmergames/bukkit/towny/TownyUniverse.java @@ -1,11 +1,7 @@ package com.palmergames.bukkit.towny; import com.palmergames.annotations.Unmodifiable; -import com.palmergames.bukkit.config.CommentedConfiguration; -import com.palmergames.bukkit.config.migration.ConfigMigrator; -import com.palmergames.bukkit.towny.db.DatabaseConfig; import com.palmergames.bukkit.towny.db.TownyDataSource; -import com.palmergames.bukkit.towny.db.TownyDatabaseHandler; import com.palmergames.bukkit.towny.db.TownyFlatFileSource; import com.palmergames.bukkit.towny.db.TownySQLSource; import com.palmergames.bukkit.towny.event.TownyLoadedDatabaseEvent; @@ -13,7 +9,7 @@ import com.palmergames.bukkit.towny.exceptions.InvalidNameException; import com.palmergames.bukkit.towny.exceptions.KeyAlreadyRegisteredException; import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; -import com.palmergames.bukkit.towny.exceptions.TownyException; +import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.PlotGroup; import com.palmergames.bukkit.towny.object.Resident; @@ -21,20 +17,16 @@ import com.palmergames.bukkit.towny.object.Town; import com.palmergames.bukkit.towny.object.TownBlock; import com.palmergames.bukkit.towny.object.TownyWorld; -import com.palmergames.bukkit.towny.object.Translation; import com.palmergames.bukkit.towny.object.WorldCoord; import com.palmergames.bukkit.towny.object.jail.Jail; import com.palmergames.bukkit.towny.object.map.TownyMapData; import com.palmergames.bukkit.towny.object.metadata.CustomDataField; -import com.palmergames.bukkit.towny.object.metadata.MetadataLoader; import com.palmergames.bukkit.towny.permissions.TownyPermissionSource; -import com.palmergames.bukkit.towny.permissions.TownyPerms; import com.palmergames.bukkit.towny.tasks.BackupTask; import com.palmergames.bukkit.towny.tasks.CleanupTask; import com.palmergames.bukkit.towny.war.eventwar.War; import com.palmergames.bukkit.util.BukkitTools; import com.palmergames.bukkit.util.NameValidation; -import com.palmergames.util.FileMgmt; import com.palmergames.util.Trie; import org.apache.commons.lang.Validate; import org.bukkit.Bukkit; @@ -44,8 +36,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -111,66 +101,6 @@ public static TownyUniverse getInstance() { return instance; } - /** - * Loads Towny's files/database en masse. Will end up in safemode if things do not go well. - * - * Loads config/language/townyperms files. - * Initiates the logger. - * Flushes object maps. - * Saves and loads the database. - * Will migrate the config if needed. - * Loads the town and nation levels. - * Legacy outpost test. - * Schedule cleanup and backup. - * - * @return true if things go well. - */ - boolean loadSettings() { - - // Load config, language and townyperms files. - if (!loadFiles()) - return false; - - // Init logger - TownyLogger.getInstance(); - - // Clears the object maps from memory. - clearAllObjects(); - - // Try to load and save the database. - if (!loadAndSaveDatabase(TownySettings.getLoadDatabase(), TownySettings.getSaveDatabase())) - return false; - - // Schedule metadata to be loaded - MetadataLoader.getInstance().scheduleDeserialization(); - - // Try migrating the config and world files if the version has changed. - if (!TownySettings.getLastRunVersion().equals(towny.getVersion())) { - ConfigMigrator migrator = new ConfigMigrator(TownySettings.getConfig(), "config-migration.json"); - migrator.migrate(); - } - - // Loads Town and Nation Levels after migration has occured. - if (!loadTownAndNationLevels()) - return false; - - File f = new File(rootFolder, "outpostschecked.txt"); // Old towny didn't keep as good track of outpost spawn points, - if (!f.exists()) { // some of them ending up outside of claimed plots. If the file - for (Town town : dataSource.getTowns()) TownyDatabaseHandler.validateTownOutposts(town); // does not exist we will test all outpostspawns and create the - towny.saveResource("outpostschecked.txt", false); // file. Sometimes still useful on servers who've manually - } // altered data manually and want to re-check. - - // Run both the cleanup and backup async. - performCleanupAndBackup(); - - // Things would appear to have gone well. - return true; - } - - /* - * loadSettings() functions. - */ - /** * Performs CleanupTask and BackupTask in async, */ @@ -180,26 +110,6 @@ public void performCleanupAndBackup() { .thenRunAsync(new BackupTask()); } - /** - * Load config, language and townyperms files. - * - * @return true if no exceptions are found. - */ - private boolean loadFiles() { - try { - if (!checkForLegacyDatabaseConfig()) - return false; - DatabaseConfig.loadDatabaseConfig(rootFolder + File.separator + "settings" + File.separator + "database.yml"); - TownySettings.loadConfig(rootFolder + File.separator + "settings" + File.separator + "config.yml", towny.getVersion()); - Translation.loadTranslationRegistry(); - TownyPerms.loadPerms(rootFolder + File.separator + "settings", "townyperms.yml"); - } catch (IOException | TownyException e) { - e.printStackTrace(); - return false; - } - return true; - } - /** * Clears the object maps. */ @@ -225,29 +135,22 @@ public void clearAllObjects() { * * @param loadDbType - load setting from the config. * @param saveDbType - save setting from the config. - * @return true when the databse will load and save. */ - private boolean loadAndSaveDatabase(String loadDbType, String saveDbType) { + void loadAndSaveDatabase(String loadDbType, String saveDbType) { towny.getLogger().info("Database: [Load] " + loadDbType + " [Save] " + saveDbType); - // Try loading the database. - long startTime = System.currentTimeMillis(); - if (!loadDatabase(loadDbType)) { - towny.getLogger().severe("Error: Failed to load!"); - return false; - } else { - Bukkit.getPluginManager().callEvent(new TownyLoadedDatabaseEvent()); - } + try { + // Try loading the database. + loadDatabase(loadDbType); + } catch (TownyInitException e) { + throw new TownyInitException(e.getMessage(), e.getError()); + } - long time = System.currentTimeMillis() - startTime; - towny.getLogger().info("Database: Loaded in " + time + "ms."); - towny.getLogger().info("Database: " + TownySettings.getUUIDPercent() + " of residents have stored UUIDs."); // TODO: remove this when we're using UUIDs directly in the database. - - // Try saving the database. - if (!saveDatabase(saveDbType)) { - towny.getLogger().severe("Error: Unsupported save format!"); - return false; - } - return true; + try { + // Try saving the database. + saveDatabase(saveDbType); + } catch (TownyInitException e) { + throw new TownyInitException(e.getMessage(), e.getError()); + } } /** @@ -258,6 +161,11 @@ private boolean loadAndSaveDatabase(String loadDbType, String saveDbType) { */ private boolean loadDatabase(String loadDbType) { + long startTime = System.currentTimeMillis(); + + /* + * Select the datasource. + */ switch (loadDbType.toLowerCase()) { case "ff": case "flatfile": { @@ -269,11 +177,25 @@ private boolean loadDatabase(String loadDbType) { break; } default: { - return false; + throw new TownyInitException("Database: Database.yml unsupported load format: " + loadDbType, TownyInitException.TownyError.DATABASE_CONFIG); } } - return dataSource.loadAll(); + /* + * Load the actual database. + */ + if (!dataSource.loadAll()) + throw new TownyInitException("Database: Failed to load database.", TownyInitException.TownyError.DATABASE); + + long time = System.currentTimeMillis() - startTime; + towny.getLogger().info("Database: Loaded in " + time + "ms."); + towny.getLogger().info("Database: " + TownySettings.getUUIDPercent() + " of residents have stored UUIDs."); // TODO: remove this when we're using UUIDs directly in the database. + + // Throw Event. + Bukkit.getPluginManager().callEvent(new TownyLoadedDatabaseEvent()); + + // Congratulations the Database loaded. + return true; } /** @@ -295,7 +217,9 @@ private boolean saveDatabase(String saveDbType) { this.dataSource = new TownySQLSource(towny, this); break; } - default: {} + default: { + throw new TownyInitException("Database.yml contains unsupported save format: " + saveDbType, TownyInitException.TownyError.DATABASE); + } } if (TownySettings.getLoadDatabase().equalsIgnoreCase(saveDbType)) { @@ -307,87 +231,10 @@ private boolean saveDatabase(String saveDbType) { } return true; } catch (UnsupportedOperationException e) { - return false; + throw new TownyInitException("Database: Failed to save database!", TownyInitException.TownyError.DATABASE); } } - /** - * Converts the older config.yml's database settings into the database.yml file. - * @return true if successful - * @since 0.97.0.24 - */ - private boolean checkForLegacyDatabaseConfig() { - File file = new File(rootFolder + File.separator + "settings" + File.separator + "config.yml"); - // Bail if the config doesn't exist at all yet. - if (!file.exists()) - return true; - - CommentedConfiguration config = new CommentedConfiguration(file); - // return false if the config cannot be loaded. - if (!config.load()) - return false; - if (config.contains("plugin.database.database_load")) { - /* - * Get old settings from config. - */ - String dbload = config.getString("plugin.database.database_load"); - String dbsave = config.getString("plugin.database.database_save"); - String hostname = config.getString("plugin.database.sql.hostname"); - String port = config.getString("plugin.database.sql.port"); - String dbname = config.getString("plugin.database.sql.dbname"); - String tableprefix = config.getString("plugin.database.sql.table_prefix"); - String username = config.getString("plugin.database.sql.username"); - String password = config.getString("plugin.database.sql.password"); - String flags = config.getString("plugin.database.sql.flags"); - String max_pool = config.getString("plugin.database.sql.pooling.max_pool_size"); - String max_lifetime = config.getString("plugin.database.sql.pooling.max_lifetime"); - String connection_timeout = config.getString("plugin.database.sql.pooling.connection_timeout"); - - /* - * Create database.yml if it doesn't exist yet, with new settings. - */ - String databaseFilePath = rootFolder + File.separator + "settings" + File.separator + "database.yml"; - if (FileMgmt.checkOrCreateFile(databaseFilePath)) { - CommentedConfiguration databaseConfig = new CommentedConfiguration(new File(databaseFilePath)); - databaseConfig.set("database.database_load", dbload); - databaseConfig.set("database.database_save", dbsave); - databaseConfig.set("database.sql.hostname", hostname); - databaseConfig.set("database.sql.port", port); - databaseConfig.set("database.sql.dbname", dbname); - databaseConfig.set("database.sql.table_prefix", tableprefix); - databaseConfig.set("database.sql.username", username); - databaseConfig.set("database.sql.password", password); - databaseConfig.set("database.sql.flags", flags); - databaseConfig.set("database.sql.pooling.max_pool_size", max_pool); - databaseConfig.set("database.sql.pooling.max_lifetime", max_lifetime); - databaseConfig.set("database.sql.pooling.connection_timeout", connection_timeout); - databaseConfig.save(); - towny.getLogger().info("Database settings migrated to towny\\data\\settings\\database.yml"); - } else { - towny.getLogger().severe("Unable to migrate old database settings to towny\\data\\settings\\database.yml"); - return false; - } - } - return true; - } - - - /** - * Loads the Town and Nation Levels from the config.yml - * - * @return true if they have the required elements. - */ - private boolean loadTownAndNationLevels() { - // Load Nation & Town level data into maps. - try { - TownySettings.loadTownLevelConfig(); - TownySettings.loadNationLevelConfig(); - return true; - } catch (IOException e) { - return false; - } - } - /** * Run during onDisable() to finish cleanup and backup. */ diff --git a/src/com/palmergames/bukkit/towny/command/TownyAdminCommand.java b/src/com/palmergames/bukkit/towny/command/TownyAdminCommand.java index 937742f5be..2510f7be40 100644 --- a/src/com/palmergames/bukkit/towny/command/TownyAdminCommand.java +++ b/src/com/palmergames/bukkit/towny/command/TownyAdminCommand.java @@ -24,6 +24,7 @@ import com.palmergames.bukkit.towny.exceptions.InvalidNameException; import com.palmergames.bukkit.towny.exceptions.NotRegisteredException; import com.palmergames.bukkit.towny.exceptions.TownyException; +import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; import com.palmergames.bukkit.towny.object.Coord; import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.Resident; @@ -1932,22 +1933,15 @@ public void reloadLangs() { } public void reloadPerms() { - String rootFolder = TownyUniverse.getInstance().getRootFolder(); try { - TownyPerms.loadPerms(rootFolder + File.separator + "settings", "townyperms.yml"); - } catch (TownyException e) { - // Place Towny in Safe Mode while the townyperms.yml is unreadable. - plugin.setError(true); + plugin.loadPermissions(true); + } catch (TownyInitException tie) { TownyMessaging.sendErrorMsg(sender, "Error Loading townyperms.yml!"); + TownyMessaging.sendErrorMsg(tie.getMessage()); + // Place Towny in Safe Mode while the townyperms.yml is unreadable. + plugin.addError(tie.getError()); return; } - // If Towny is in Safe Mode (hopefully because of townyperms only) turn off Safe Mode. - // TODO: Potentially do a full towny reload via the normal TownyUniverse.loadSettings() so that we would know if there would be a reason to have safe mode remain on. - if (plugin.isError()) - plugin.setError(false); - - // Update everyone who is online with the changes made. - TownyPerms.updateOnlinePerms(); TownyMessaging.sendMsg(sender, Translatable.of("msg_reloaded_perms")); } @@ -1985,16 +1979,19 @@ public void reloadConfig(boolean reset) { */ public void reloadDatabase() { TownyUniverse.getInstance().getDataSource().finishTasks(); - if (plugin.load()) { - - // Register all child permissions for ranks - TownyPerms.registerPermissionNodes(); - - // Update permissions for all online players - TownyPerms.updateOnlinePerms(); - + try { + plugin.loadFoundation(true); + } catch (TownyInitException tie) { + TownyMessaging.sendErrorMsg(tie.getMessage()); + + plugin.addError(tie.getError()); + return; } + // Register all child permissions for ranks + TownyPerms.registerPermissionNodes(); + // Update permissions for all online players + TownyPerms.updateOnlinePerms(); TownyMessaging.sendMsg(sender, Translatable.of("msg_reloaded_db")); } diff --git a/src/com/palmergames/bukkit/towny/db/DatabaseConfig.java b/src/com/palmergames/bukkit/towny/db/DatabaseConfig.java index b01e2acf29..56ab2bf78c 100644 --- a/src/com/palmergames/bukkit/towny/db/DatabaseConfig.java +++ b/src/com/palmergames/bukkit/towny/db/DatabaseConfig.java @@ -4,6 +4,7 @@ import com.palmergames.bukkit.config.CommentedConfiguration; import com.palmergames.bukkit.towny.Towny; +import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; import com.palmergames.util.FileMgmt; public enum DatabaseConfig { @@ -94,9 +95,8 @@ public static void loadDatabaseConfig(String filepath) { // read the config.yml into memory databaseConfig = new CommentedConfiguration(file); - if (!databaseConfig.load()) { - Towny.getPlugin().getLogger().severe("Failed to load database.yml!"); - } + if (!databaseConfig.load()) + throw new TownyInitException("Database: Failed to load database.yml!", TownyInitException.TownyError.DATABASE_CONFIG); setDatabaseDefaults(file); diff --git a/src/com/palmergames/bukkit/towny/db/TownyDatabaseHandler.java b/src/com/palmergames/bukkit/towny/db/TownyDatabaseHandler.java index 3cdcfac72a..bbb9436477 100644 --- a/src/com/palmergames/bukkit/towny/db/TownyDatabaseHandler.java +++ b/src/com/palmergames/bukkit/towny/db/TownyDatabaseHandler.java @@ -1,7 +1,6 @@ package com.palmergames.bukkit.towny.db; import com.palmergames.bukkit.towny.Towny; -import com.palmergames.bukkit.towny.TownyAPI; import com.palmergames.bukkit.towny.TownyEconomyHandler; import com.palmergames.bukkit.towny.TownyMessaging; import com.palmergames.bukkit.towny.TownySettings; @@ -1554,24 +1553,6 @@ public void deleteFile(String fileName) { queryQueue.add(new DeleteFileTask(file, true)); } - /** - * @param town - Town to validate outpost spawns of - * @author - Articdive | Author note is only for people to know who wrote it and - * who to ask, not to creditize - */ - public static void validateTownOutposts(Town town) { - List validoutpostspawns = new ArrayList<>(); - if (town != null && town.hasOutpostSpawn()) { - for (Location outpostSpawn : town.getAllOutpostSpawns()) { - TownBlock outpostSpawnTB = TownyAPI.getInstance().getTownBlock(outpostSpawn); - if (outpostSpawnTB != null) { - validoutpostspawns.add(outpostSpawn); - } - } - town.setOutpostSpawns(validoutpostspawns); - } - } - /** * Merges the succumbingNation into the prevailingNation. * diff --git a/src/com/palmergames/bukkit/towny/exceptions/initialization/TownyInitException.java b/src/com/palmergames/bukkit/towny/exceptions/initialization/TownyInitException.java new file mode 100644 index 0000000000..5767bdad2d --- /dev/null +++ b/src/com/palmergames/bukkit/towny/exceptions/initialization/TownyInitException.java @@ -0,0 +1,42 @@ +package com.palmergames.bukkit.towny.exceptions.initialization; + +import org.jetbrains.annotations.NotNull; + +/** + * Exception thrown when Towny fails to initialize. + */ +public class TownyInitException extends RuntimeException { + private static final long serialVersionUID = -1943705202251722549L; + private TownyError error = TownyError.OTHER; + + public TownyInitException(@NotNull String message) { + super(message); + } + + public TownyInitException(@NotNull String message, @NotNull Throwable t) { + super(message, t); + } + + public TownyInitException(@NotNull String message, @NotNull TownyError error) { + super(message); + this.error = error; + } + + public TownyInitException(@NotNull String message, @NotNull TownyError error, @NotNull Throwable t) { + super(message, t); + this.error = error; + } + + public TownyError getError() { + return error; + } + + public enum TownyError { + OTHER, + MAIN_CONFIG, + LOCALIZATION, + DATABASE, + DATABASE_CONFIG, + PERMISSIONS, + } +} diff --git a/src/com/palmergames/bukkit/towny/permissions/TownyPerms.java b/src/com/palmergames/bukkit/towny/permissions/TownyPerms.java index 224a70c91c..e6a9c7e55d 100644 --- a/src/com/palmergames/bukkit/towny/permissions/TownyPerms.java +++ b/src/com/palmergames/bukkit/towny/permissions/TownyPerms.java @@ -4,7 +4,7 @@ import com.palmergames.bukkit.towny.Towny; import com.palmergames.bukkit.towny.TownyAPI; import com.palmergames.bukkit.towny.TownyUniverse; -import com.palmergames.bukkit.towny.exceptions.TownyException; +import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; import com.palmergames.bukkit.towny.object.Nation; import com.palmergames.bukkit.towny.object.Resident; import com.palmergames.bukkit.towny.object.Town; @@ -16,6 +16,7 @@ import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionAttachment; import org.bukkit.permissions.PermissionDefault; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -64,25 +65,24 @@ public static void initialize(Towny plugin) { * * @param filepath - Path to townyperms.yml * @param defaultRes - Default townyperms.yml within the jar. - * @throws TownyException - When permission file cannot be loaded. + * @throws TownyInitException - When permission file cannot be loaded. */ - public static void loadPerms(String filepath, String defaultRes) throws TownyException { + public static void loadPerms(@NotNull String filepath, @NotNull String defaultRes) { String fullPath = filepath + File.separator + defaultRes; File file = FileMgmt.unpackResourceFile(fullPath, defaultRes, defaultRes); - if (file != null) { - // read the (language).yml into memory - perms = new CommentedConfiguration(file); - if (!perms.load()) - throw new TownyException("Could not read Townyperms.yml"); - - groupPermsMap.clear(); - buildGroupPermsMap(); - buildComments(); - perms.save(); + // read the townyperms.yml into memory + perms = new CommentedConfiguration(file); + if (!perms.load()) { + throw new TownyInitException("Could not read townyperms.yml", TownyInitException.TownyError.PERMISSIONS); } - + + groupPermsMap.clear(); + buildGroupPermsMap(); + buildComments(); + perms.save(); + /* * Only do this once as we are really only interested in Towny perms. */