diff --git a/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java index bca300508..dcc440f9a 100644 --- a/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java @@ -27,6 +27,7 @@ import com.gmail.nossr50.datatypes.skills.AbilityType; import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.util.Misc; import com.gmail.nossr50.util.StringUtils; +import com.gmail.nossr50.util.upgrade.UpgradeType; import org.apache.commons.lang.ArrayUtils; @@ -762,6 +763,13 @@ public final class FlatfileDatabaseManager implements DatabaseManager { tryClose(out); } } + mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_FISHING); + mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_BLAST_MINING_COOLDOWN); + mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_SQL_INDEXES); + mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_MOB_HEALTHBARS); + mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.DROP_SQL_PARTY_NAMES); + mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.DROP_SPOUT); + mcMMO.getUpgradeManager().setUpgradeCompleted(UpgradeType.ADD_ALCHEMY); return; } diff --git a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java index 663577583..8069af287 100644 --- a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java @@ -4,6 +4,7 @@ import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; @@ -17,7 +18,6 @@ import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; import com.gmail.nossr50.datatypes.MobHealthbarType; import com.gmail.nossr50.datatypes.database.DatabaseType; -import com.gmail.nossr50.datatypes.database.DatabaseUpdateType; import com.gmail.nossr50.datatypes.database.PlayerStat; import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.skills.AbilityType; @@ -25,6 +25,7 @@ import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.runnables.database.SQLDatabaseKeepaliveTask; import com.gmail.nossr50.runnables.database.SQLReconnectTask; import com.gmail.nossr50.util.Misc; +import com.gmail.nossr50.util.upgrade.UpgradeType; public final class SQLDatabaseManager implements DatabaseManager { private String connectionString; @@ -709,144 +710,174 @@ public final class SQLDatabaseManager implements DatabaseManager { + "PRIMARY KEY (`user_id`)) " + "DEFAULT CHARSET=latin1;"); - for (DatabaseUpdateType updateType : DatabaseUpdateType.values()) { + for (UpgradeType updateType : UpgradeType.values()) { checkDatabaseStructure(updateType); } + + mcMMO.p.getLogger().info("Killing orphans"); + write("DELETE FROM `" + tablePrefix + "experience` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "experience`.`user_id` = `u`.`id`)"); + write("DELETE FROM `" + tablePrefix + "huds` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "huds`.`user_id` = `u`.`id`)"); + write("DELETE FROM `" + tablePrefix + "cooldowns` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "cooldowns`.`user_id` = `u`.`id`)"); + write("DELETE FROM `" + tablePrefix + "skills` WHERE NOT EXISTS (SELECT * FROM `" + tablePrefix + "users` `u` WHERE `" + tablePrefix + "skills`.`user_id` = `u`.`id`)"); } /** - * Check database structure for missing values. + * Check database structure for necessary upgrades. * - * @param update Type of data to check updates for + * @param upgrade Upgrade to attempt to apply */ - private void checkDatabaseStructure(DatabaseUpdateType update) { - String sql = ""; - - switch (update) { - case BLAST_MINING: - sql = "SELECT * FROM `" + tablePrefix + "cooldowns` ORDER BY `" + tablePrefix + "cooldowns`.`blast_mining` ASC LIMIT 0 , 30"; - break; - - case FISHING: - sql = "SELECT * FROM `" + tablePrefix + "experience` ORDER BY `" + tablePrefix + "experience`.`fishing` ASC LIMIT 0 , 30"; - break; - - case ALCHEMY: - sql = "SELECT * FROM `" + tablePrefix + "experience` ORDER BY `" + tablePrefix + "experience`.`alchemy` ASC LIMIT 0 , 30"; - break; - - case INDEX: - if (read("SHOW INDEX FROM " + tablePrefix + "skills").size() != 13 && checkConnected()) { - mcMMO.p.getLogger().info("Indexing tables, this may take a while on larger databases"); - write("ALTER TABLE `" + tablePrefix + "skills` ADD INDEX `idx_taming` (`taming`) USING BTREE, " - + "ADD INDEX `idx_mining` (`mining`) USING BTREE, " - + "ADD INDEX `idx_woodcutting` (`woodcutting`) USING BTREE, " - + "ADD INDEX `idx_repair` (`repair`) USING BTREE, " - + "ADD INDEX `idx_unarmed` (`unarmed`) USING BTREE, " - + "ADD INDEX `idx_herbalism` (`herbalism`) USING BTREE, " - + "ADD INDEX `idx_excavation` (`excavation`) USING BTREE, " - + "ADD INDEX `idx_archery` (`archery`) USING BTREE, " - + "ADD INDEX `idx_swords` (`swords`) USING BTREE, " - + "ADD INDEX `idx_axes` (`axes`) USING BTREE, " - + "ADD INDEX `idx_acrobatics` (`acrobatics`) USING BTREE, " - + "ADD INDEX `idx_fishing` (`fishing`) USING BTREE;"); - } - return; - - case MOB_HEALTHBARS: - sql = "SELECT * FROM `" + tablePrefix + "huds` ORDER BY `" + tablePrefix + "huds`.`mobhealthbar` ASC LIMIT 0 , 30"; - break; - - case PARTY_NAMES: - write("ALTER TABLE `" + tablePrefix + "users` DROP COLUMN `party` ;"); - return; - - case DROPPED_SPOUT: - write("ALTER TABLE `" + tablePrefix + "huds` DROP COLUMN `hudtype` ;"); - return; - - case KILL_ORPHANS: - mcMMO.p.getLogger().info("Killing orphans"); - write( - "DELETE FROM " + tablePrefix + "experience " + - "WHERE NOT EXISTS (SELECT * FROM " + - tablePrefix + "users u WHERE " + - tablePrefix + "experience.user_id = u.id);" - ); - write( - "DELETE FROM " + tablePrefix + "huds " + - "WHERE NOT EXISTS (SELECT * FROM " + - tablePrefix + "users u WHERE " + - tablePrefix + "huds.user_id = u.id);" - ); - write( - "DELETE FROM " + tablePrefix + "cooldowns " + - "WHERE NOT EXISTS (SELECT * FROM " + - tablePrefix + "users u WHERE " + - tablePrefix + "cooldowns.user_id = u.id);" - ); - write( - "DELETE FROM " + tablePrefix + "skills " + - "WHERE NOT EXISTS (SELECT * FROM " + - tablePrefix + "users u WHERE " + - tablePrefix + "skills.user_id = u.id);" - ); - return; - - default: - break; + private void checkDatabaseStructure(UpgradeType upgrade) { + if (!checkConnected()) { + return; } - ResultSet resultSet; - PreparedStatement statement = null; + if(!mcMMO.getUpgradeManager().shouldUpgrade(upgrade)) { + mcMMO.p.debug("Skipping " + upgrade.name() + " upgrade (unneeded)"); + return; + } + + Statement statement = null; + ResultSet resultSet = null; try { - if (!checkConnected()) { - return; - } + statement = connection.createStatement(); - statement = connection.prepareStatement(sql); - resultSet = statement.executeQuery(); - - while (resultSet.next()) { - // No reason to do anything here... we're just trying to catch exceptions - } - } - catch (SQLException ex) { - switch (update) { - case BLAST_MINING: - mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Blast Mining..."); - write("ALTER TABLE `"+tablePrefix + "cooldowns` ADD `blast_mining` int(32) NOT NULL DEFAULT '0' ;"); + switch (upgrade) { + case ADD_FISHING: + try { + statement.executeQuery("SELECT `fishing` FROM `" + tablePrefix + "skills` LIMIT 1"); + } + catch (SQLException ex) { + mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Fishing..."); + statement.executeQuery("ALTER TABLE `" + tablePrefix + "skills` ADD `fishing` int(10) NOT NULL DEFAULT '0'"); + statement.executeQuery("ALTER TABLE `" + tablePrefix + "experience` ADD `fishing` int(10) NOT NULL DEFAULT '0'"); + } break; - case FISHING: - mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Fishing..."); - write("ALTER TABLE `"+tablePrefix + "skills` ADD `fishing` int(10) NOT NULL DEFAULT '0' ;"); - write("ALTER TABLE `"+tablePrefix + "experience` ADD `fishing` int(10) NOT NULL DEFAULT '0' ;"); + case ADD_BLAST_MINING_COOLDOWN: + try { + statement.executeQuery("SELECT `blast_mining` FROM `" + tablePrefix + "cooldowns` LIMIT 1"); + } + catch (SQLException ex) { + mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Blast Mining..."); + statement.executeQuery("ALTER TABLE `"+tablePrefix + "cooldowns` ADD `blast_mining` int(32) NOT NULL DEFAULT '0'"); + } break; - case MOB_HEALTHBARS: - mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for mob healthbars..."); - write("ALTER TABLE `" + tablePrefix + "huds` ADD `mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "' ;"); + case ADD_SQL_INDEXES: + resultSet = statement.executeQuery("SHOW INDEX FROM `" + tablePrefix + "skills` WHERE `Key_name` LIKE 'idx\\_%'"); + resultSet.last(); + + if (resultSet.getRow() != SkillType.NON_CHILD_SKILLS.size()) { + mcMMO.p.getLogger().info("Indexing tables, this may take a while on larger databases"); + + for (SkillType skill : SkillType.NON_CHILD_SKILLS) { + String skill_name = skill.name().toLowerCase(); + + try { + statement.executeUpdate("ALTER TABLE `" + tablePrefix + "skills` ADD INDEX `idx_" + skill_name + "` (`" + skill_name + "`) USING BTREE"); + } + catch (SQLException ex) { + // Ignore + } + } + } break; - case ALCHEMY: - mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Alchemy..."); - write("ALTER TABLE `"+tablePrefix + "skills` ADD `alchemy` int(10) NOT NULL DEFAULT '0' ;"); - write("ALTER TABLE `"+tablePrefix + "experience` ADD `alchemy` int(10) NOT NULL DEFAULT '0' ;"); + case ADD_MOB_HEALTHBARS: + try { + statement.executeQuery("SELECT `mobhealthbar` FROM `" + tablePrefix + "huds` LIMIT 1"); + } + catch (SQLException ex) { + mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for mob healthbars..."); + statement.executeQuery("ALTER TABLE `" + tablePrefix + "huds` ADD `mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "'"); + } break; - + + case DROP_SQL_PARTY_NAMES: + try { + resultSet = statement.executeQuery("SELECT * FROM `" + tablePrefix + "users` LIMIT 1"); + + ResultSetMetaData rsmeta = resultSet.getMetaData(); + boolean column_exists = false; + + for (int i = 1; i <= rsmeta.getColumnCount(); i++) { + if(rsmeta.getColumnName(i).equalsIgnoreCase("party")) { + column_exists = true; + break; + } + } + + if (column_exists) { + mcMMO.p.getLogger().info("Removing party name from users table..."); + statement.executeQuery("ALTER TABLE `" + tablePrefix + "users` DROP COLUMN `party`"); + } + } + catch (SQLException ex) { + // Ignore + } + break; + + case DROP_SPOUT: + try { + resultSet = statement.executeQuery("SELECT * FROM `" + tablePrefix + "huds` LIMIT 1"); + + ResultSetMetaData rsmeta = resultSet.getMetaData(); + boolean column_exists = false; + + for (int i = 1; i <= rsmeta.getColumnCount(); i++) { + if(rsmeta.getColumnName(i).equalsIgnoreCase("hudtype")) { + column_exists = true; + break; + } + } + + if (column_exists) { + mcMMO.p.getLogger().info("Removing Spout HUD type from huds table..."); + statement.executeQuery("ALTER TABLE `" + tablePrefix + "huds` DROP COLUMN `hudtype`"); + } + } + catch (SQLException ex) { + // Ignore + } + break; + + case ADD_ALCHEMY: + try { + statement.executeQuery("SELECT `alchemy` FROM `" + tablePrefix + "skills` LIMIT 1"); + } + catch (SQLException ex) { + mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Alchemy..."); + statement.executeQuery("ALTER TABLE `" + tablePrefix + "skills` ADD `alchemy` int(10) NOT NULL DEFAULT '0'"); + statement.executeQuery("ALTER TABLE `" + tablePrefix + "experience` ADD `alchemy` int(10) NOT NULL DEFAULT '0'"); + } + break; + default: break; + } + + mcMMO.getUpgradeManager().setUpgradeCompleted(upgrade); + } + catch (SQLException ex) { + } finally { + if (resultSet != null) { + try { + resultSet.close(); + } + catch (SQLException e) { + // Ignore + } + } if (statement != null) { try { statement.close(); } catch (SQLException e) { - // Ignore the error, we're leaving + // Ignore } } } diff --git a/src/main/java/com/gmail/nossr50/datatypes/database/DatabaseUpdateType.java b/src/main/java/com/gmail/nossr50/datatypes/database/DatabaseUpdateType.java deleted file mode 100644 index 2223c1969..000000000 --- a/src/main/java/com/gmail/nossr50/datatypes/database/DatabaseUpdateType.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.gmail.nossr50.datatypes.database; - -public enum DatabaseUpdateType { - FISHING, - BLAST_MINING, - INDEX, - MOB_HEALTHBARS, - PARTY_NAMES, - KILL_ORPHANS, - DROPPED_SPOUT, - ALCHEMY - ; -} diff --git a/src/main/java/com/gmail/nossr50/mcMMO.java b/src/main/java/com/gmail/nossr50/mcMMO.java index 5f0e7a071..d4ea83366 100644 --- a/src/main/java/com/gmail/nossr50/mcMMO.java +++ b/src/main/java/com/gmail/nossr50/mcMMO.java @@ -60,6 +60,7 @@ import com.gmail.nossr50.util.commands.CommandRegistrationManager; import com.gmail.nossr50.util.experience.FormulaManager; import com.gmail.nossr50.util.player.UserManager; import com.gmail.nossr50.util.scoreboards.ScoreboardManager; +import com.gmail.nossr50.util.upgrade.UpgradeManager; import net.shatteredlands.shatt.backup.ZipLibrary; @@ -72,6 +73,7 @@ public class mcMMO extends JavaPlugin { private static DatabaseManager databaseManager; private static FormulaManager formulaManager; private static HolidayManager holidayManager; + private static UpgradeManager upgradeManager; /* File Paths */ private static String mainDirectory; @@ -129,6 +131,8 @@ public class mcMMO extends JavaPlugin { PluginManager pluginManager = getServer().getPluginManager(); healthBarPluginEnabled = pluginManager.getPlugin("HealthBar") != null; + upgradeManager = new UpgradeManager(); + setupFilePaths(); modManager = new ModManager(); @@ -309,6 +313,10 @@ public class mcMMO extends JavaPlugin { return modManager; } + public static UpgradeManager getUpgradeManager() { + return upgradeManager; + } + @Deprecated public static void setDatabaseManager(DatabaseManager databaseManager) { mcMMO.databaseManager = databaseManager; diff --git a/src/main/java/com/gmail/nossr50/util/upgrade/UpgradeManager.java b/src/main/java/com/gmail/nossr50/util/upgrade/UpgradeManager.java new file mode 100644 index 000000000..958d06d9c --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/upgrade/UpgradeManager.java @@ -0,0 +1,64 @@ +package com.gmail.nossr50.util.upgrade; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Set; + +import com.gmail.nossr50.config.ConfigLoader; + +public class UpgradeManager extends ConfigLoader { + private final Set setNeededUpgrades; + + public UpgradeManager() { + super("upgrades.yml"); + + setNeededUpgrades = EnumSet.allOf(UpgradeType.class); + + loadKeys(); + } + + /** + * Check if the given {@link UpgradeType} is necessary. + * + * @param type Upgrade type to check + * @return true if plugin data needs to have the given upgrade + */ + public boolean shouldUpgrade(final UpgradeType type) { + return setNeededUpgrades.contains(type); + } + + /** + * Set the given {@link UpgradeType} as completed. Does nothing if + * the upgrade was applied previously. + * + * @param type Upgrade type to set as complete + */ + public void setUpgradeCompleted(final UpgradeType type) { + if (!setNeededUpgrades.remove(type)) { + return; + } + + plugin.debug("Saving upgrade status..."); + + config.set(type.name().toLowerCase(), true); + + try { + config.save(getFile()); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + protected void loadKeys() { + for (UpgradeType type : UpgradeType.values()) { + if (config.getBoolean(type.name().toLowerCase())) { + setNeededUpgrades.remove(type); + } + } + + plugin.debug("Needed upgrades: " + Arrays.toString(setNeededUpgrades.toArray(new UpgradeType[setNeededUpgrades.size()]))); + } + +} diff --git a/src/main/java/com/gmail/nossr50/util/upgrade/UpgradeType.java b/src/main/java/com/gmail/nossr50/util/upgrade/UpgradeType.java new file mode 100644 index 000000000..e9cd20854 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/upgrade/UpgradeType.java @@ -0,0 +1,12 @@ +package com.gmail.nossr50.util.upgrade; + +public enum UpgradeType { + ADD_FISHING, + ADD_BLAST_MINING_COOLDOWN, + ADD_SQL_INDEXES, + ADD_MOB_HEALTHBARS, + DROP_SQL_PARTY_NAMES, + DROP_SPOUT, + ADD_ALCHEMY + ; +} diff --git a/src/main/resources/upgrades.yml b/src/main/resources/upgrades.yml new file mode 100644 index 000000000..bc1e27047 --- /dev/null +++ b/src/main/resources/upgrades.yml @@ -0,0 +1,8 @@ +# WARNING: DO NOT MODIFY THIS CONFIG +add_fishing: false +add_blast_mining_cooldown: false +add_sql_indexes: false +add_mob_healthbars: false +drop_sql_party_names: false +drop_spout: false +add_alchemy: false