Add an UpgradeManager

This commit is contained in:
zreed 2014-07-22 20:01:26 -04:00 committed by TfT_02
parent 87bce65098
commit 48c7aa3e84
7 changed files with 299 additions and 174 deletions

View File

@ -22,6 +22,7 @@ import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.datatypes.MobHealthbarType; import com.gmail.nossr50.datatypes.MobHealthbarType;
import com.gmail.nossr50.datatypes.database.DatabaseType; import com.gmail.nossr50.datatypes.database.DatabaseType;
import com.gmail.nossr50.datatypes.database.PlayerStat; import com.gmail.nossr50.datatypes.database.PlayerStat;
import com.gmail.nossr50.datatypes.database.UpgradeType;
import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.AbilityType; import com.gmail.nossr50.datatypes.skills.AbilityType;
import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.datatypes.skills.SkillType;
@ -762,6 +763,14 @@ public final class FlatfileDatabaseManager implements DatabaseManager {
tryClose(out); 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; return;
} }

View File

@ -4,6 +4,7 @@ import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
@ -17,8 +18,8 @@ import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.config.Config; import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.datatypes.MobHealthbarType; import com.gmail.nossr50.datatypes.MobHealthbarType;
import com.gmail.nossr50.datatypes.database.DatabaseType; 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.database.PlayerStat;
import com.gmail.nossr50.datatypes.database.UpgradeType;
import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.player.PlayerProfile;
import com.gmail.nossr50.datatypes.skills.AbilityType; import com.gmail.nossr50.datatypes.skills.AbilityType;
import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.datatypes.skills.SkillType;
@ -484,11 +485,11 @@ public final class SQLDatabaseManager implements DatabaseManager {
/** /**
* Check connection status and re-establish if dead or stale. * Check connection status and re-establish if dead or stale.
* * <p/>
* If the very first immediate attempt fails, further attempts * If the very first immediate attempt fails, further attempts
* will be made in progressively larger intervals up to MAX_WAIT * will be made in progressively larger intervals up to MAX_WAIT
* intervals. * intervals.
* * <p/>
* This allows for MySQL to time out idle connections as needed by * This allows for MySQL to time out idle connections as needed by
* server operator, without affecting McMMO, while still providing * server operator, without affecting McMMO, while still providing
* protection against a database outage taking down Bukkit's tick * protection against a database outage taking down Bukkit's tick
@ -709,144 +710,174 @@ public final class SQLDatabaseManager implements DatabaseManager {
+ "PRIMARY KEY (`user_id`)) " + "PRIMARY KEY (`user_id`)) "
+ "DEFAULT CHARSET=latin1;"); + "DEFAULT CHARSET=latin1;");
for (DatabaseUpdateType updateType : DatabaseUpdateType.values()) { for (UpgradeType updateType : UpgradeType.values()) {
checkDatabaseStructure(updateType); 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) { private void checkDatabaseStructure(UpgradeType upgrade) {
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;
}
ResultSet resultSet;
PreparedStatement statement = null;
try {
if (!checkConnected()) { if (!checkConnected()) {
return; return;
} }
statement = connection.prepareStatement(sql); if (!mcMMO.getUpgradeManager().shouldUpgrade(upgrade)) {
resultSet = statement.executeQuery(); mcMMO.p.debug("Skipping " + upgrade.name() + " upgrade (unneeded)");
return;
}
while (resultSet.next()) { Statement statement = null;
// No reason to do anything here... we're just trying to catch exceptions ResultSet resultSet = null;
try {
statement = connection.createStatement();
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 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 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 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) { catch (SQLException ex) {
switch (update) { // Ignore
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' ;");
break; break;
case FISHING: case DROP_SPOUT:
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Fishing..."); try {
write("ALTER TABLE `"+tablePrefix + "skills` ADD `fishing` int(10) NOT NULL DEFAULT '0' ;"); resultSet = statement.executeQuery("SELECT * FROM `" + tablePrefix + "huds` LIMIT 1");
write("ALTER TABLE `"+tablePrefix + "experience` ADD `fishing` int(10) NOT NULL DEFAULT '0' ;");
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; break;
case MOB_HEALTHBARS: case ADD_ALCHEMY:
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for mob healthbars..."); try {
write("ALTER TABLE `" + tablePrefix + "huds` ADD `mobhealthbar` varchar(50) NOT NULL DEFAULT '" + Config.getInstance().getMobHealthbarDefault() + "' ;"); statement.executeQuery("SELECT `alchemy` FROM `" + tablePrefix + "skills` LIMIT 1");
break; }
catch (SQLException ex) {
case ALCHEMY:
mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Alchemy..."); mcMMO.p.getLogger().info("Updating mcMMO MySQL tables for Alchemy...");
write("ALTER TABLE `"+tablePrefix + "skills` ADD `alchemy` int(10) NOT NULL DEFAULT '0' ;"); statement.executeQuery("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' ;"); statement.executeQuery("ALTER TABLE `" + tablePrefix + "experience` ADD `alchemy` int(10) NOT NULL DEFAULT '0'");
}
break; break;
default: default:
break; break;
} }
mcMMO.getUpgradeManager().setUpgradeCompleted(upgrade);
}
catch (SQLException ex) {
} }
finally { finally {
if (resultSet != null) {
try {
resultSet.close();
}
catch (SQLException e) {
// Ignore
}
}
if (statement != null) { if (statement != null) {
try { try {
statement.close(); statement.close();
} }
catch (SQLException e) { catch (SQLException e) {
// Ignore the error, we're leaving // Ignore
} }
} }
} }
@ -856,6 +887,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
* Attempt to write the SQL query. * Attempt to write the SQL query.
* *
* @param sql Query to write. * @param sql Query to write.
*
* @return true if the query was successfully written, false otherwise. * @return true if the query was successfully written, false otherwise.
*/ */
private boolean write(String sql) { private boolean write(String sql) {
@ -891,6 +923,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
* Returns the number of rows affected by either a DELETE or UPDATE query * Returns the number of rows affected by either a DELETE or UPDATE query
* *
* @param sql SQL query to execute * @param sql SQL query to execute
*
* @return the number of rows affected * @return the number of rows affected
*/ */
private int update(String sql) { private int update(String sql) {
@ -925,6 +958,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
* Read SQL query. * Read SQL query.
* *
* @param sql SQL query to read * @param sql SQL query to read
*
* @return the rows in this SQL query * @return the rows in this SQL query
*/ */
private HashMap<Integer, ArrayList<String>> read(String sql) { private HashMap<Integer, ArrayList<String>> read(String sql) {
@ -970,6 +1004,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
* Get the Integer. Only return first row / first field. * Get the Integer. Only return first row / first field.
* *
* @param statement SQL query to execute * @param statement SQL query to execute
*
* @return the value in the first row / first field * @return the value in the first row / first field
*/ */
private int readInt(PreparedStatement statement) { private int readInt(PreparedStatement statement) {
@ -1113,6 +1148,7 @@ public final class SQLDatabaseManager implements DatabaseManager {
* Retrieve the database id for a player * Retrieve the database id for a player
* *
* @param playerName The name of the user to retrieve the id for * @param playerName The name of the user to retrieve the id for
*
* @return the requested id or -1 if not found * @return the requested id or -1 if not found
*/ */
private int readId(String playerName) { private int readId(String playerName) {

View File

@ -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
;
}

View File

@ -0,0 +1,11 @@
package com.gmail.nossr50.datatypes.database;
public enum UpgradeType {
ADD_FISHING,
ADD_BLAST_MINING_COOLDOWN,
ADD_SQL_INDEXES,
ADD_MOB_HEALTHBARS,
DROP_SQL_PARTY_NAMES,
DROP_SPOUT,
ADD_ALCHEMY;
}

View File

@ -60,6 +60,7 @@ import com.gmail.nossr50.util.commands.CommandRegistrationManager;
import com.gmail.nossr50.util.experience.FormulaManager; import com.gmail.nossr50.util.experience.FormulaManager;
import com.gmail.nossr50.util.player.UserManager; import com.gmail.nossr50.util.player.UserManager;
import com.gmail.nossr50.util.scoreboards.ScoreboardManager; import com.gmail.nossr50.util.scoreboards.ScoreboardManager;
import com.gmail.nossr50.util.upgrade.UpgradeManager;
import net.shatteredlands.shatt.backup.ZipLibrary; import net.shatteredlands.shatt.backup.ZipLibrary;
@ -72,6 +73,7 @@ public class mcMMO extends JavaPlugin {
private static DatabaseManager databaseManager; private static DatabaseManager databaseManager;
private static FormulaManager formulaManager; private static FormulaManager formulaManager;
private static HolidayManager holidayManager; private static HolidayManager holidayManager;
private static UpgradeManager upgradeManager;
/* File Paths */ /* File Paths */
private static String mainDirectory; private static String mainDirectory;
@ -129,6 +131,8 @@ public class mcMMO extends JavaPlugin {
PluginManager pluginManager = getServer().getPluginManager(); PluginManager pluginManager = getServer().getPluginManager();
healthBarPluginEnabled = pluginManager.getPlugin("HealthBar") != null; healthBarPluginEnabled = pluginManager.getPlugin("HealthBar") != null;
upgradeManager = new UpgradeManager();
setupFilePaths(); setupFilePaths();
modManager = new ModManager(); modManager = new ModManager();
@ -309,6 +313,10 @@ public class mcMMO extends JavaPlugin {
return modManager; return modManager;
} }
public static UpgradeManager getUpgradeManager() {
return upgradeManager;
}
@Deprecated @Deprecated
public static void setDatabaseManager(DatabaseManager databaseManager) { public static void setDatabaseManager(DatabaseManager databaseManager) {
mcMMO.databaseManager = databaseManager; mcMMO.databaseManager = databaseManager;

View File

@ -0,0 +1,65 @@
package com.gmail.nossr50.util.upgrade;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import com.gmail.nossr50.config.ConfigLoader;
import com.gmail.nossr50.datatypes.database.UpgradeType;
public class UpgradeManager extends ConfigLoader {
private final Set<UpgradeType> 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 for type " + type.toString() + "...");
config.set("Upgrades_Finished." + type.toString(), true);
try {
config.save(getFile());
}
catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void loadKeys() {
for (UpgradeType type : UpgradeType.values()) {
if (config.getBoolean("Upgrades_Finished." + type.toString())) {
setNeededUpgrades.remove(type);
}
}
plugin.debug("Needed upgrades: " + Arrays.toString(setNeededUpgrades.toArray(new UpgradeType[setNeededUpgrades.size()])));
}
}

View File

@ -0,0 +1,9 @@
# WARNING: DO NOT MODIFY THIS CONFIG
Upgrades_Finished:
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