2
0
Эх сурвалжийг харах

Abstract our database operations into a single class.

This allows the logic between SQL and Flatfile to remain more hidden in
most cases and makes the code easier to read.
GJ 12 жил өмнө
parent
commit
eea5784527
18 өөрчлөгдсөн 1189 нэмэгдсэн , 1384 устгасан
  1. 4 14
      src/main/java/com/gmail/nossr50/commands/database/McpurgeCommand.java
  2. 4 16
      src/main/java/com/gmail/nossr50/commands/database/McremoveCommand.java
  3. 5 55
      src/main/java/com/gmail/nossr50/commands/player/McrankCommand.java
  4. 10 18
      src/main/java/com/gmail/nossr50/commands/player/MctopCommand.java
  5. 33 613
      src/main/java/com/gmail/nossr50/database/DatabaseManager.java
  6. 50 85
      src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java
  7. 628 0
      src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java
  8. 411 500
      src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java
  9. 3 15
      src/main/java/com/gmail/nossr50/mcMMO.java
  10. 4 2
      src/main/java/com/gmail/nossr50/runnables/commands/McrankCommandAsyncTask.java
  11. 10 14
      src/main/java/com/gmail/nossr50/runnables/commands/McrankCommandDisplayTask.java
  12. 3 3
      src/main/java/com/gmail/nossr50/runnables/commands/MctopCommandAsyncTask.java
  13. 12 12
      src/main/java/com/gmail/nossr50/runnables/database/SQLConversionTask.java
  14. 2 2
      src/main/java/com/gmail/nossr50/runnables/database/SQLReconnectTask.java
  15. 4 14
      src/main/java/com/gmail/nossr50/runnables/database/UserPurgeTask.java
  16. 5 2
      src/main/java/com/gmail/nossr50/runnables/player/PlayerProfileSaveTask.java
  17. 0 16
      src/main/java/com/gmail/nossr50/util/player/UserManager.java
  18. 1 3
      src/main/java/net/shatteredlands/shatt/backup/ZipLibrary.java

+ 4 - 14
src/main/java/com/gmail/nossr50/commands/database/McpurgeCommand.java

@@ -6,9 +6,8 @@ import org.bukkit.command.Command;
 import org.bukkit.command.CommandSender;
 import org.bukkit.command.CommandSender;
 import org.bukkit.command.TabExecutor;
 import org.bukkit.command.TabExecutor;
 
 
+import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.database.DatabaseManager;
-import com.gmail.nossr50.database.LeaderboardManager;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.Permissions;
 
 
@@ -24,19 +23,10 @@ public class McpurgeCommand implements TabExecutor {
 
 
         switch (args.length) {
         switch (args.length) {
             case 0:
             case 0:
-                if (Config.getInstance().getUseMySQL()) {
-                    DatabaseManager.purgePowerlessSQL();
+                mcMMO.databaseManager.purgePowerlessUsers();
 
 
-                    if (Config.getInstance().getOldUsersCutoff() != -1) {
-                        DatabaseManager.purgeOldSQL();
-                    }
-                }
-                else {
-                    LeaderboardManager.purgePowerlessFlatfile();
-
-                    if (Config.getInstance().getOldUsersCutoff() != -1) {
-                        LeaderboardManager.purgeOldFlatfile();
-                    }
+                if (Config.getInstance().getOldUsersCutoff() != -1) {
+                    mcMMO.databaseManager.purgeOldUsers();
                 }
                 }
 
 
                 sender.sendMessage(LocaleLoader.getString("Commands.mcpurge.Success"));
                 sender.sendMessage(LocaleLoader.getString("Commands.mcpurge.Success"));

+ 4 - 16
src/main/java/com/gmail/nossr50/commands/database/McremoveCommand.java

@@ -9,12 +9,9 @@ import org.bukkit.command.CommandSender;
 import org.bukkit.command.TabExecutor;
 import org.bukkit.command.TabExecutor;
 import org.bukkit.util.StringUtil;
 import org.bukkit.util.StringUtil;
 
 
-import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.database.DatabaseManager;
-import com.gmail.nossr50.database.LeaderboardManager;
+import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.datatypes.player.PlayerProfile;
 import com.gmail.nossr50.datatypes.player.PlayerProfile;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.locale.LocaleLoader;
-import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.commands.CommandUtils;
 import com.gmail.nossr50.util.commands.CommandUtils;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.player.UserManager;
@@ -35,20 +32,11 @@ public class McremoveCommand implements TabExecutor {
                     return true;
                     return true;
                 }
                 }
 
 
-                /* MySQL */
-                if (Config.getInstance().getUseMySQL()) {
-                    String tablePrefix = Config.getInstance().getMySQLTablePrefix();
-
-                    if (DatabaseManager.update("DELETE FROM " + tablePrefix + "users WHERE " + tablePrefix + "users.user = '" + args[0] + "'") != 0) {
-                        Misc.profileCleanup(args[0]);
-                        sender.sendMessage(LocaleLoader.getString("Commands.mcremove.Success", args[0]));
-                    }
+                if (mcMMO.databaseManager.removeUser(args[0])) {
+                    sender.sendMessage(LocaleLoader.getString("Commands.mcremove.Success", args[0]));
                 }
                 }
                 else {
                 else {
-                    if (LeaderboardManager.removeFlatFileUser(args[0])) {
-                        Misc.profileCleanup(args[0]);
-                        sender.sendMessage(LocaleLoader.getString("Commands.mcremove.Success", args[0]));
-                    }
+                    sender.sendMessage(args[0] + " could not be removed from the database."); // Pretty sure this should NEVER happen.
                 }
                 }
 
 
                 return true;
                 return true;

+ 5 - 55
src/main/java/com/gmail/nossr50/commands/player/McrankCommand.java

@@ -10,18 +10,12 @@ import org.bukkit.command.TabExecutor;
 import org.bukkit.util.StringUtil;
 import org.bukkit.util.StringUtil;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
-import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.database.LeaderboardManager;
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
 import com.gmail.nossr50.datatypes.player.PlayerProfile;
 import com.gmail.nossr50.datatypes.player.PlayerProfile;
-import com.gmail.nossr50.datatypes.skills.SkillType;
-import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.runnables.commands.McrankCommandAsyncTask;
 import com.gmail.nossr50.runnables.commands.McrankCommandAsyncTask;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.commands.CommandUtils;
 import com.gmail.nossr50.util.commands.CommandUtils;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.player.UserManager;
-import com.gmail.nossr50.util.skills.SkillUtils;
-
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList;
 
 
 public class McrankCommand implements TabExecutor {
 public class McrankCommand implements TabExecutor {
@@ -38,13 +32,7 @@ public class McrankCommand implements TabExecutor {
                     return true;
                     return true;
                 }
                 }
 
 
-                if (Config.getInstance().getUseMySQL()) {
-                    sqlDisplay(sender, sender.getName());
-                }
-                else {
-                    flatfileDisplay(sender, sender.getName());
-                }
-
+                display(sender, sender.getName());
                 return true;
                 return true;
 
 
             case 1:
             case 1:
@@ -62,18 +50,12 @@ public class McrankCommand implements TabExecutor {
                     if (CommandUtils.tooFar(sender, mcMMOPlayer.getPlayer(), Permissions.mcrankFar(sender))) {
                     if (CommandUtils.tooFar(sender, mcMMOPlayer.getPlayer(), Permissions.mcrankFar(sender))) {
                         return true;
                         return true;
                     }
                     }
-
-                } else if (CommandUtils.inspectOffline(sender, new PlayerProfile(playerName, false), Permissions.mcrankOffline(sender))) {
-                    return true;
-                }
-
-                if (Config.getInstance().getUseMySQL()) {
-                    sqlDisplay(sender, playerName);
                 }
                 }
-                else {
-                    flatfileDisplay(sender, playerName);
+                else if (CommandUtils.inspectOffline(sender, new PlayerProfile(playerName, false), Permissions.mcrankOffline(sender))) {
+                    return true;
                 }
                 }
 
 
+                display(sender, playerName);
                 return true;
                 return true;
 
 
             default:
             default:
@@ -92,39 +74,7 @@ public class McrankCommand implements TabExecutor {
         }
         }
     }
     }
 
 
-    private void flatfileDisplay(CommandSender sender, String playerName) {
-        LeaderboardManager.updateLeaderboards(); // Make sure the information is up to date
-
-        sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Heading"));
-        sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Player", playerName));
-
-        for (SkillType skillType : SkillType.values()) {
-            int[] rankInts = LeaderboardManager.getPlayerRank(playerName, skillType);
-
-            if (!Permissions.skillEnabled(sender, skillType) || skillType.isChildSkill()) {
-                continue;
-            }
-
-            if (rankInts[1] == 0) {
-                sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", SkillUtils.getSkillName(skillType), LocaleLoader.getString("Commands.mcrank.Unranked"))); // Don't bother showing ranking for players without skills
-            }
-            else {
-                sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", SkillUtils.getSkillName(skillType), rankInts[0]));
-            }
-        }
-
-        // Show the powerlevel ranking
-        int[] rankInts = LeaderboardManager.getPlayerRank(playerName);
-
-        if (rankInts[1] == 0) {
-            sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Overall", LocaleLoader.getString("Commands.mcrank.Unranked"))); // Don't bother showing ranking for players without skills
-        }
-        else {
-            sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Overall", rankInts[0]));
-        }
-    }
-
-    private void sqlDisplay(CommandSender sender, String playerName) {
+    private void display(CommandSender sender, String playerName) {
         new McrankCommandAsyncTask(playerName, sender).runTaskAsynchronously(mcMMO.p);
         new McrankCommandAsyncTask(playerName, sender).runTaskAsynchronously(mcMMO.p);
     }
     }
 }
 }

+ 10 - 18
src/main/java/com/gmail/nossr50/commands/player/MctopCommand.java

@@ -11,7 +11,7 @@ import org.bukkit.util.StringUtil;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.database.LeaderboardManager;
+import com.gmail.nossr50.database.FlatfileDatabaseManager;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.runnables.commands.MctopCommandAsyncTask;
 import com.gmail.nossr50.runnables.commands.MctopCommandAsyncTask;
@@ -26,17 +26,14 @@ public class MctopCommand implements TabExecutor {
 
 
     @Override
     @Override
     public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
     public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
-        boolean useMySQL = Config.getInstance().getUseMySQL();
-
-
         switch (args.length) {
         switch (args.length) {
             case 0:
             case 0:
-                display(1, "ALL", sender, useMySQL, command);
+                display(1, "ALL", sender, command);
                 return true;
                 return true;
 
 
             case 1:
             case 1:
                 if (StringUtils.isInt(args[0])) {
                 if (StringUtils.isInt(args[0])) {
-                    display(Math.abs(Integer.parseInt(args[0])), "ALL", sender, useMySQL, command);
+                    display(Math.abs(Integer.parseInt(args[0])), "ALL", sender, command);
                     return true;
                     return true;
                 }
                 }
 
 
@@ -44,7 +41,7 @@ public class MctopCommand implements TabExecutor {
                     return true;
                     return true;
                 }
                 }
 
 
-                display(1, skill.toString(), sender, useMySQL, command);
+                display(1, skill.toString(), sender, command);
                 return true;
                 return true;
 
 
             case 2:
             case 2:
@@ -56,7 +53,7 @@ public class MctopCommand implements TabExecutor {
                     return true;
                     return true;
                 }
                 }
 
 
-                display(Math.abs(Integer.parseInt(args[1])), skill.toString(), sender, useMySQL, command);
+                display(Math.abs(Integer.parseInt(args[1])), skill.toString(), sender, command);
                 return true;
                 return true;
 
 
             default:
             default:
@@ -74,19 +71,14 @@ public class MctopCommand implements TabExecutor {
         }
         }
     }
     }
 
 
-    private void display(int page, String skill, CommandSender sender, boolean sql, Command command) {
+    private void display(int page, String skill, CommandSender sender, Command command) {
         if (!skill.equalsIgnoreCase("all") && !Permissions.mctop(sender, this.skill)) {
         if (!skill.equalsIgnoreCase("all") && !Permissions.mctop(sender, this.skill)) {
             sender.sendMessage(command.getPermissionMessage());
             sender.sendMessage(command.getPermissionMessage());
             return;
             return;
         }
         }
 
 
-        if (sql) {
-            if (skill.equalsIgnoreCase("all")) {
-                sqlDisplay(page, "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing", sender);
-            }
-            else {
-                sqlDisplay(page, skill, sender);
-            }
+        if (Config.getInstance().getUseMySQL()) {
+            sqlDisplay(page, skill, sender);
         }
         }
         else {
         else {
             flatfileDisplay(page, skill, sender);
             flatfileDisplay(page, skill, sender);
@@ -94,7 +86,7 @@ public class MctopCommand implements TabExecutor {
     }
     }
 
 
     private void flatfileDisplay(int page, String skill, CommandSender sender) {
     private void flatfileDisplay(int page, String skill, CommandSender sender) {
-        LeaderboardManager.updateLeaderboards(); // Make sure we have the latest information
+        FlatfileDatabaseManager.updateLeaderboards(); // Make sure we have the latest information
 
 
         if (skill.equalsIgnoreCase("all")) {
         if (skill.equalsIgnoreCase("all")) {
             sender.sendMessage(LocaleLoader.getString("Commands.PowerLevel.Leaderboard"));
             sender.sendMessage(LocaleLoader.getString("Commands.PowerLevel.Leaderboard"));
@@ -105,7 +97,7 @@ public class MctopCommand implements TabExecutor {
 
 
         int position = (page * 10) - 9;
         int position = (page * 10) - 9;
 
 
-        for (String playerStat : LeaderboardManager.retrieveInfo(skill, page)) {
+        for (String playerStat : FlatfileDatabaseManager.retrieveInfo(skill, page)) {
             if (playerStat == null) {
             if (playerStat == null) {
                 continue;
                 continue;
             }
             }

+ 33 - 613
src/main/java/com/gmail/nossr50/database/DatabaseManager.java

@@ -1,643 +1,63 @@
 package com.gmail.nossr50.database;
 package com.gmail.nossr50.database;
 
 
-import java.sql.Connection;
-import java.sql.DriverManager;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
+import java.io.File;
+import java.io.IOException;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
-import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.datatypes.database.DatabaseUpdateType;
-import com.gmail.nossr50.datatypes.skills.SkillType;
-import com.gmail.nossr50.runnables.database.SQLReconnectTask;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Misc;
 
 
-public final class DatabaseManager {
-    private static String connectionString;
+public class DatabaseManager {
+    private final mcMMO plugin;
+    private final boolean isUsingSQL;
+    private File usersFile;
 
 
-    private static String tablePrefix = Config.getInstance().getMySQLTablePrefix();
-    private static Connection connection = null;
+    public DatabaseManager(final mcMMO plugin, final boolean isUsingSQL) {
+        this.plugin = plugin;
+        this.isUsingSQL = isUsingSQL;
 
 
-    // Scale waiting time by this much per failed attempt
-    private static final double SCALING_FACTOR = 40;
-
-    // Minimum wait in nanoseconds (default 500ms)
-    private static final long MIN_WAIT = 500L * 1000000L;
-
-    // Maximum time to wait between reconnects (default 5 minutes)
-    private static final long MAX_WAIT = 5L * 60L * 1000L * 1000000L;
-
-    // How long to wait when checking if connection is valid (default 3 seconds)
-    private static final int VALID_TIMEOUT = 3;
-
-    // When next to try connecting to Database in nanoseconds
-    private static long nextReconnectTimestamp = 0L;
-
-    // How many connection attempts have failed
-    private static int reconnectAttempt = 0;
-
-    private static final long ONE_MONTH = 2630000000L;
-
-    private DatabaseManager() {}
-
-    /**
-     * Attempt to connect to the mySQL database.
-     */
-    public static void connect() {
-        Config configInstance = Config.getInstance();
-        connectionString = "jdbc:mysql://" + configInstance.getMySQLServerName() + ":" + configInstance.getMySQLServerPort() + "/" + configInstance.getMySQLDatabaseName();
-
-        try {
-            mcMMO.p.getLogger().info("Attempting connection to MySQL...");
-
-            // Force driver to load if not yet loaded
-            Class.forName("com.mysql.jdbc.Driver");
-            Properties connectionProperties = new Properties();
-            connectionProperties.put("user", configInstance.getMySQLUserName());
-            connectionProperties.put("password", configInstance.getMySQLUserPassword());
-            connectionProperties.put("autoReconnect", "false");
-            connectionProperties.put("maxReconnects", "0");
-            connection = DriverManager.getConnection(connectionString, connectionProperties);
-
-            mcMMO.p.getLogger().info("Connection to MySQL was a success!");
+        if (isUsingSQL) {
+            SQLDatabaseManager.checkConnected();
+            SQLDatabaseManager.createStructure();
         }
         }
-        catch (SQLException ex) {
-            connection = null;
-
-            if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
-                mcMMO.p.getLogger().info("Connection to MySQL failed!");
-            }
-        }
-        catch (ClassNotFoundException ex) {
-            connection = null;
-
-            if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
-                mcMMO.p.getLogger().info("MySQL database driver not found!");
-            }
+        else {
+            usersFile = new File(mcMMO.getUsersFilePath());
+            createFlatfileDatabase();
+            FlatfileDatabaseManager.updateLeaderboards();
         }
         }
     }
     }
 
 
-    /**
-     * Attempt to create the database structure.
-     */
-    public static void createStructure() {
-        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` ("
-                + "`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
-                + "`user` varchar(40) NOT NULL,"
-                + "`lastlogin` int(32) unsigned NOT NULL,"
-                + "PRIMARY KEY (`id`),"
-                + "UNIQUE KEY `user` (`user`)) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;");
-        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "huds` ("
-                + "`user_id` int(10) unsigned NOT NULL,"
-                + "`hudtype` varchar(50) NOT NULL DEFAULT 'STANDARD',"
-                + "`mobhealthbar` varchar(50) NOT NULL DEFAULT 'HEARTS',"
-                + "PRIMARY KEY (`user_id`),"
-                + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
-                + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
-        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "cooldowns` ("
-                + "`user_id` int(10) unsigned NOT NULL,"
-                + "`taming` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`mining` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`woodcutting` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`repair` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`unarmed` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`herbalism` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`excavation` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`archery` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`swords` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`axes` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`acrobatics` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "`blast_mining` int(32) unsigned NOT NULL DEFAULT '0',"
-                + "PRIMARY KEY (`user_id`),"
-                + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
-                + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
-        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "skills` ("
-                + "`user_id` int(10) unsigned NOT NULL,"
-                + "`taming` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`mining` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`woodcutting` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`repair` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`unarmed` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`herbalism` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`excavation` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`archery` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`swords` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`axes` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "PRIMARY KEY (`user_id`),"
-                + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
-                + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
-        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "experience` ("
-                + "`user_id` int(10) unsigned NOT NULL,"
-                + "`taming` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`mining` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`woodcutting` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`repair` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`unarmed` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`herbalism` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`excavation` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`archery` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`swords` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`axes` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0',"
-                + "PRIMARY KEY (`user_id`),"
-                + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
-                + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
-
-        checkDatabaseStructure(DatabaseUpdateType.FISHING);
-        checkDatabaseStructure(DatabaseUpdateType.BLAST_MINING);
-        checkDatabaseStructure(DatabaseUpdateType.CASCADE_DELETE);
-        checkDatabaseStructure(DatabaseUpdateType.INDEX);
-        checkDatabaseStructure(DatabaseUpdateType.MOB_HEALTHBARS);
-    }
-
-    /**
-     * Attempt to write the SQL query.
-     *
-     * @param sql Query to write.
-     * @return true if the query was successfully written, false otherwise.
-     */
-    public static boolean write(String sql) {
-        if (!checkConnected()) {
-            return false;
-        }
-
-        PreparedStatement statement = null;
-        try {
-            statement = connection.prepareStatement(sql);
-            statement.executeUpdate();
-            return true;
-        }
-        catch (SQLException ex) {
-            printErrors(ex);
-            return false;
-        }
-        finally {
-            if (statement != null) {
-                try {
-                    statement.close();
-                }
-                catch (SQLException e) {
-                    printErrors(e);
-                }
-            }
-        }
-    }
-
-    /**
-     * Returns the number of rows affected by either a DELETE or UPDATE query
-     *
-     * @param sql SQL query to execute
-     * @return the number of rows affected
-     */
-    public static int update(String sql) {
-        if (!checkConnected()) {
-            return 0;
-        }
-
-        int rows = 0;
-
-        PreparedStatement statement = null;
-        try {
-            statement = connection.prepareStatement(sql);
-            rows = statement.executeUpdate();
-        }
-        catch (SQLException ex) {
-            printErrors(ex);
-        }
-        finally {
-            if (statement != null) {
-                try {
-                    statement.close();
-                }
-                catch (SQLException e) {
-                    printErrors(e);
-                }
-            }
-        }
-
-        return rows;
+    public void purgePowerlessUsers() {
+        plugin.getLogger().info("Purging powerless users...");
+        plugin.getLogger().info("Purged " + (isUsingSQL ? SQLDatabaseManager.purgePowerlessSQL() : FlatfileDatabaseManager.purgePowerlessFlatfile()) + " users from the database.");
     }
     }
 
 
-    /**
-     * Get the Integer. Only return first row / first field.
-     *
-     * @param sql SQL query to execute
-     * @return the value in the first row / first field
-     */
-    public static int getInt(String sql) {
-        if (!checkConnected()) {
-            return 0;
-        }
-
-        int result = 0;
-
-        PreparedStatement statement = null;
-
-        try {
-            statement = connection.prepareStatement(sql);
-            ResultSet resultSet = statement.executeQuery();
-
-            if (resultSet.next()) {
-                result = resultSet.getInt(1);
-            }
-        }
-        catch (SQLException ex) {
-            printErrors(ex);
-        }
-        finally {
-            if (statement != null) {
-                try {
-                    statement.close();
-                }
-                catch (SQLException e) {
-                    printErrors(e);
-                }
-            }
-        }
-
-        return result;
+    public void purgeOldUsers() {
+        plugin.getLogger().info("Purging old users...");
+        plugin.getLogger().info("Purged " + (isUsingSQL ? SQLDatabaseManager.purgeOldSQL() : FlatfileDatabaseManager.removeOldFlatfileUsers()) + " users from the database.");
     }
     }
 
 
-    /**
-     * Check connection status and re-establish if dead or stale.
-     *
-     * If the very first immediate attempt fails, further attempts
-     * will be made in progressively larger intervals up to MAX_WAIT
-     * intervals.
-     *
-     * This allows for MySQL to time out idle connections as needed by
-     * server operator, without affecting McMMO, while still providing
-     * protection against a database outage taking down Bukkit's tick
-     * processing loop due to attemping a database connection each
-     * time McMMO needs the database.
-     *
-     * @return the boolean value for whether or not we are connected
-     */
-    public static boolean checkConnected() {
-        boolean isClosed = true;
-        boolean isValid = false;
-        boolean exists = (connection != null);
-
-        // If we're waiting for server to recover then leave early
-        if (nextReconnectTimestamp > 0 && nextReconnectTimestamp > System.nanoTime()) {
-            return false;
-        }
-
-        if (exists) {
-            try {
-                isClosed = connection.isClosed();
-            }
-            catch (SQLException e) {
-                isClosed = true;
-                e.printStackTrace();
-                printErrors(e);
-            }
-
-            if (!isClosed) {
-                try {
-                    isValid = connection.isValid(VALID_TIMEOUT);
-                }
-                catch (SQLException e) {
-                    // Don't print stack trace because it's valid to lose idle connections to the server and have to restart them.
-                    isValid = false;
-                }
-            }
-        }
-
-        // Leave if all ok
-        if (exists && !isClosed && isValid) {
-            // Housekeeping
-            nextReconnectTimestamp = 0;
-            reconnectAttempt = 0;
+    public boolean removeUser(String playerName) {
+        if (isUsingSQL ? SQLDatabaseManager.removeUserSQL(playerName) : FlatfileDatabaseManager.removeFlatFileUser(playerName)) {
+            Misc.profileCleanup(playerName);
             return true;
             return true;
         }
         }
 
 
-        // Cleanup after ourselves for GC and MySQL's sake
-        if (exists && !isClosed) {
-            try {
-                connection.close();
-            }
-            catch (SQLException ex) {
-                // This is a housekeeping exercise, ignore errors
-            }
-        }
-
-        // Try to connect again
-        connect();
-
-        // Leave if connection is good
-        try {
-            if (connection != null && !connection.isClosed()) {
-                // Schedule a database save if we really had an outage
-                if (reconnectAttempt > 1) {
-                    new SQLReconnectTask().runTaskLater(mcMMO.p, 5);
-                }
-                nextReconnectTimestamp = 0;
-                reconnectAttempt = 0;
-                return true;
-            }
-        }
-        catch (SQLException e) {
-            // Failed to check isClosed, so presume connection is bad and attempt later
-            e.printStackTrace();
-            printErrors(e);
-        }
-
-        reconnectAttempt++;
-        nextReconnectTimestamp = (long)(System.nanoTime() + Math.min(MAX_WAIT, (reconnectAttempt * SCALING_FACTOR * MIN_WAIT)));
         return false;
         return false;
     }
     }
 
 
-    /**
-     * Read SQL query.
-     *
-     * @param sql SQL query to read
-     * @return the rows in this SQL query
-     */
-    public static HashMap<Integer, ArrayList<String>> read(String sql) {
-        ResultSet resultSet;
-        HashMap<Integer, ArrayList<String>> rows = new HashMap<Integer, ArrayList<String>>();
-
-        if (checkConnected()) {
-            PreparedStatement statement = null;
-
-            try {
-                statement = connection.prepareStatement(sql);
-                resultSet = statement.executeQuery();
-
-                while (resultSet.next()) {
-                    ArrayList<String> column = new ArrayList<String>();
-
-                    for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
-                        column.add(resultSet.getString(i));
-                    }
-
-                    rows.put(resultSet.getRow(), column);
-                }
-            }
-            catch (SQLException ex) {
-                printErrors(ex);
-            }
-            finally {
-                if (statement != null) {
-                    try {
-                        statement.close();
-                    }
-                    catch (SQLException e) {
-                        printErrors(e);
-                    }
-                }
-            }
-        }
-
-        return rows;
-    }
-
-    public static Map<String, Integer> readSQLRank(String playerName) {
-        ResultSet resultSet;
-        Map<String, Integer> skills = new HashMap<String, Integer>();
-
-        if (checkConnected()) {
-            try {
-                for (SkillType skillType : SkillType.values()) {
-                    if (skillType.isChildSkill()) {
-                        continue;
-                    }
-
-                    String skillName = skillType.name().toLowerCase();
-                    String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " +
-                                 "AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
-                                 "WHERE user = ?)";
-
-                    PreparedStatement statement = connection.prepareStatement(sql);
-                    statement.setString(1, playerName);
-                    resultSet = statement.executeQuery();
-
-                    resultSet.next();
-
-                    int rank = resultSet.getInt("rank");
-
-                    sql = "SELECT user, " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " +
-                          "AND " + skillName + " = (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
-                          "WHERE user = '" + playerName + "') ORDER BY user";
-
-                    statement = connection.prepareStatement(sql);
-                    resultSet = statement.executeQuery();
-
-                    while (resultSet.next()) {
-                        if (resultSet.getString("user").equalsIgnoreCase(playerName)) {
-                            skills.put(skillType.name(), rank + resultSet.getRow());
-                            break;
-                        }
-                    }
-
-                    statement.close();
-                }
-
-                String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
-                             "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > 0 " +
-                             "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > " +
-                             "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " +
-                             "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)";
-
-                PreparedStatement statement = connection.prepareStatement(sql);
-                statement.setString(1, playerName);
-                resultSet = statement.executeQuery();
-
-                resultSet.next();
-
-                int rank = resultSet.getInt("rank");
-
-                sql = "SELECT user, taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " +
-                      "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
-                      "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > 0 " +
-                      "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing = " +
-                      "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " +
-                      "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?) ORDER BY user";
-
-                statement = connection.prepareStatement(sql);
-                statement.setString(1, playerName);
-                resultSet = statement.executeQuery();
-
-                while (resultSet.next()) {
-                    if (resultSet.getString("user").equalsIgnoreCase(playerName)) {
-                        skills.put("ALL", rank + resultSet.getRow());
-                        break;
-                    }
-                }
-
-                statement.close();
-            }
-            catch (SQLException ex) {
-                printErrors(ex);
-            }
-        }
-
-        return skills;
-    }
-
-    public static void purgePowerlessSQL() {
-        mcMMO.p.getLogger().info("Purging powerless users...");
-        HashMap<Integer, ArrayList<String>> usernames;
-
-        usernames = read("SELECT u.user FROM " + tablePrefix + "skills AS s, " + tablePrefix + "users AS u " + "WHERE s.user_id = u.id AND " +
-                "(s.taming+s.mining+s.woodcutting+s.repair+s.unarmed+s.herbalism+s.excavation+s.archery+s.swords+s.axes+s.acrobatics+s.fishing) = 0");
-
-        write("DELETE FROM " + tablePrefix + "users WHERE " + tablePrefix + "users.id IN (SELECT * FROM " +
-                "(SELECT u.id FROM " + tablePrefix + "skills AS s, " + tablePrefix + "users AS u " + "WHERE s.user_id = u.id " +
-                "AND (s.taming+s.mining+s.woodcutting+s.repair+s.unarmed+s.herbalism+s.excavation+s.archery+s.swords+s.axes+s.acrobatics+s.fishing) = 0) AS p)");
-
-        int purgedUsers = 0;
-        for (int i = 1; i <= usernames.size(); i++) {
-            String playerName = usernames.get(i).get(0);
-
-            if (playerName == null || mcMMO.p.getServer().getOfflinePlayer(playerName).isOnline()) {
-                continue;
-            }
-
-            Misc.profileCleanup(playerName);
-            purgedUsers++;
-        }
-
-        mcMMO.p.getLogger().info("Purged " + purgedUsers + " users from the database.");
-    }
-
-    public static void purgeOldSQL() {
-        mcMMO.p.getLogger().info("Purging old users...");
-        long currentTime = System.currentTimeMillis();
-        long purgeTime = ONE_MONTH * Config.getInstance().getOldUsersCutoff();
-        HashMap<Integer, ArrayList<String>> usernames = read("SELECT user FROM " + tablePrefix + "users WHERE ((" + currentTime + " - lastlogin*1000) > " + purgeTime + ")");
-        write("DELETE FROM " + tablePrefix + "users WHERE " + tablePrefix + "users.id IN (SELECT * FROM (SELECT id FROM " + tablePrefix + "users WHERE ((" + currentTime + " - lastlogin*1000) > " + purgeTime + ")) AS p)");
-
-        int purgedUsers = 0;
-        for (int i = 1; i <= usernames.size(); i++) {
-            String playerName = usernames.get(i).get(0);
-
-            if (playerName == null) {
-                continue;
-            }
-
-            Misc.profileCleanup(playerName);
-            purgedUsers++;
+    private void createFlatfileDatabase() {
+        if (usersFile.exists()) {
+            return;
         }
         }
 
 
-        mcMMO.p.getLogger().info("Purged " + purgedUsers + " users from the database.");
-    }
-
-    /**
-     * Check database structure for missing values.
-     *
-     * @param update Type of data to check updates for
-     */
-    private static void checkDatabaseStructure(DatabaseUpdateType update) {
-        String sql = null;
-        ResultSet resultSet = null;
-        HashMap<Integer, ArrayList<String>> rows = new HashMap<Integer, ArrayList<String>>();
-
-        switch (update) {
-            case BLAST_MINING:
-                sql = "SELECT * FROM  `" + tablePrefix + "cooldowns` ORDER BY  `" + tablePrefix + "cooldowns`.`blast_mining` ASC LIMIT 0 , 30";
-                break;
-
-            case CASCADE_DELETE:
-                write("ALTER TABLE `" + tablePrefix + "huds` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE;");
-                write("ALTER TABLE `" + tablePrefix + "experience` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE;");
-                write("ALTER TABLE `" + tablePrefix + "cooldowns` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE;");
-                write("ALTER TABLE `" + tablePrefix + "skills` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE;");
-                break;
-
-            case FISHING:
-                sql = "SELECT * FROM  `" + tablePrefix + "experience` ORDER BY  `" + tablePrefix + "experience`.`fishing` ASC LIMIT 0 , 30";
-                break;
+        usersFile.getParentFile().mkdir();
 
 
-            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;");
-                }
-                break;
-
-            case MOB_HEALTHBARS:
-                sql = "SELECT * FROM  `" + tablePrefix + "huds` ORDER BY  `" + tablePrefix + "huds`.`mobhealthbar` ASC LIMIT 0 , 30";
-                break;
-
-            default:
-                break;
-        }
-
-        PreparedStatement statement = null;
         try {
         try {
-            if (!checkConnected()) {
-                return;
-            }
-
-            statement = connection.prepareStatement(sql);
-            resultSet = statement.executeQuery();
-
-            while (resultSet.next()) {
-                ArrayList<String> column = new ArrayList<String>();
-
-                for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
-                    column.add(resultSet.getString(i));
-                }
-
-                rows.put(resultSet.getRow(), column);
-            }
+            plugin.debug("Creating mcmmo.users file...");
+            new File(mcMMO.getUsersFilePath()).createNewFile();
         }
         }
-        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' ;");
-                    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' ;");
-                    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 'HEARTS' ;");
-                    break;
-
-                default:
-                    break;
-            }
-        }
-        finally {
-            if (statement != null) {
-                try {
-                    statement.close();
-                }
-                catch (SQLException e) {
-                    // Ignore the error, we're leaving
-                }
-            }
+        catch (IOException e) {
+            e.printStackTrace();
         }
         }
     }
     }
-
-    private static void printErrors(SQLException ex) {
-        mcMMO.p.getLogger().severe("SQLException: " + ex.getMessage());
-        mcMMO.p.getLogger().severe("SQLState: " + ex.getSQLState());
-        mcMMO.p.getLogger().severe("VendorError: " + ex.getErrorCode());
-    }
 }
 }

+ 50 - 85
src/main/java/com/gmail/nossr50/database/LeaderboardManager.java → src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java

@@ -9,6 +9,7 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.Config;
@@ -16,7 +17,7 @@ import com.gmail.nossr50.datatypes.database.PlayerStat;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.util.StringUtils;
 import com.gmail.nossr50.util.StringUtils;
 
 
-public final class LeaderboardManager {
+public final class FlatfileDatabaseManager {
     private static HashMap<SkillType, List<PlayerStat>> playerStatHash = new HashMap<SkillType, List<PlayerStat>>();
     private static HashMap<SkillType, List<PlayerStat>> playerStatHash = new HashMap<SkillType, List<PlayerStat>>();
     private static List<PlayerStat> powerLevels = new ArrayList<PlayerStat>();
     private static List<PlayerStat> powerLevels = new ArrayList<PlayerStat>();
     private static long lastUpdate = 0;
     private static long lastUpdate = 0;
@@ -24,7 +25,7 @@ public final class LeaderboardManager {
     private static final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
     private static final long UPDATE_WAIT_TIME = 600000L; // 10 minutes
     private static final long ONE_MONTH = 2630000000L;
     private static final long ONE_MONTH = 2630000000L;
 
 
-    private LeaderboardManager() {}
+    private FlatfileDatabaseManager() {}
 
 
     /**
     /**
      * Update the leader boards.
      * Update the leader boards.
@@ -35,6 +36,7 @@ public final class LeaderboardManager {
             return;
             return;
         }
         }
 
 
+        String usersFilePath = mcMMO.getUsersFilePath();
         lastUpdate = System.currentTimeMillis(); // Log when the last update was run
         lastUpdate = System.currentTimeMillis(); // Log when the last update was run
         powerLevels.clear(); // Clear old values from the power levels
         powerLevels.clear(); // Clear old values from the power levels
 
 
@@ -54,8 +56,7 @@ public final class LeaderboardManager {
 
 
         // Read from the FlatFile database and fill our arrays with information
         // Read from the FlatFile database and fill our arrays with information
         try {
         try {
-            FileReader file = new FileReader(mcMMO.getUsersFilePath());
-            BufferedReader in = new BufferedReader(file);
+            BufferedReader in = new BufferedReader(new FileReader(usersFilePath));
             String line = "";
             String line = "";
             ArrayList<String> players = new ArrayList<String>();
             ArrayList<String> players = new ArrayList<String>();
 
 
@@ -72,72 +73,25 @@ public final class LeaderboardManager {
 
 
                 players.add(playerName);
                 players.add(playerName);
 
 
-                if (data.length > 1 && StringUtils.isInt(data[1])) {
-                    mining.add(new PlayerStat(playerName, Integer.parseInt(data[1])));
-                    powerLevel += Integer.parseInt(data[1]);
-                }
-
-                if (data.length > 5 && StringUtils.isInt(data[5])) {
-                    woodcutting.add(new PlayerStat(playerName, Integer.parseInt(data[5])));
-                    powerLevel += Integer.parseInt(data[5]);
-                }
-
-                if (data.length > 7 && StringUtils.isInt(data[7])) {
-                    repair.add(new PlayerStat(playerName, Integer.parseInt(data[7])));
-                    powerLevel += Integer.parseInt(data[7]);
-                }
-
-                if (data.length > 8 && StringUtils.isInt(data[8])) {
-                    unarmed.add(new PlayerStat(playerName, Integer.parseInt(data[8])));
-                    powerLevel += Integer.parseInt(data[8]);
-                }
-
-                if (data.length > 9 && StringUtils.isInt(data[9])) {
-                    herbalism.add(new PlayerStat(playerName, Integer.parseInt(data[9])));
-                    powerLevel += Integer.parseInt(data[9]);
-                }
-
-                if (data.length > 10 && StringUtils.isInt(data[10])) {
-                    excavation.add(new PlayerStat(playerName, Integer.parseInt(data[10])));
-                    powerLevel += Integer.parseInt(data[10]);
-                }
-
-                if (data.length > 11 && StringUtils.isInt(data[11])) {
-                    archery.add(new PlayerStat(playerName, Integer.parseInt(data[11])));
-                    powerLevel += Integer.parseInt(data[11]);
-                }
-
-                if (data.length > 12 && StringUtils.isInt(data[12])) {
-                    swords.add(new PlayerStat(playerName, Integer.parseInt(data[12])));
-                    powerLevel += Integer.parseInt(data[12]);
-                }
-
-                if (data.length > 13 && StringUtils.isInt(data[13])) {
-                    axes.add(new PlayerStat(playerName, Integer.parseInt(data[13])));
-                    powerLevel += Integer.parseInt(data[13]);
-                }
-
-                if (data.length > 14 && StringUtils.isInt(data[14])) {
-                    acrobatics.add(new PlayerStat(playerName, Integer.parseInt(data[14])));
-                    powerLevel += Integer.parseInt(data[14]);
-                }
-
-                if (data.length > 24 && StringUtils.isInt(data[24])) {
-                    taming.add(new PlayerStat(playerName, Integer.parseInt(data[24])));
-                    powerLevel += Integer.parseInt(data[24]);
-                }
-
-                if (data.length > 34 && StringUtils.isInt(data[34])) {
-                    fishing.add(new PlayerStat(playerName, Integer.parseInt(data[34])));
-                    powerLevel += Integer.parseInt(data[34]);
-                }
+                powerLevel += loadStat(mining, playerName, data, 1);
+                powerLevel += loadStat(woodcutting, playerName, data, 5);
+                powerLevel += loadStat(repair, playerName, data, 7);
+                powerLevel += loadStat(unarmed, playerName, data, 8);
+                powerLevel += loadStat(herbalism, playerName, data, 9);
+                powerLevel += loadStat(excavation, playerName, data, 10);
+                powerLevel += loadStat(archery, playerName, data, 11);
+                powerLevel += loadStat(swords, playerName, data, 12);
+                powerLevel += loadStat(axes, playerName, data, 13);
+                powerLevel += loadStat(acrobatics, playerName, data, 14);
+                powerLevel += loadStat(taming, playerName, data, 24);
+                powerLevel += loadStat(fishing, playerName, data, 34);
 
 
                 powerLevels.add(new PlayerStat(playerName, powerLevel));
                 powerLevels.add(new PlayerStat(playerName, powerLevel));
             }
             }
             in.close();
             in.close();
         }
         }
         catch (Exception e) {
         catch (Exception e) {
-            mcMMO.p.getLogger().severe("Exception while reading " + mcMMO.getUsersFilePath() + " (Are you sure you formatted it correctly?)" + e.toString());
+            mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString());
         }
         }
 
 
         SkillComparator c = new SkillComparator();
         SkillComparator c = new SkillComparator();
@@ -203,14 +157,6 @@ public final class LeaderboardManager {
         return info;
         return info;
     }
     }
 
 
-    public static int[] getPlayerRank(String playerName) {
-        return getPlayerRank(playerName, powerLevels);
-    }
-
-    public static int[] getPlayerRank(String playerName, SkillType skillType) {
-        return getPlayerRank(playerName, playerStatHash.get(skillType));
-    }
-
     public static boolean removeFlatFileUser(String playerName) {
     public static boolean removeFlatFileUser(String playerName) {
         boolean worked = false;
         boolean worked = false;
 
 
@@ -266,7 +212,7 @@ public final class LeaderboardManager {
         return worked;
         return worked;
     }
     }
 
 
-    public static void purgePowerlessFlatfile() {
+    public static int purgePowerlessFlatfile() {
         mcMMO.p.getLogger().info("Purging powerless users...");
         mcMMO.p.getLogger().info("Purging powerless users...");
 
 
         int purgedUsers = 0;
         int purgedUsers = 0;
@@ -277,16 +223,10 @@ public final class LeaderboardManager {
             }
             }
         }
         }
 
 
-        mcMMO.p.getLogger().info("Purged " + purgedUsers + " users from the database.");
-    }
-
-    public static void purgeOldFlatfile() {
-        mcMMO.p.getLogger().info("Purging old users...");
-        int purgedUsers = removeOldFlatfileUsers();
-        mcMMO.p.getLogger().info("Purged " + purgedUsers + " users from the database.");
+        return purgedUsers;
     }
     }
 
 
-    private static int removeOldFlatfileUsers() {
+    public static int removeOldFlatfileUsers() {
         int removedPlayers = 0;
         int removedPlayers = 0;
         long currentTime = System.currentTimeMillis();
         long currentTime = System.currentTimeMillis();
         long purgeTime = ONE_MONTH * Config.getInstance().getOldUsersCutoff();
         long purgeTime = ONE_MONTH * Config.getInstance().getOldUsersCutoff();
@@ -350,22 +290,47 @@ public final class LeaderboardManager {
         return removedPlayers;
         return removedPlayers;
     }
     }
 
 
-    private static int[] getPlayerRank(String playerName, List<PlayerStat> statsList) {
+    private static Integer getPlayerRank(String playerName, List<PlayerStat> statsList) {
         int currentPos = 1;
         int currentPos = 1;
 
 
         if (statsList == null) {
         if (statsList == null) {
-            return new int[] {0, 0};
+            return null;
         }
         }
 
 
         for (PlayerStat stat : statsList) {
         for (PlayerStat stat : statsList) {
             if (stat.name.equalsIgnoreCase(playerName)) {
             if (stat.name.equalsIgnoreCase(playerName)) {
-                return new int[] {currentPos, stat.statVal};
+                return currentPos;
             }
             }
 
 
             currentPos++;
             currentPos++;
         }
         }
 
 
-        return new int[] {0, 0};
+        return null;
+    }
+
+    public static Map<String, Integer> getPlayerRanks(String playerName) {
+        updateLeaderboards();
+
+        Map<String, Integer> skills = new HashMap<String, Integer>();
+
+        for (SkillType skill : SkillType.values()) {
+            skills.put(playerName, getPlayerRank(playerName, playerStatHash.get(skill)));
+        }
+
+        skills.put("ALL", getPlayerRank(playerName, powerLevels));
+
+        return skills;
+    }
+
+    private static int loadStat(List<PlayerStat> statList, String playerName, String[] data, int dataIndex) {
+        if (data.length > dataIndex) {
+            int statValue = Integer.parseInt(data[dataIndex]);
+
+            statList.add(new PlayerStat(playerName, statValue));
+            return statValue;
+        }
+
+        return 0;
     }
     }
 
 
     private static class SkillComparator implements Comparator<PlayerStat> {
     private static class SkillComparator implements Comparator<PlayerStat> {

+ 628 - 0
src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java

@@ -0,0 +1,628 @@
+package com.gmail.nossr50.database;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import com.gmail.nossr50.mcMMO;
+import com.gmail.nossr50.config.Config;
+import com.gmail.nossr50.datatypes.database.DatabaseUpdateType;
+import com.gmail.nossr50.datatypes.skills.SkillType;
+import com.gmail.nossr50.runnables.database.SQLReconnectTask;
+import com.gmail.nossr50.util.Misc;
+
+public final class SQLDatabaseManager {
+    private static String connectionString;
+
+    private static String tablePrefix = Config.getInstance().getMySQLTablePrefix();
+    private static Connection connection = null;
+
+    // Scale waiting time by this much per failed attempt
+    private static final double SCALING_FACTOR = 40;
+
+    // Minimum wait in nanoseconds (default 500ms)
+    private static final long MIN_WAIT = 500L * 1000000L;
+
+    // Maximum time to wait between reconnects (default 5 minutes)
+    private static final long MAX_WAIT = 5L * 60L * 1000L * 1000000L;
+
+    // How long to wait when checking if connection is valid (default 3 seconds)
+    private static final int VALID_TIMEOUT = 3;
+
+    // When next to try connecting to Database in nanoseconds
+    private static long nextReconnectTimestamp = 0L;
+
+    // How many connection attempts have failed
+    private static int reconnectAttempt = 0;
+
+    private static final long ONE_MONTH = 2630000000L;
+
+    private SQLDatabaseManager() {}
+
+    /**
+     * Attempt to connect to the mySQL database.
+     */
+    public static void connect() {
+        Config configInstance = Config.getInstance();
+        connectionString = "jdbc:mysql://" + configInstance.getMySQLServerName() + ":" + configInstance.getMySQLServerPort() + "/" + configInstance.getMySQLDatabaseName();
+
+        try {
+            mcMMO.p.getLogger().info("Attempting connection to MySQL...");
+
+            // Force driver to load if not yet loaded
+            Class.forName("com.mysql.jdbc.Driver");
+            Properties connectionProperties = new Properties();
+            connectionProperties.put("user", configInstance.getMySQLUserName());
+            connectionProperties.put("password", configInstance.getMySQLUserPassword());
+            connectionProperties.put("autoReconnect", "false");
+            connectionProperties.put("maxReconnects", "0");
+            connection = DriverManager.getConnection(connectionString, connectionProperties);
+
+            mcMMO.p.getLogger().info("Connection to MySQL was a success!");
+        }
+        catch (SQLException ex) {
+            connection = null;
+
+            if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
+                mcMMO.p.getLogger().info("Connection to MySQL failed!");
+            }
+        }
+        catch (ClassNotFoundException ex) {
+            connection = null;
+
+            if (reconnectAttempt == 0 || reconnectAttempt >= 11) {
+                mcMMO.p.getLogger().info("MySQL database driver not found!");
+            }
+        }
+    }
+
+    /**
+     * Attempt to create the database structure.
+     */
+    public static void createStructure() {
+        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` ("
+                + "`id` int(10) unsigned NOT NULL AUTO_INCREMENT,"
+                + "`user` varchar(40) NOT NULL,"
+                + "`lastlogin` int(32) unsigned NOT NULL,"
+                + "PRIMARY KEY (`id`),"
+                + "UNIQUE KEY `user` (`user`)) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;");
+        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "huds` ("
+                + "`user_id` int(10) unsigned NOT NULL,"
+                + "`hudtype` varchar(50) NOT NULL DEFAULT 'STANDARD',"
+                + "`mobhealthbar` varchar(50) NOT NULL DEFAULT 'HEARTS',"
+                + "PRIMARY KEY (`user_id`),"
+                + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
+                + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
+        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "cooldowns` ("
+                + "`user_id` int(10) unsigned NOT NULL,"
+                + "`taming` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`mining` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`woodcutting` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`repair` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`unarmed` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`herbalism` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`excavation` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`archery` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`swords` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`axes` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`acrobatics` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "`blast_mining` int(32) unsigned NOT NULL DEFAULT '0',"
+                + "PRIMARY KEY (`user_id`),"
+                + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
+                + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
+        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "skills` ("
+                + "`user_id` int(10) unsigned NOT NULL,"
+                + "`taming` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`mining` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`woodcutting` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`repair` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`unarmed` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`herbalism` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`excavation` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`archery` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`swords` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`axes` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "PRIMARY KEY (`user_id`),"
+                + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
+                + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
+        write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "experience` ("
+                + "`user_id` int(10) unsigned NOT NULL,"
+                + "`taming` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`mining` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`woodcutting` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`repair` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`unarmed` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`herbalism` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`excavation` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`archery` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`swords` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`axes` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "`acrobatics` int(10) unsigned NOT NULL DEFAULT '0',"
+                + "PRIMARY KEY (`user_id`),"
+                + "FOREIGN KEY (`user_id`) REFERENCES `" + tablePrefix + "users` (`id`) "
+                + "ON DELETE CASCADE) ENGINE=MyISAM DEFAULT CHARSET=latin1;");
+
+        checkDatabaseStructure(DatabaseUpdateType.FISHING);
+        checkDatabaseStructure(DatabaseUpdateType.BLAST_MINING);
+        checkDatabaseStructure(DatabaseUpdateType.CASCADE_DELETE);
+        checkDatabaseStructure(DatabaseUpdateType.INDEX);
+        checkDatabaseStructure(DatabaseUpdateType.MOB_HEALTHBARS);
+    }
+
+    /**
+     * Attempt to write the SQL query.
+     *
+     * @param sql Query to write.
+     * @return true if the query was successfully written, false otherwise.
+     */
+    public static boolean write(String sql) {
+        if (!checkConnected()) {
+            return false;
+        }
+
+        PreparedStatement statement = null;
+        try {
+            statement = connection.prepareStatement(sql);
+            statement.executeUpdate();
+            return true;
+        }
+        catch (SQLException ex) {
+            printErrors(ex);
+            return false;
+        }
+        finally {
+            if (statement != null) {
+                try {
+                    statement.close();
+                }
+                catch (SQLException e) {
+                    printErrors(e);
+                }
+            }
+        }
+    }
+
+    public static boolean removeUserSQL(String playerName) {
+        return SQLDatabaseManager.update("DELETE FROM " + tablePrefix + "users WHERE " + tablePrefix + "users.user = '" + playerName + "'") != 0;
+    }
+
+    /**
+     * Returns the number of rows affected by either a DELETE or UPDATE query
+     *
+     * @param sql SQL query to execute
+     * @return the number of rows affected
+     */
+    public static int update(String sql) {
+        if (!checkConnected()) {
+            return 0;
+        }
+
+        int rows = 0;
+
+        PreparedStatement statement = null;
+        try {
+            statement = connection.prepareStatement(sql);
+            rows = statement.executeUpdate();
+        }
+        catch (SQLException ex) {
+            printErrors(ex);
+        }
+        finally {
+            if (statement != null) {
+                try {
+                    statement.close();
+                }
+                catch (SQLException e) {
+                    printErrors(e);
+                }
+            }
+        }
+
+        return rows;
+    }
+
+    /**
+     * Get the Integer. Only return first row / first field.
+     *
+     * @param sql SQL query to execute
+     * @return the value in the first row / first field
+     */
+    public static int getInt(String sql) {
+        if (!checkConnected()) {
+            return 0;
+        }
+
+        int result = 0;
+
+        PreparedStatement statement = null;
+
+        try {
+            statement = connection.prepareStatement(sql);
+            ResultSet resultSet = statement.executeQuery();
+
+            if (resultSet.next()) {
+                result = resultSet.getInt(1);
+            }
+        }
+        catch (SQLException ex) {
+            printErrors(ex);
+        }
+        finally {
+            if (statement != null) {
+                try {
+                    statement.close();
+                }
+                catch (SQLException e) {
+                    printErrors(e);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Check connection status and re-establish if dead or stale.
+     *
+     * If the very first immediate attempt fails, further attempts
+     * will be made in progressively larger intervals up to MAX_WAIT
+     * intervals.
+     *
+     * This allows for MySQL to time out idle connections as needed by
+     * server operator, without affecting McMMO, while still providing
+     * protection against a database outage taking down Bukkit's tick
+     * processing loop due to attemping a database connection each
+     * time McMMO needs the database.
+     *
+     * @return the boolean value for whether or not we are connected
+     */
+    public static boolean checkConnected() {
+        boolean isClosed = true;
+        boolean isValid = false;
+        boolean exists = (connection != null);
+
+        // If we're waiting for server to recover then leave early
+        if (nextReconnectTimestamp > 0 && nextReconnectTimestamp > System.nanoTime()) {
+            return false;
+        }
+
+        if (exists) {
+            try {
+                isClosed = connection.isClosed();
+            }
+            catch (SQLException e) {
+                isClosed = true;
+                e.printStackTrace();
+                printErrors(e);
+            }
+
+            if (!isClosed) {
+                try {
+                    isValid = connection.isValid(VALID_TIMEOUT);
+                }
+                catch (SQLException e) {
+                    // Don't print stack trace because it's valid to lose idle connections to the server and have to restart them.
+                    isValid = false;
+                }
+            }
+        }
+
+        // Leave if all ok
+        if (exists && !isClosed && isValid) {
+            // Housekeeping
+            nextReconnectTimestamp = 0;
+            reconnectAttempt = 0;
+            return true;
+        }
+
+        // Cleanup after ourselves for GC and MySQL's sake
+        if (exists && !isClosed) {
+            try {
+                connection.close();
+            }
+            catch (SQLException ex) {
+                // This is a housekeeping exercise, ignore errors
+            }
+        }
+
+        // Try to connect again
+        connect();
+
+        // Leave if connection is good
+        try {
+            if (connection != null && !connection.isClosed()) {
+                // Schedule a database save if we really had an outage
+                if (reconnectAttempt > 1) {
+                    new SQLReconnectTask().runTaskLater(mcMMO.p, 5);
+                }
+                nextReconnectTimestamp = 0;
+                reconnectAttempt = 0;
+                return true;
+            }
+        }
+        catch (SQLException e) {
+            // Failed to check isClosed, so presume connection is bad and attempt later
+            e.printStackTrace();
+            printErrors(e);
+        }
+
+        reconnectAttempt++;
+        nextReconnectTimestamp = (long)(System.nanoTime() + Math.min(MAX_WAIT, (reconnectAttempt * SCALING_FACTOR * MIN_WAIT)));
+        return false;
+    }
+
+    /**
+     * Read SQL query.
+     *
+     * @param sql SQL query to read
+     * @return the rows in this SQL query
+     */
+    public static HashMap<Integer, ArrayList<String>> read(String sql) {
+        ResultSet resultSet;
+        HashMap<Integer, ArrayList<String>> rows = new HashMap<Integer, ArrayList<String>>();
+
+        if (checkConnected()) {
+            PreparedStatement statement = null;
+
+            try {
+                statement = connection.prepareStatement(sql);
+                resultSet = statement.executeQuery();
+
+                while (resultSet.next()) {
+                    ArrayList<String> column = new ArrayList<String>();
+
+                    for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
+                        column.add(resultSet.getString(i));
+                    }
+
+                    rows.put(resultSet.getRow(), column);
+                }
+            }
+            catch (SQLException ex) {
+                printErrors(ex);
+            }
+            finally {
+                if (statement != null) {
+                    try {
+                        statement.close();
+                    }
+                    catch (SQLException e) {
+                        printErrors(e);
+                    }
+                }
+            }
+        }
+
+        return rows;
+    }
+
+    public static Map<String, Integer> readSQLRank(String playerName) {
+        ResultSet resultSet;
+        Map<String, Integer> skills = new HashMap<String, Integer>();
+
+        if (checkConnected()) {
+            try {
+                for (SkillType skillType : SkillType.values()) {
+                    if (skillType.isChildSkill()) {
+                        continue;
+                    }
+
+                    String skillName = skillType.name().toLowerCase();
+                    String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " +
+                                 "AND " + skillName + " > (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
+                                 "WHERE user = ?)";
+
+                    PreparedStatement statement = connection.prepareStatement(sql);
+                    statement.setString(1, playerName);
+                    resultSet = statement.executeQuery();
+
+                    resultSet.next();
+
+                    int rank = resultSet.getInt("rank");
+
+                    sql = "SELECT user, " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE " + skillName + " > 0 " +
+                          "AND " + skillName + " = (SELECT " + skillName + " FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
+                          "WHERE user = '" + playerName + "') ORDER BY user";
+
+                    statement = connection.prepareStatement(sql);
+                    resultSet = statement.executeQuery();
+
+                    while (resultSet.next()) {
+                        if (resultSet.getString("user").equalsIgnoreCase(playerName)) {
+                            skills.put(skillType.name(), rank + resultSet.getRow());
+                            break;
+                        }
+                    }
+
+                    statement.close();
+                }
+
+                String sql = "SELECT COUNT(*) AS rank FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
+                             "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > 0 " +
+                             "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > " +
+                             "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " +
+                             "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?)";
+
+                PreparedStatement statement = connection.prepareStatement(sql);
+                statement.setString(1, playerName);
+                resultSet = statement.executeQuery();
+
+                resultSet.next();
+
+                int rank = resultSet.getInt("rank");
+
+                sql = "SELECT user, taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " +
+                      "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id " +
+                      "WHERE taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing > 0 " +
+                      "AND taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing = " +
+                      "(SELECT taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing " +
+                      "FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON user_id = id WHERE user = ?) ORDER BY user";
+
+                statement = connection.prepareStatement(sql);
+                statement.setString(1, playerName);
+                resultSet = statement.executeQuery();
+
+                while (resultSet.next()) {
+                    if (resultSet.getString("user").equalsIgnoreCase(playerName)) {
+                        skills.put("ALL", rank + resultSet.getRow());
+                        break;
+                    }
+                }
+
+                statement.close();
+            }
+            catch (SQLException ex) {
+                printErrors(ex);
+            }
+        }
+
+        return skills;
+    }
+
+    public static int purgePowerlessSQL() {
+        HashMap<Integer, ArrayList<String>> usernames = read("SELECT u.user FROM " + tablePrefix + "skills AS s, " + tablePrefix + "users AS u WHERE s.user_id = u.id AND (s.taming+s.mining+s.woodcutting+s.repair+s.unarmed+s.herbalism+s.excavation+s.archery+s.swords+s.axes+s.acrobatics+s.fishing) = 0");
+        write("DELETE FROM " + tablePrefix + "users WHERE " + tablePrefix + "users.id IN (SELECT * FROM (SELECT u.id FROM " + tablePrefix + "skills AS s, " + tablePrefix + "users AS u WHERE s.user_id = u.id AND (s.taming+s.mining+s.woodcutting+s.repair+s.unarmed+s.herbalism+s.excavation+s.archery+s.swords+s.axes+s.acrobatics+s.fishing) = 0) AS p)");
+
+        return processPurge(usernames.values());
+    }
+
+    public static int purgeOldSQL() {
+        long currentTime = System.currentTimeMillis();
+        long purgeTime = ONE_MONTH * Config.getInstance().getOldUsersCutoff();
+
+        HashMap<Integer, ArrayList<String>> usernames = read("SELECT user FROM " + tablePrefix + "users WHERE ((" + currentTime + " - lastlogin*1000) > " + purgeTime + ")");
+        write("DELETE FROM " + tablePrefix + "users WHERE " + tablePrefix + "users.id IN (SELECT * FROM (SELECT id FROM " + tablePrefix + "users WHERE ((" + currentTime + " - lastlogin*1000) > " + purgeTime + ")) AS p)");
+
+        return processPurge(usernames.values());
+    }
+
+    private static int processPurge(Collection<ArrayList<String>> usernames) {
+        int purgedUsers = 0;
+
+        for (ArrayList<String> user : usernames) {
+            Misc.profileCleanup(user.get(0));
+            purgedUsers++;
+        }
+
+        return purgedUsers;
+    }
+
+    /**
+     * Check database structure for missing values.
+     *
+     * @param update Type of data to check updates for
+     */
+    private static void checkDatabaseStructure(DatabaseUpdateType update) {
+        String sql = null;
+        ResultSet resultSet = null;
+        HashMap<Integer, ArrayList<String>> rows = new HashMap<Integer, ArrayList<String>>();
+
+        switch (update) {
+            case BLAST_MINING:
+                sql = "SELECT * FROM  `" + tablePrefix + "cooldowns` ORDER BY  `" + tablePrefix + "cooldowns`.`blast_mining` ASC LIMIT 0 , 30";
+                break;
+
+            case CASCADE_DELETE:
+                write("ALTER TABLE `" + tablePrefix + "huds` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE;");
+                write("ALTER TABLE `" + tablePrefix + "experience` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE;");
+                write("ALTER TABLE `" + tablePrefix + "cooldowns` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE;");
+                write("ALTER TABLE `" + tablePrefix + "skills` ADD FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE;");
+                break;
+
+            case FISHING:
+                sql = "SELECT * FROM  `" + tablePrefix + "experience` ORDER BY  `" + tablePrefix + "experience`.`fishing` 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;");
+                }
+                break;
+
+            case MOB_HEALTHBARS:
+                sql = "SELECT * FROM  `" + tablePrefix + "huds` ORDER BY  `" + tablePrefix + "huds`.`mobhealthbar` ASC LIMIT 0 , 30";
+                break;
+
+            default:
+                break;
+        }
+
+        PreparedStatement statement = null;
+        try {
+            if (!checkConnected()) {
+                return;
+            }
+
+            statement = connection.prepareStatement(sql);
+            resultSet = statement.executeQuery();
+
+            while (resultSet.next()) {
+                ArrayList<String> column = new ArrayList<String>();
+
+                for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) {
+                    column.add(resultSet.getString(i));
+                }
+
+                rows.put(resultSet.getRow(), column);
+            }
+        }
+        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' ;");
+                    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' ;");
+                    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 'HEARTS' ;");
+                    break;
+
+                default:
+                    break;
+            }
+        }
+        finally {
+            if (statement != null) {
+                try {
+                    statement.close();
+                }
+                catch (SQLException e) {
+                    // Ignore the error, we're leaving
+                }
+            }
+        }
+    }
+
+    private static void printErrors(SQLException ex) {
+        mcMMO.p.getLogger().severe("SQLException: " + ex.getMessage());
+        mcMMO.p.getLogger().severe("SQLState: " + ex.getSQLState());
+        mcMMO.p.getLogger().severe("VendorError: " + ex.getErrorCode());
+    }
+}

+ 411 - 500
src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java

@@ -12,7 +12,7 @@ import java.util.Set;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.spout.SpoutConfig;
 import com.gmail.nossr50.config.spout.SpoutConfig;
-import com.gmail.nossr50.database.DatabaseManager;
+import com.gmail.nossr50.database.SQLDatabaseManager;
 import com.gmail.nossr50.datatypes.MobHealthbarType;
 import com.gmail.nossr50.datatypes.MobHealthbarType;
 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;
@@ -20,512 +20,54 @@ import com.gmail.nossr50.datatypes.spout.huds.HudType;
 import com.gmail.nossr50.datatypes.spout.huds.McMMOHud;
 import com.gmail.nossr50.datatypes.spout.huds.McMMOHud;
 import com.gmail.nossr50.skills.child.FamilyTree;
 import com.gmail.nossr50.skills.child.FamilyTree;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Misc;
-import com.gmail.nossr50.util.StringUtils;
 
 
 public class PlayerProfile {
 public class PlayerProfile {
-    private String playerName;
-
-    // HUD
-    private McMMOHud spoutHud;
-    private HudType  hudType;
-    private MobHealthbarType mobHealthbarType;
-
-    // mySQL Stuff
+    private final String playerName;
     private int userId;
     private int userId;
-
     private boolean loaded;
     private boolean loaded;
 
 
-    private Map<SkillType, Integer>   skills     = new HashMap<SkillType, Integer>(); // Skills and Levels
-    private Map<SkillType, Integer>   skillsXp   = new HashMap<SkillType, Integer>(); // Skills and Xp
-    private Map<AbilityType, Integer> skillsDATS = new HashMap<AbilityType, Integer>();
+    /* HUDs */
+    private HudType hudType;
+    private MobHealthbarType mobHealthbarType;
+    private McMMOHud spoutHud;
 
 
-    private final static String location = mcMMO.getUsersFilePath();
+    /* Skill Data */
+    private final Map<SkillType, Integer>   skills     = new HashMap<SkillType, Integer>();   // Skill & Level
+    private final Map<SkillType, Integer>   skillsXp   = new HashMap<SkillType, Integer>();   // Skill & XP
+    private final Map<AbilityType, Integer> skillsDATS = new HashMap<AbilityType, Integer>(); // Ability & Cooldown
 
 
     public PlayerProfile(String playerName, boolean addNew) {
     public PlayerProfile(String playerName, boolean addNew) {
         this.playerName = playerName;
         this.playerName = playerName;
-        mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
 
 
-        if (mcMMO.spoutEnabled) {
-            hudType = SpoutConfig.getInstance().getDefaultHudType();
-        }
-        else {
-            hudType = HudType.DISABLED;
-        }
+        hudType = mcMMO.spoutEnabled ? SpoutConfig.getInstance().getDefaultHudType() : HudType.DISABLED;
+        mobHealthbarType = Config.getInstance().getMobHealthbarDefault();
 
 
         for (AbilityType abilityType : AbilityType.values()) {
         for (AbilityType abilityType : AbilityType.values()) {
-            skillsDATS.put(abilityType, 0);
+            skillsDATS.put(abilityType, 0); 
         }
         }
 
 
         for (SkillType skillType : SkillType.values()) {
         for (SkillType skillType : SkillType.values()) {
-            if (!skillType.isChildSkill()) {
-                skills.put(skillType, 0);
-                skillsXp.put(skillType, 0);
-            }
-        }
-
-        if (Config.getInstance().getUseMySQL()) {
-            if (!loadMySQL() && addNew) {
-                addMySQLPlayer();
-                loaded = true;
-            }
-        }
-        else if (!load() && addNew) {
-            addPlayer();
-            loaded = true;
-        }
-    }
-
-    public String getPlayerName() {
-        return playerName;
-    }
-
-    public boolean loadMySQL() {
-        String tablePrefix = Config.getInstance().getMySQLTablePrefix();
-
-        userId = DatabaseManager.getInt("SELECT id FROM " + tablePrefix + "users WHERE user = '" + playerName + "'");
-
-        if (userId == 0) {
-            return false;
-        }
-
-        HashMap<Integer, ArrayList<String>> huds = DatabaseManager.read("SELECT hudtype FROM " + tablePrefix + "huds WHERE user_id = " + userId);
-
-        if (huds.get(1) == null) {
-            DatabaseManager.write("INSERT INTO " + tablePrefix + "huds (user_id) VALUES (" + userId + ")");
-        }
-        else {
-            hudType = HudType.valueOf(huds.get(1).get(0));
-            mobHealthbarType = MobHealthbarType.valueOf(DatabaseManager.read("SELECT mobhealthbar FROM " + tablePrefix + "huds WHERE user_id = " + userId).get(1).get(0));
-        }
-
-        HashMap<Integer, ArrayList<String>> cooldowns = DatabaseManager.read("SELECT mining, woodcutting, unarmed, herbalism, excavation, swords, axes, blast_mining FROM " + tablePrefix + "cooldowns WHERE user_id = " + userId);
-        ArrayList<String> cooldownValues = cooldowns.get(1);
-
-        if (cooldownValues == null) {
-            DatabaseManager.write("INSERT INTO " + tablePrefix + "cooldowns (user_id) VALUES (" + userId + ")");
-            mcMMO.p.getLogger().warning(playerName + "does not exist in the cooldown table. Their cooldowns will be reset.");
-        }
-        else {
-            skillsDATS.put(AbilityType.SUPER_BREAKER, Integer.valueOf(cooldownValues.get(0)));
-            skillsDATS.put(AbilityType.TREE_FELLER, Integer.valueOf(cooldownValues.get(1)));
-            skillsDATS.put(AbilityType.BERSERK, Integer.valueOf(cooldownValues.get(2)));
-            skillsDATS.put(AbilityType.GREEN_TERRA, Integer.valueOf(cooldownValues.get(3)));
-            skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, Integer.valueOf(cooldownValues.get(4)));
-            skillsDATS.put(AbilityType.SERRATED_STRIKES, Integer.valueOf(cooldownValues.get(5)));
-            skillsDATS.put(AbilityType.SKULL_SPLITTER, Integer.valueOf(cooldownValues.get(6)));
-            skillsDATS.put(AbilityType.BLAST_MINING, Integer.valueOf(cooldownValues.get(7)));
-        }
-
-        HashMap<Integer, ArrayList<String>> stats = DatabaseManager.read("SELECT taming, mining, repair, woodcutting, unarmed, herbalism, excavation, archery, swords, axes, acrobatics, fishing FROM " + tablePrefix + "skills WHERE user_id = " + userId);
-        ArrayList<String> statValues = stats.get(1);
-
-        if (statValues == null) {
-            DatabaseManager.write("INSERT INTO " + tablePrefix + "skills (user_id) VALUES (" + userId + ")");
-            mcMMO.p.getLogger().warning(playerName + "does not exist in the skills table. Their stats will be reset.");
-        }
-        else {
-            skills.put(SkillType.TAMING, Integer.valueOf(statValues.get(0)));
-            skills.put(SkillType.MINING, Integer.valueOf(statValues.get(1)));
-            skills.put(SkillType.REPAIR, Integer.valueOf(statValues.get(2)));
-            skills.put(SkillType.WOODCUTTING, Integer.valueOf(statValues.get(3)));
-            skills.put(SkillType.UNARMED, Integer.valueOf(statValues.get(4)));
-            skills.put(SkillType.HERBALISM, Integer.valueOf(statValues.get(5)));
-            skills.put(SkillType.EXCAVATION, Integer.valueOf(statValues.get(6)));
-            skills.put(SkillType.ARCHERY, Integer.valueOf(statValues.get(7)));
-            skills.put(SkillType.SWORDS, Integer.valueOf(statValues.get(8)));
-            skills.put(SkillType.AXES, Integer.valueOf(statValues.get(9)));
-            skills.put(SkillType.ACROBATICS, Integer.valueOf(statValues.get(10)));
-            skills.put(SkillType.FISHING, Integer.valueOf(statValues.get(11)));
-        }
-
-        HashMap<Integer, ArrayList<String>> experience = DatabaseManager.read("SELECT taming, mining, repair, woodcutting, unarmed, herbalism, excavation, archery, swords, axes, acrobatics, fishing FROM " + tablePrefix + "experience WHERE user_id = " + userId);
-        ArrayList<String> experienceValues = experience.get(1);
-
-        if (experienceValues == null) {
-            DatabaseManager.write("INSERT INTO " + tablePrefix + "experience (user_id) VALUES (" + userId + ")");
-            mcMMO.p.getLogger().warning(playerName + "does not exist in the experience table. Their experience will be reset.");
-        }
-        else {
-            skillsXp.put(SkillType.TAMING, Integer.valueOf(experienceValues.get(0)));
-            skillsXp.put(SkillType.MINING, Integer.valueOf(experienceValues.get(1)));
-            skillsXp.put(SkillType.REPAIR, Integer.valueOf(experienceValues.get(2)));
-            skillsXp.put(SkillType.WOODCUTTING, Integer.valueOf(experienceValues.get(3)));
-            skillsXp.put(SkillType.UNARMED, Integer.valueOf(experienceValues.get(4)));
-            skillsXp.put(SkillType.HERBALISM, Integer.valueOf(experienceValues.get(5)));
-            skillsXp.put(SkillType.EXCAVATION, Integer.valueOf(experienceValues.get(6)));
-            skillsXp.put(SkillType.ARCHERY, Integer.valueOf(experienceValues.get(7)));
-            skillsXp.put(SkillType.SWORDS, Integer.valueOf(experienceValues.get(8)));
-            skillsXp.put(SkillType.AXES, Integer.valueOf(experienceValues.get(9)));
-            skillsXp.put(SkillType.ACROBATICS, Integer.valueOf(experienceValues.get(10)));
-            skillsXp.put(SkillType.FISHING, Integer.valueOf(experienceValues.get(11)));
-        }
-
-        loaded = true;
-        return true;
-    }
-
-    public void addMySQLPlayer() {
-        String tablePrefix = Config.getInstance().getMySQLTablePrefix();
-
-        DatabaseManager.write("INSERT INTO " + tablePrefix + "users (user, lastlogin) VALUES ('" + playerName + "'," + System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR + ")");
-        userId = DatabaseManager.getInt("SELECT id FROM " + tablePrefix + "users WHERE user = '" + playerName + "'");
-
-        DatabaseManager.write("INSERT INTO " + tablePrefix + "cooldowns (user_id) VALUES (" + userId + ")");
-        DatabaseManager.write("INSERT INTO " + tablePrefix + "skills (user_id) VALUES (" + userId + ")");
-        DatabaseManager.write("INSERT INTO " + tablePrefix + "experience (user_id) VALUES (" + userId + ")");
-    }
-
-    public boolean load() {
-        try {
-            // Open the user file
-            FileReader file = new FileReader(location);
-            BufferedReader in = new BufferedReader(file);
-            String line;
-
-            while ((line = in.readLine()) != null) {
-                // Find if the line contains the player we want.
-                String[] character = line.split(":");
-
-                if (!character[0].equalsIgnoreCase(playerName)) {
-                    continue;
-                }
-
-                if (character.length > 1 && StringUtils.isInt(character[1])) {
-                    skills.put(SkillType.MINING, Integer.valueOf(character[1]));
-                }
-
-                if (character.length > 4 && StringUtils.isInt(character[4])) {
-                    skillsXp.put(SkillType.MINING, Integer.valueOf(character[4]));
-                }
-
-                if (character.length > 5 && StringUtils.isInt(character[5])) {
-                    skills.put(SkillType.WOODCUTTING, Integer.valueOf(character[5]));
-                }
-
-                if (character.length > 6 && StringUtils.isInt(character[6])) {
-                    skillsXp.put(SkillType.WOODCUTTING, Integer.valueOf(character[6]));
-                }
-
-                if (character.length > 7 && StringUtils.isInt(character[7])) {
-                    skills.put(SkillType.REPAIR, Integer.valueOf(character[7]));
-                }
-
-                if (character.length > 8 && StringUtils.isInt(character[8])) {
-                    skills.put(SkillType.UNARMED,  Integer.valueOf(character[8]));
-                }
-
-                if (character.length > 9 && StringUtils.isInt(character[9])) {
-                    skills.put(SkillType.HERBALISM, Integer.valueOf(character[9]));
-                }
-
-                if (character.length > 10 && StringUtils.isInt(character[10])) {
-                    skills.put(SkillType.EXCAVATION, Integer.valueOf(character[10]));
-                }
-
-                if (character.length > 11 && StringUtils.isInt(character[11])) {
-                    skills.put(SkillType.ARCHERY, Integer.valueOf(character[11]));
-                }
-
-                if (character.length > 12 && StringUtils.isInt(character[12])) {
-                    skills.put(SkillType.SWORDS, Integer.valueOf(character[12]));
-                }
-
-                if (character.length > 13 && StringUtils.isInt(character[13])) {
-                    skills.put(SkillType.AXES, Integer.valueOf(character[13]));
-                }
-
-                if (character.length > 14 && StringUtils.isInt(character[14])) {
-                    skills.put(SkillType.ACROBATICS, Integer.valueOf(character[14]));
-                }
-
-                if (character.length > 15 && StringUtils.isInt(character[15])) {
-                    skillsXp.put(SkillType.REPAIR, Integer.valueOf(character[15]));
-                }
-
-                if (character.length > 16 && StringUtils.isInt(character[16])) {
-                    skillsXp.put(SkillType.UNARMED, Integer.valueOf(character[16]));
-                }
-
-                if (character.length > 17 && StringUtils.isInt(character[17])) {
-                    skillsXp.put(SkillType.HERBALISM, Integer.valueOf(character[17]));
-                }
-
-                if (character.length > 18 && StringUtils.isInt(character[18])) {
-                    skillsXp.put(SkillType.EXCAVATION, Integer.valueOf(character[18]));
-                }
-
-                if (character.length > 19 && StringUtils.isInt(character[19])) {
-                    skillsXp.put(SkillType.ARCHERY, Integer.valueOf(character[19]));
-                }
-
-                if (character.length > 20 && StringUtils.isInt(character[20])) {
-                    skillsXp.put(SkillType.SWORDS, Integer.valueOf(character[20]));
-                }
-
-                if (character.length > 21 && StringUtils.isInt(character[21])) {
-                    skillsXp.put(SkillType.AXES, Integer.valueOf(character[21]));
-                }
-
-                if (character.length > 22 && StringUtils.isInt(character[22])) {
-                    skillsXp.put(SkillType.ACROBATICS, Integer.valueOf(character[22]));
-                }
-
-                if (character.length > 24 && StringUtils.isInt(character[24])) {
-                    skills.put(SkillType.TAMING, Integer.valueOf(character[24]));
-                }
-
-                if (character.length > 25 && StringUtils.isInt(character[25])) {
-                    skillsXp.put(SkillType.TAMING, Integer.valueOf(character[25]));
-                }
-
-                if (character.length > 26) {
-                    skillsDATS.put(AbilityType.BERSERK, Integer.valueOf(character[26]));
-                }
-
-                if (character.length > 27) {
-                    skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, Integer.valueOf(character[27]));
-                }
-
-                if (character.length > 28) {
-                    skillsDATS.put(AbilityType.TREE_FELLER, Integer.valueOf(character[28]));
-                }
-
-                if (character.length > 29) {
-                    skillsDATS.put(AbilityType.GREEN_TERRA, Integer.valueOf(character[29]));
-                }
-
-                if (character.length > 30) {
-                    skillsDATS.put(AbilityType.SERRATED_STRIKES, Integer.valueOf(character[30]));
-                }
-
-                if (character.length > 31) {
-                    skillsDATS.put(AbilityType.SKULL_SPLITTER, Integer.valueOf(character[31]));
-                }
-
-                if (character.length > 32) {
-                    skillsDATS.put(AbilityType.SUPER_BREAKER, Integer.valueOf(character[32]));
-                }
-
-                if (character.length > 33) {
-                    hudType = HudType.valueOf(character[33]);
-                }
-
-                if (character.length > 34) {
-                    skills.put(SkillType.FISHING, Integer.valueOf(character[34]));
-                }
-
-                if (character.length > 35) {
-                    skillsXp.put(SkillType.FISHING, Integer.valueOf(character[35]));
-                }
-
-                if (character.length > 36) {
-                    skillsDATS.put(AbilityType.BLAST_MINING, Integer.valueOf(character[36]));
-                }
-
-                if (character.length > 38) {
-                    mobHealthbarType = MobHealthbarType.valueOf(character[38]);
-                }
-
-                loaded = true;
-
-                in.close();
-                return true;
+            if (skillType.isChildSkill()) {
+                continue;
             }
             }
 
 
-            in.close();
-        }
-        catch (Exception e) {
-            e.printStackTrace();
-        }
-        return false;
-    }
-
-    public void save() {
-        Long timestamp = System.currentTimeMillis();
-
-        // If we are using mysql save to database
-        if (Config.getInstance().getUseMySQL()) {
-            String tablePrefix = Config.getInstance().getMySQLTablePrefix();
-
-            DatabaseManager.write("UPDATE " + tablePrefix + "huds SET hudtype = '" + hudType.toString() + "' WHERE user_id = " + userId);
-            DatabaseManager.write("UPDATE " + tablePrefix + "huds SET mobhealthbar = '" + mobHealthbarType.toString() + "' WHERE user_id = " + userId);
-            DatabaseManager.write("UPDATE " + tablePrefix + "users SET lastlogin = " + ((int) (timestamp / Misc.TIME_CONVERSION_FACTOR)) + " WHERE id = " + userId);
-            DatabaseManager.write("UPDATE " + tablePrefix + "cooldowns SET "
-                    + " mining = " + skillsDATS.get(AbilityType.SUPER_BREAKER)
-                    + ", woodcutting = " + skillsDATS.get(AbilityType.TREE_FELLER)
-                    + ", unarmed = " + skillsDATS.get(AbilityType.BERSERK)
-                    + ", herbalism = " + skillsDATS.get(AbilityType.GREEN_TERRA)
-                    + ", excavation = " + skillsDATS.get(AbilityType.GIGA_DRILL_BREAKER)
-                    + ", swords = " + skillsDATS.get(AbilityType.SERRATED_STRIKES)
-                    + ", axes = " + skillsDATS.get(AbilityType.SKULL_SPLITTER)
-                    + ", blast_mining = " + skillsDATS.get(AbilityType.BLAST_MINING)
-                    + " WHERE user_id = " + userId);
-            DatabaseManager.write("UPDATE " + tablePrefix + "skills SET "
-                    + " taming = " + skills.get(SkillType.TAMING)
-                    + ", mining = " + skills.get(SkillType.MINING)
-                    + ", repair = " + skills.get(SkillType.REPAIR)
-                    + ", woodcutting = " + skills.get(SkillType.WOODCUTTING)
-                    + ", unarmed = " + skills.get(SkillType.UNARMED)
-                    + ", herbalism = " + skills.get(SkillType.HERBALISM)
-                    + ", excavation = " + skills.get(SkillType.EXCAVATION)
-                    + ", archery = " + skills.get(SkillType.ARCHERY)
-                    + ", swords = " + skills.get(SkillType.SWORDS)
-                    + ", axes = " + skills.get(SkillType.AXES)
-                    + ", acrobatics = " + skills.get(SkillType.ACROBATICS)
-                    + ", fishing = " + skills.get(SkillType.FISHING)
-                    + " WHERE user_id = " + userId);
-            DatabaseManager.write("UPDATE " + tablePrefix + "experience SET "
-                    + "  taming = " + skillsXp.get(SkillType.TAMING)
-                    + ", mining = " + skillsXp.get(SkillType.MINING)
-                    + ", repair = " + skillsXp.get(SkillType.REPAIR)
-                    + ", woodcutting = " + skillsXp.get(SkillType.WOODCUTTING)
-                    + ", unarmed = " + skillsXp.get(SkillType.UNARMED)
-                    + ", herbalism = " + skillsXp.get(SkillType.HERBALISM)
-                    + ", excavation = " + skillsXp.get(SkillType.EXCAVATION)
-                    + ", archery = " + skillsXp.get(SkillType.ARCHERY)
-                    + ", swords = " + skillsXp.get(SkillType.SWORDS)
-                    + ", axes = " + skillsXp.get(SkillType.AXES)
-                    + ", acrobatics = " + skillsXp.get(SkillType.ACROBATICS)
-                    + ", fishing = " + skillsXp.get(SkillType.FISHING)
-                    + " WHERE user_id = " + userId);
-        }
-        else {
-            // Otherwise save to flatfile
-            try {
-                // Open the file
-                FileReader file = new FileReader(location);
-                BufferedReader in = new BufferedReader(file);
-                StringBuilder writer = new StringBuilder();
-                String line;
-
-                // While not at the end of the file
-                while ((line = in.readLine()) != null) {
-                    // Read the line in and copy it to the output it's not the player we want to edit
-                    if (!line.split(":")[0].equalsIgnoreCase(playerName)) {
-                        writer.append(line).append("\r\n");
-                    }
-                    else {
-                        // Otherwise write the new player information
-                        writer.append(playerName).append(":");
-                        writer.append(skills.get(SkillType.MINING)).append(":");
-                        writer.append(":");
-                        writer.append(":");
-                        writer.append(skillsXp.get(SkillType.MINING)).append(":");
-                        writer.append(skills.get(SkillType.WOODCUTTING)).append(":");
-                        writer.append(skillsXp.get(SkillType.WOODCUTTING)).append(":");
-                        writer.append(skills.get(SkillType.REPAIR)).append(":");
-                        writer.append(skills.get(SkillType.UNARMED)).append(":");
-                        writer.append(skills.get(SkillType.HERBALISM)).append(":");
-                        writer.append(skills.get(SkillType.EXCAVATION)).append(":");
-                        writer.append(skills.get(SkillType.ARCHERY)).append(":");
-                        writer.append(skills.get(SkillType.SWORDS)).append(":");
-                        writer.append(skills.get(SkillType.AXES)).append(":");
-                        writer.append(skills.get(SkillType.ACROBATICS)).append(":");
-                        writer.append(skillsXp.get(SkillType.REPAIR)).append(":");
-                        writer.append(skillsXp.get(SkillType.UNARMED)).append(":");
-                        writer.append(skillsXp.get(SkillType.HERBALISM)).append(":");
-                        writer.append(skillsXp.get(SkillType.EXCAVATION)).append(":");
-                        writer.append(skillsXp.get(SkillType.ARCHERY)).append(":");
-                        writer.append(skillsXp.get(SkillType.SWORDS)).append(":");
-                        writer.append(skillsXp.get(SkillType.AXES)).append(":");
-                        writer.append(skillsXp.get(SkillType.ACROBATICS)).append(":");
-                        writer.append(":");
-                        writer.append(skills.get(SkillType.TAMING)).append(":");
-                        writer.append(skillsXp.get(SkillType.TAMING)).append(":");
-                        writer.append(skillsDATS.get(AbilityType.BERSERK)).append(":");
-                        writer.append(skillsDATS.get(AbilityType.GIGA_DRILL_BREAKER)).append(":");
-                        writer.append(skillsDATS.get(AbilityType.TREE_FELLER)).append(":");
-                        writer.append(skillsDATS.get(AbilityType.GREEN_TERRA)).append(":");
-                        writer.append(skillsDATS.get(AbilityType.SERRATED_STRIKES)).append(":");
-                        writer.append(skillsDATS.get(AbilityType.SKULL_SPLITTER)).append(":");
-                        writer.append(skillsDATS.get(AbilityType.SUPER_BREAKER)).append(":");
-                        writer.append(hudType.toString()).append(":");
-                        writer.append(skills.get(SkillType.FISHING)).append(":");
-                        writer.append(skillsXp.get(SkillType.FISHING)).append(":");
-                        writer.append(skillsDATS.get(AbilityType.BLAST_MINING)).append(":");
-                        writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
-                        writer.append(mobHealthbarType.toString()).append(":");
-                        writer.append("\r\n");
-                    }
-                }
-
-                in.close();
-
-                // Write the new file
-                FileWriter out = new FileWriter(location);
-                out.write(writer.toString());
-                out.flush();
-                out.close();
-            }
-            catch (Exception e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    public void addPlayer() {
-        try {
-            // Open the file to write the player
-            FileWriter file = new FileWriter(location, true);
-            BufferedWriter out = new BufferedWriter(file);
-
-            // Add the player to the end
-            out.append(playerName).append(":");
-            out.append("0:"); // Mining
-            out.append(":");
-            out.append(":");
-            out.append("0:"); // Xp
-            out.append("0:"); // Woodcutting
-            out.append("0:"); // WoodCuttingXp
-            out.append("0:"); // Repair
-            out.append("0:"); // Unarmed
-            out.append("0:"); // Herbalism
-            out.append("0:"); // Excavation
-            out.append("0:"); // Archery
-            out.append("0:"); // Swords
-            out.append("0:"); // Axes
-            out.append("0:"); // Acrobatics
-            out.append("0:"); // RepairXp
-            out.append("0:"); // UnarmedXp
-            out.append("0:"); // HerbalismXp
-            out.append("0:"); // ExcavationXp
-            out.append("0:"); // ArcheryXp
-            out.append("0:"); // SwordsXp
-            out.append("0:"); // AxesXp
-            out.append("0:"); // AcrobaticsXp
-            out.append(":");
-            out.append("0:"); // Taming
-            out.append("0:"); // TamingXp
-            out.append("0:"); // DATS
-            out.append("0:"); // DATS
-            out.append("0:"); // DATS
-            out.append("0:"); // DATS
-            out.append("0:"); // DATS
-            out.append("0:"); // DATS
-            out.append("0:"); // DATS
-            out.append(hudType.toString()).append(":"); // HUD
-            out.append("0:"); // Fishing
-            out.append("0:"); // FishingXp
-            out.append("0:"); // Blast Mining
-            out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
-            out.append(mobHealthbarType.toString()).append(":"); // Mob Healthbar HUD
-
-            // Add more in the same format as the line above
-
-            out.newLine();
-            out.close();
-        }
-        catch (Exception e) {
-            e.printStackTrace();
+            skills.put(skillType, 0);
+            skillsXp.put(skillType, 0);
         }
         }
-    }
 
 
-    /*
-     * mySQL Stuff
-     */
+        if (!loadPlayer() && addNew) {
+            addPlayer();
+            loaded = true;
+        }
+    }
 
 
-    public int getMySQLuserId() {
-        return userId;
+    public void save() {
+        if (Config.getInstance().getUseMySQL()) {
+            saveMySQL();
+        }
+        else {
+            saveFlatfile();
+        }
     }
     }
 
 
     public boolean isLoaded() {
     public boolean isLoaded() {
@@ -594,8 +136,8 @@ public class PlayerProfile {
      * Reset all skill cooldowns.
      * Reset all skill cooldowns.
      */
      */
     public void resetCooldowns() {
     public void resetCooldowns() {
-        for (AbilityType x : skillsDATS.keySet()) {
-            skillsDATS.put(x, 0);
+        for (AbilityType ability : skillsDATS.keySet()) {
+            skillsDATS.put(ability, 0);
         }
         }
     }
     }
 
 
@@ -611,17 +153,6 @@ public class PlayerProfile {
         return skills.get(skillType);
         return skills.get(skillType);
     }
     }
 
 
-    public int getChildSkillLevel(SkillType skillType) {
-        Set<SkillType> parents = FamilyTree.getParents(skillType);
-        int sum = 0;
-
-        for (SkillType parent : parents) {
-            sum += Math.min(getSkillLevel(parent), 1000);
-        }
-
-        return sum / parents.size();
-    }
-
     public int getSkillXpLevel(SkillType skillType) {
     public int getSkillXpLevel(SkillType skillType) {
         return skillsXp.get(skillType);
         return skillsXp.get(skillType);
     }
     }
@@ -705,4 +236,384 @@ public class PlayerProfile {
     public int getXpToLevel(SkillType skillType) {
     public int getXpToLevel(SkillType skillType) {
         return 1020 + (skills.get(skillType) *  Config.getInstance().getFormulaMultiplierCurve());
         return 1020 + (skills.get(skillType) *  Config.getInstance().getFormulaMultiplierCurve());
     }
     }
+
+    private int getChildSkillLevel(SkillType skillType) {
+        Set<SkillType> parents = FamilyTree.getParents(skillType);
+        int sum = 0;
+
+        for (SkillType parent : parents) {
+            sum += Math.min(getSkillLevel(parent), 1000);
+        }
+
+        return sum / parents.size();
+    }
+
+    private boolean loadPlayer() {
+        return Config.getInstance().getUseMySQL() ? loadMySQL() : loadFlatfile();
+    }
+
+    private void addPlayer() {
+        if (Config.getInstance().getUseMySQL()) {
+            addMySQLPlayer();
+        }
+        else {
+            addFlatfilePlayer();
+        }
+    }
+
+    private boolean loadMySQL() {
+        String tablePrefix = Config.getInstance().getMySQLTablePrefix();
+
+        userId = SQLDatabaseManager.getInt("SELECT id FROM " + tablePrefix + "users WHERE user = '" + playerName + "'");
+
+        if (userId == 0) {
+            return false;
+        }
+
+        ArrayList<String> hudValues = SQLDatabaseManager.read("SELECT hudtype, mobhealthbar FROM " + tablePrefix + "huds WHERE user_id = " + userId).get(1);
+
+        if (hudValues == null) {
+            SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "huds (user_id, mobhealthbar) VALUES (" + userId + "," + mobHealthbarType.name() + ")");
+            mcMMO.p.getLogger().warning(playerName + "does not exist in the HUD table. Their HUDs will be reset.");
+        }
+        else {
+            hudType = HudType.valueOf(hudValues.get(0));
+            mobHealthbarType = MobHealthbarType.valueOf(hudValues.get(1));
+        }
+
+        ArrayList<String> cooldownValues = SQLDatabaseManager.read("SELECT mining, woodcutting, unarmed, herbalism, excavation, swords, axes, blast_mining FROM " + tablePrefix + "cooldowns WHERE user_id = " + userId).get(1);
+
+        if (cooldownValues == null) {
+            SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "cooldowns (user_id) VALUES (" + userId + ")");
+            mcMMO.p.getLogger().warning(playerName + "does not exist in the cooldown table. Their cooldowns will be reset.");
+        }
+        else {
+            skillsDATS.put(AbilityType.SUPER_BREAKER, Integer.valueOf(cooldownValues.get(0)));
+            skillsDATS.put(AbilityType.TREE_FELLER, Integer.valueOf(cooldownValues.get(1)));
+            skillsDATS.put(AbilityType.BERSERK, Integer.valueOf(cooldownValues.get(2)));
+            skillsDATS.put(AbilityType.GREEN_TERRA, Integer.valueOf(cooldownValues.get(3)));
+            skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, Integer.valueOf(cooldownValues.get(4)));
+            skillsDATS.put(AbilityType.SERRATED_STRIKES, Integer.valueOf(cooldownValues.get(5)));
+            skillsDATS.put(AbilityType.SKULL_SPLITTER, Integer.valueOf(cooldownValues.get(6)));
+            skillsDATS.put(AbilityType.BLAST_MINING, Integer.valueOf(cooldownValues.get(7)));
+        }
+
+        ArrayList<String> statValues = SQLDatabaseManager.read("SELECT taming, mining, repair, woodcutting, unarmed, herbalism, excavation, archery, swords, axes, acrobatics, fishing FROM " + tablePrefix + "skills WHERE user_id = " + userId).get(1);
+
+        if (statValues == null) {
+            SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "skills (user_id) VALUES (" + userId + ")");
+            mcMMO.p.getLogger().warning(playerName + "does not exist in the skills table. Their stats will be reset.");
+        }
+        else {
+            skills.put(SkillType.TAMING, Integer.valueOf(statValues.get(0)));
+            skills.put(SkillType.MINING, Integer.valueOf(statValues.get(1)));
+            skills.put(SkillType.REPAIR, Integer.valueOf(statValues.get(2)));
+            skills.put(SkillType.WOODCUTTING, Integer.valueOf(statValues.get(3)));
+            skills.put(SkillType.UNARMED, Integer.valueOf(statValues.get(4)));
+            skills.put(SkillType.HERBALISM, Integer.valueOf(statValues.get(5)));
+            skills.put(SkillType.EXCAVATION, Integer.valueOf(statValues.get(6)));
+            skills.put(SkillType.ARCHERY, Integer.valueOf(statValues.get(7)));
+            skills.put(SkillType.SWORDS, Integer.valueOf(statValues.get(8)));
+            skills.put(SkillType.AXES, Integer.valueOf(statValues.get(9)));
+            skills.put(SkillType.ACROBATICS, Integer.valueOf(statValues.get(10)));
+            skills.put(SkillType.FISHING, Integer.valueOf(statValues.get(11)));
+        }
+
+        ArrayList<String> experienceValues = SQLDatabaseManager.read("SELECT taming, mining, repair, woodcutting, unarmed, herbalism, excavation, archery, swords, axes, acrobatics, fishing FROM " + tablePrefix + "experience WHERE user_id = " + userId).get(1);
+
+        if (experienceValues == null) {
+            SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "experience (user_id) VALUES (" + userId + ")");
+            mcMMO.p.getLogger().warning(playerName + "does not exist in the experience table. Their experience will be reset.");
+        }
+        else {
+            skillsXp.put(SkillType.TAMING, Integer.valueOf(experienceValues.get(0)));
+            skillsXp.put(SkillType.MINING, Integer.valueOf(experienceValues.get(1)));
+            skillsXp.put(SkillType.REPAIR, Integer.valueOf(experienceValues.get(2)));
+            skillsXp.put(SkillType.WOODCUTTING, Integer.valueOf(experienceValues.get(3)));
+            skillsXp.put(SkillType.UNARMED, Integer.valueOf(experienceValues.get(4)));
+            skillsXp.put(SkillType.HERBALISM, Integer.valueOf(experienceValues.get(5)));
+            skillsXp.put(SkillType.EXCAVATION, Integer.valueOf(experienceValues.get(6)));
+            skillsXp.put(SkillType.ARCHERY, Integer.valueOf(experienceValues.get(7)));
+            skillsXp.put(SkillType.SWORDS, Integer.valueOf(experienceValues.get(8)));
+            skillsXp.put(SkillType.AXES, Integer.valueOf(experienceValues.get(9)));
+            skillsXp.put(SkillType.ACROBATICS, Integer.valueOf(experienceValues.get(10)));
+            skillsXp.put(SkillType.FISHING, Integer.valueOf(experienceValues.get(11)));
+        }
+
+        loaded = true;
+        return true;
+    }
+
+    private void addMySQLPlayer() {
+        String tablePrefix = Config.getInstance().getMySQLTablePrefix();
+
+        SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "users (user, lastlogin) VALUES ('" + playerName + "'," + System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR + ")");
+        userId = SQLDatabaseManager.getInt("SELECT id FROM " + tablePrefix + "users WHERE user = '" + playerName + "'");
+
+        SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "huds (user_id, mobhealthbar) VALUES (" + userId + "," + mobHealthbarType.name() + ")");
+        SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "cooldowns (user_id) VALUES (" + userId + ")");
+        SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "skills (user_id) VALUES (" + userId + ")");
+        SQLDatabaseManager.write("INSERT INTO " + tablePrefix + "experience (user_id) VALUES (" + userId + ")");
+    }
+
+
+    private void addFlatfilePlayer() {
+        try {
+            // Open the file to write the player
+            BufferedWriter out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true));
+
+            // Add the player to the end
+            out.append(playerName).append(":");
+            out.append("0:"); // Mining
+            out.append(":");
+            out.append(":");
+            out.append("0:"); // Xp
+            out.append("0:"); // Woodcutting
+            out.append("0:"); // WoodCuttingXp
+            out.append("0:"); // Repair
+            out.append("0:"); // Unarmed
+            out.append("0:"); // Herbalism
+            out.append("0:"); // Excavation
+            out.append("0:"); // Archery
+            out.append("0:"); // Swords
+            out.append("0:"); // Axes
+            out.append("0:"); // Acrobatics
+            out.append("0:"); // RepairXp
+            out.append("0:"); // UnarmedXp
+            out.append("0:"); // HerbalismXp
+            out.append("0:"); // ExcavationXp
+            out.append("0:"); // ArcheryXp
+            out.append("0:"); // SwordsXp
+            out.append("0:"); // AxesXp
+            out.append("0:"); // AcrobaticsXp
+            out.append(":");
+            out.append("0:"); // Taming
+            out.append("0:"); // TamingXp
+            out.append("0:"); // DATS
+            out.append("0:"); // DATS
+            out.append("0:"); // DATS
+            out.append("0:"); // DATS
+            out.append("0:"); // DATS
+            out.append("0:"); // DATS
+            out.append("0:"); // DATS
+            out.append(hudType.toString()).append(":"); // HUD
+            out.append("0:"); // Fishing
+            out.append("0:"); // FishingXp
+            out.append("0:"); // Blast Mining
+            out.append(String.valueOf(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)).append(":"); // LastLogin
+            out.append(mobHealthbarType.toString()).append(":"); // Mob Healthbar HUD
+
+            // Add more in the same format as the line above
+
+            out.newLine();
+            out.close();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private boolean loadFlatfile() {
+        try {
+            // Open the user file
+            FileReader file = new FileReader(mcMMO.getUsersFilePath());
+            BufferedReader in = new BufferedReader(file);
+            String line;
+
+            while ((line = in.readLine()) != null) {
+                // Find if the line contains the player we want.
+                String[] character = line.split(":");
+
+                if (!character[0].equalsIgnoreCase(playerName)) {
+                    continue;
+                }
+
+                loadSkillData(SkillType.MINING, character, 1);
+                loadSkillData(SkillType.WOODCUTTING, character, 5);
+                loadSkillData(SkillType.REPAIR, character, 7);
+                loadSkillData(SkillType.UNARMED, character, 8);
+                loadSkillData(SkillType.HERBALISM, character, 9);
+                loadSkillData(SkillType.EXCAVATION, character, 10);
+                loadSkillData(SkillType.ARCHERY, character, 11);
+                loadSkillData(SkillType.SWORDS, character, 12);
+                loadSkillData(SkillType.AXES, character, 13);
+                loadSkillData(SkillType.ACROBATICS, character, 14);
+                loadSkillData(SkillType.TAMING, character, 24);
+                loadSkillData(SkillType.FISHING, character, 34);
+
+                loadSkillXpData(SkillType.MINING, character, 4);
+                loadSkillXpData(SkillType.WOODCUTTING, character, 6);
+                loadSkillXpData(SkillType.REPAIR, character, 15);
+                loadSkillXpData(SkillType.UNARMED, character, 16);
+                loadSkillXpData(SkillType.HERBALISM, character, 17);
+                loadSkillXpData(SkillType.EXCAVATION, character, 18);
+                loadSkillXpData(SkillType.ARCHERY, character, 19);
+                loadSkillXpData(SkillType.SWORDS, character, 20);
+                loadSkillXpData(SkillType.AXES, character, 21);
+                loadSkillXpData(SkillType.ACROBATICS, character, 22);
+                loadSkillXpData(SkillType.TAMING, character, 25);
+                loadSkillXpData(SkillType.FISHING, character, 35);
+
+                loadDATSData(AbilityType.BERSERK, character, 26);
+                loadDATSData(AbilityType.GIGA_DRILL_BREAKER, character, 27);
+                loadDATSData(AbilityType.TREE_FELLER, character, 28);
+                loadDATSData(AbilityType.GREEN_TERRA, character, 29);
+                loadDATSData(AbilityType.SERRATED_STRIKES, character, 30);
+                loadDATSData(AbilityType.SKULL_SPLITTER, character, 31);
+                loadDATSData(AbilityType.SUPER_BREAKER, character, 32);
+                loadDATSData(AbilityType.BLAST_MINING, character, 36);
+
+                hudType = character.length > 33 ? HudType.valueOf(character[33]) : null;
+                mobHealthbarType = character.length > 38 ? MobHealthbarType.valueOf(character[38]) : null;
+
+                loaded = true;
+            }
+
+            in.close();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return loaded;
+    }
+
+    private void saveMySQL() {
+        if (Config.getInstance().getUseMySQL()) {
+            String tablePrefix = Config.getInstance().getMySQLTablePrefix();
+
+            SQLDatabaseManager.write("UPDATE " + tablePrefix + "users SET lastlogin = " + ((int) (System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR)) + " WHERE id = " + userId);
+            SQLDatabaseManager.write("UPDATE " + tablePrefix + "huds SET "
+                    + "  hudtype = " + hudType.toString()
+                    + ", mobhealthbar = " + mobHealthbarType.toString()
+                    + "  WHERE user_id = " + userId);
+            SQLDatabaseManager.write("UPDATE " + tablePrefix + "cooldowns SET "
+                    + "  mining = " + skillsDATS.get(AbilityType.SUPER_BREAKER)
+                    + ", woodcutting = " + skillsDATS.get(AbilityType.TREE_FELLER)
+                    + ", unarmed = " + skillsDATS.get(AbilityType.BERSERK)
+                    + ", herbalism = " + skillsDATS.get(AbilityType.GREEN_TERRA)
+                    + ", excavation = " + skillsDATS.get(AbilityType.GIGA_DRILL_BREAKER)
+                    + ", swords = " + skillsDATS.get(AbilityType.SERRATED_STRIKES)
+                    + ", axes = " + skillsDATS.get(AbilityType.SKULL_SPLITTER)
+                    + ", blast_mining = " + skillsDATS.get(AbilityType.BLAST_MINING)
+                    + "  WHERE user_id = " + userId);
+            SQLDatabaseManager.write("UPDATE " + tablePrefix + "skills SET "
+                    + "  taming = " + skills.get(SkillType.TAMING)
+                    + ", mining = " + skills.get(SkillType.MINING)
+                    + ", repair = " + skills.get(SkillType.REPAIR)
+                    + ", woodcutting = " + skills.get(SkillType.WOODCUTTING)
+                    + ", unarmed = " + skills.get(SkillType.UNARMED)
+                    + ", herbalism = " + skills.get(SkillType.HERBALISM)
+                    + ", excavation = " + skills.get(SkillType.EXCAVATION)
+                    + ", archery = " + skills.get(SkillType.ARCHERY)
+                    + ", swords = " + skills.get(SkillType.SWORDS)
+                    + ", axes = " + skills.get(SkillType.AXES)
+                    + ", acrobatics = " + skills.get(SkillType.ACROBATICS)
+                    + ", fishing = " + skills.get(SkillType.FISHING)
+                    + "  WHERE user_id = " + userId);
+            SQLDatabaseManager.write("UPDATE " + tablePrefix + "experience SET "
+                    + "  taming = " + skillsXp.get(SkillType.TAMING)
+                    + ", mining = " + skillsXp.get(SkillType.MINING)
+                    + ", repair = " + skillsXp.get(SkillType.REPAIR)
+                    + ", woodcutting = " + skillsXp.get(SkillType.WOODCUTTING)
+                    + ", unarmed = " + skillsXp.get(SkillType.UNARMED)
+                    + ", herbalism = " + skillsXp.get(SkillType.HERBALISM)
+                    + ", excavation = " + skillsXp.get(SkillType.EXCAVATION)
+                    + ", archery = " + skillsXp.get(SkillType.ARCHERY)
+                    + ", swords = " + skillsXp.get(SkillType.SWORDS)
+                    + ", axes = " + skillsXp.get(SkillType.AXES)
+                    + ", acrobatics = " + skillsXp.get(SkillType.ACROBATICS)
+                    + ", fishing = " + skillsXp.get(SkillType.FISHING)
+                    + "  WHERE user_id = " + userId);
+        }
+    }
+
+    private void saveFlatfile() {
+        try {
+            // Open the file
+            BufferedReader in = new BufferedReader(new FileReader(mcMMO.getUsersFilePath()));
+            StringBuilder writer = new StringBuilder();
+            String line;
+
+            // While not at the end of the file
+            while ((line = in.readLine()) != null) {
+                // Read the line in and copy it to the output it's not the player we want to edit
+                if (!line.split(":")[0].equalsIgnoreCase(playerName)) {
+                    writer.append(line).append("\r\n");
+                }
+                else {
+                    // Otherwise write the new player information
+                    writer.append(playerName).append(":");
+                    writer.append(skills.get(SkillType.MINING)).append(":");
+                    writer.append(":");
+                    writer.append(":");
+                    writer.append(skillsXp.get(SkillType.MINING)).append(":");
+                    writer.append(skills.get(SkillType.WOODCUTTING)).append(":");
+                    writer.append(skillsXp.get(SkillType.WOODCUTTING)).append(":");
+                    writer.append(skills.get(SkillType.REPAIR)).append(":");
+                    writer.append(skills.get(SkillType.UNARMED)).append(":");
+                    writer.append(skills.get(SkillType.HERBALISM)).append(":");
+                    writer.append(skills.get(SkillType.EXCAVATION)).append(":");
+                    writer.append(skills.get(SkillType.ARCHERY)).append(":");
+                    writer.append(skills.get(SkillType.SWORDS)).append(":");
+                    writer.append(skills.get(SkillType.AXES)).append(":");
+                    writer.append(skills.get(SkillType.ACROBATICS)).append(":");
+                    writer.append(skillsXp.get(SkillType.REPAIR)).append(":");
+                    writer.append(skillsXp.get(SkillType.UNARMED)).append(":");
+                    writer.append(skillsXp.get(SkillType.HERBALISM)).append(":");
+                    writer.append(skillsXp.get(SkillType.EXCAVATION)).append(":");
+                    writer.append(skillsXp.get(SkillType.ARCHERY)).append(":");
+                    writer.append(skillsXp.get(SkillType.SWORDS)).append(":");
+                    writer.append(skillsXp.get(SkillType.AXES)).append(":");
+                    writer.append(skillsXp.get(SkillType.ACROBATICS)).append(":");
+                    writer.append(":");
+                    writer.append(skills.get(SkillType.TAMING)).append(":");
+                    writer.append(skillsXp.get(SkillType.TAMING)).append(":");
+                    writer.append(skillsDATS.get(AbilityType.BERSERK)).append(":");
+                    writer.append(skillsDATS.get(AbilityType.GIGA_DRILL_BREAKER)).append(":");
+                    writer.append(skillsDATS.get(AbilityType.TREE_FELLER)).append(":");
+                    writer.append(skillsDATS.get(AbilityType.GREEN_TERRA)).append(":");
+                    writer.append(skillsDATS.get(AbilityType.SERRATED_STRIKES)).append(":");
+                    writer.append(skillsDATS.get(AbilityType.SKULL_SPLITTER)).append(":");
+                    writer.append(skillsDATS.get(AbilityType.SUPER_BREAKER)).append(":");
+                    writer.append(hudType.toString()).append(":");
+                    writer.append(skills.get(SkillType.FISHING)).append(":");
+                    writer.append(skillsXp.get(SkillType.FISHING)).append(":");
+                    writer.append(skillsDATS.get(AbilityType.BLAST_MINING)).append(":");
+                    writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":");
+                    writer.append(mobHealthbarType.toString()).append(":");
+                    writer.append("\r\n");
+                }
+            }
+
+            in.close();
+
+            // Write the new file
+            FileWriter out = new FileWriter(mcMMO.getUsersFilePath());
+            out.write(writer.toString());
+            out.flush();
+            out.close();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void loadSkillXpData(SkillType skill, String[] data, int dataIndex) {
+        if (data.length > dataIndex) {
+            skillsXp.put(skill, Integer.valueOf(data[dataIndex]));
+        }
+    }
+
+    private void loadSkillData(SkillType skill, String[] data, int dataIndex) {
+        if (data.length > dataIndex) {
+            skills.put(skill, Integer.valueOf(data[dataIndex]));
+        }
+    }
+
+    private void loadDATSData(AbilityType ability, String[] data, int dataIndex) {
+        if (data.length > dataIndex) {
+            skillsDATS.put(ability, Integer.valueOf(data[dataIndex]));
+        }
+    }
 }
 }

+ 3 - 15
src/main/java/com/gmail/nossr50/mcMMO.java

@@ -23,7 +23,6 @@ import com.gmail.nossr50.config.mods.CustomToolConfig;
 import com.gmail.nossr50.config.spout.SpoutConfig;
 import com.gmail.nossr50.config.spout.SpoutConfig;
 import com.gmail.nossr50.config.treasure.TreasureConfig;
 import com.gmail.nossr50.config.treasure.TreasureConfig;
 import com.gmail.nossr50.database.DatabaseManager;
 import com.gmail.nossr50.database.DatabaseManager;
-import com.gmail.nossr50.database.LeaderboardManager;
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
 import com.gmail.nossr50.datatypes.skills.AbilityType;
 import com.gmail.nossr50.datatypes.skills.AbilityType;
 import com.gmail.nossr50.listeners.BlockListener;
 import com.gmail.nossr50.listeners.BlockListener;
@@ -65,8 +64,9 @@ public class mcMMO extends JavaPlugin {
 
 
     public static mcMMO p;
     public static mcMMO p;
 
 
-    public static ChunkManager  placeStore;
+    public static ChunkManager placeStore;
     public static RepairableManager repairableManager;
     public static RepairableManager repairableManager;
+    public static DatabaseManager databaseManager;
 
 
     // Jar Stuff
     // Jar Stuff
     public static File mcmmo;
     public static File mcmmo;
@@ -111,25 +111,13 @@ public class mcMMO extends JavaPlugin {
             setupSpout();
             setupSpout();
             loadConfigFiles();
             loadConfigFiles();
 
 
-            if (!Config.getInstance().getUseMySQL()) {
-                UserManager.loadUsers();
-            }
+            databaseManager = new DatabaseManager(this, Config.getInstance().getUseMySQL());
 
 
             registerEvents();
             registerEvents();
             registerCustomRecipes();
             registerCustomRecipes();
 
 
             PartyManager.loadParties();
             PartyManager.loadParties();
 
 
-            // Setup the leader boards
-            if (Config.getInstance().getUseMySQL()) {
-                // TODO: Why do we have to check for a connection that hasn't be made yet?
-                DatabaseManager.checkConnected();
-                DatabaseManager.createStructure();
-            }
-            else {
-                LeaderboardManager.updateLeaderboards();
-            }
-
             for (Player player : getServer().getOnlinePlayers()) {
             for (Player player : getServer().getOnlinePlayers()) {
                 UserManager.addUser(player); // In case of reload add all users back into UserManager
                 UserManager.addUser(player); // In case of reload add all users back into UserManager
             }
             }

+ 4 - 2
src/main/java/com/gmail/nossr50/runnables/commands/McrankCommandAsyncTask.java

@@ -6,7 +6,9 @@ import org.bukkit.command.CommandSender;
 import org.bukkit.scheduler.BukkitRunnable;
 import org.bukkit.scheduler.BukkitRunnable;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
-import com.gmail.nossr50.database.DatabaseManager;
+import com.gmail.nossr50.config.Config;
+import com.gmail.nossr50.database.SQLDatabaseManager;
+import com.gmail.nossr50.database.FlatfileDatabaseManager;
 
 
 public class McrankCommandAsyncTask extends BukkitRunnable {
 public class McrankCommandAsyncTask extends BukkitRunnable {
     private final String playerName;
     private final String playerName;
@@ -19,7 +21,7 @@ public class McrankCommandAsyncTask extends BukkitRunnable {
 
 
     @Override
     @Override
     public void run() {
     public void run() {
-        Map<String, Integer> skills = DatabaseManager.readSQLRank(playerName);
+        Map<String, Integer> skills = Config.getInstance().getUseMySQL() ? SQLDatabaseManager.readSQLRank(playerName) : FlatfileDatabaseManager.getPlayerRanks(playerName);
 
 
         new McrankCommandDisplayTask(skills, sender, playerName).runTaskLater(mcMMO.p, 1);
         new McrankCommandDisplayTask(skills, sender, playerName).runTaskLater(mcMMO.p, 1);
     }
     }

+ 10 - 14
src/main/java/com/gmail/nossr50/runnables/commands/McrankCommandDisplayTask.java

@@ -6,6 +6,7 @@ import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Player;
 import org.bukkit.scheduler.BukkitRunnable;
 import org.bukkit.scheduler.BukkitRunnable;
 
 
+import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.datatypes.skills.SkillType;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.locale.LocaleLoader;
 import com.gmail.nossr50.util.Permissions;
 import com.gmail.nossr50.util.Permissions;
@@ -24,27 +25,22 @@ public class McrankCommandDisplayTask extends BukkitRunnable {
 
 
     @Override
     @Override
     public void run() {
     public void run() {
+        Player player = mcMMO.p.getServer().getPlayer(playerName);
+        Integer rank;
+
         sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Heading"));
         sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Heading"));
         sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Player", playerName));
         sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Player", playerName));
 
 
-        for (SkillType skillType : SkillType.values()) {
-            if ((sender instanceof Player && !Permissions.skillEnabled(sender, skillType)) || skillType.isChildSkill()) {
+        for (SkillType skill : SkillType.values()) {
+            if (skill.isChildSkill() || !Permissions.skillEnabled(player, skill)) {
                 continue;
                 continue;
             }
             }
 
 
-            if (skills.get(skillType.name()) == null) {
-                sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", SkillUtils.getSkillName(skillType), LocaleLoader.getString("Commands.mcrank.Unranked")));
-            }
-            else {
-                sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", SkillUtils.getSkillName(skillType), skills.get(skillType.name())));
-            }
+            rank = skills.get(skill.name());
+            sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Skill", SkillUtils.getSkillName(skill), (rank == null ? LocaleLoader.getString("Commands.mcrank.Unranked") : rank)));
         }
         }
 
 
-        if (skills.get("ALL") == null) {
-            sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Overall", LocaleLoader.getString("Commands.mcrank.Unranked")));
-        }
-        else {
-            sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Overall", skills.get("ALL")));
-        }
+        rank = skills.get("ALL");
+        sender.sendMessage(LocaleLoader.getString("Commands.mcrank.Overall", (rank == null ? LocaleLoader.getString("Commands.mcrank.Unranked") : rank)));
     }
     }
 }
 }

+ 3 - 3
src/main/java/com/gmail/nossr50/runnables/commands/MctopCommandAsyncTask.java

@@ -8,7 +8,7 @@ import org.bukkit.scheduler.BukkitRunnable;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.database.DatabaseManager;
+import com.gmail.nossr50.database.SQLDatabaseManager;
 
 
 public class MctopCommandAsyncTask extends BukkitRunnable {
 public class MctopCommandAsyncTask extends BukkitRunnable {
 
 
@@ -18,14 +18,14 @@ public class MctopCommandAsyncTask extends BukkitRunnable {
 
 
     public MctopCommandAsyncTask(int page, String query, CommandSender sender) {
     public MctopCommandAsyncTask(int page, String query, CommandSender sender) {
         this.page = page;
         this.page = page;
-        this.query = query;
+        this.query = query.equalsIgnoreCase("ALL") ? "taming+mining+woodcutting+repair+unarmed+herbalism+excavation+archery+swords+axes+acrobatics+fishing" : query;
         this.sender = sender;
         this.sender = sender;
     }
     }
 
 
     @Override
     @Override
     public void run() {
     public void run() {
         String tablePrefix = Config.getInstance().getMySQLTablePrefix();
         String tablePrefix = Config.getInstance().getMySQLTablePrefix();
-        final HashMap<Integer, ArrayList<String>> userslist = DatabaseManager.read("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT " + ((page * 10) - 10) + ",10");
+        final HashMap<Integer, ArrayList<String>> userslist = SQLDatabaseManager.read("SELECT " + query + ", user, NOW() FROM " + tablePrefix + "users JOIN " + tablePrefix + "skills ON (user_id = id) WHERE " + query + " > 0 ORDER BY " + query + " DESC, user LIMIT " + ((page * 10) - 10) + ",10");
 
 
         new MctopCommandDisplayTask(userslist, page, tablePrefix, sender).runTaskLater(mcMMO.p, 1);
         new MctopCommandDisplayTask(userslist, page, tablePrefix, sender).runTaskLater(mcMMO.p, 1);
     }
     }

+ 12 - 12
src/main/java/com/gmail/nossr50/runnables/database/SQLConversionTask.java

@@ -7,7 +7,7 @@ import org.bukkit.scheduler.BukkitRunnable;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.database.DatabaseManager;
+import com.gmail.nossr50.database.SQLDatabaseManager;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.Misc;
 import com.gmail.nossr50.util.StringUtils;
 import com.gmail.nossr50.util.StringUtils;
 
 
@@ -158,7 +158,7 @@ public class SQLConversionTask extends BukkitRunnable {
                 }
                 }
 
 
                 // Check to see if the user is in the DB
                 // Check to see if the user is in the DB
-                id = DatabaseManager.getInt("SELECT id FROM "
+                id = SQLDatabaseManager.getInt("SELECT id FROM "
                         + tablePrefix
                         + tablePrefix
                         + "users WHERE user = '" + playerName + "'");
                         + "users WHERE user = '" + playerName + "'");
 
 
@@ -166,11 +166,11 @@ public class SQLConversionTask extends BukkitRunnable {
                     theCount++;
                     theCount++;
 
 
                     // Update the skill values
                     // Update the skill values
-                    DatabaseManager.write("UPDATE "
+                    SQLDatabaseManager.write("UPDATE "
                             + tablePrefix
                             + tablePrefix
                             + "users SET lastlogin = " + 0
                             + "users SET lastlogin = " + 0
                             + " WHERE id = " + id);
                             + " WHERE id = " + id);
-                    DatabaseManager.write("UPDATE "
+                    SQLDatabaseManager.write("UPDATE "
                             + tablePrefix
                             + tablePrefix
                             + "skills SET "
                             + "skills SET "
                             + "  taming = taming+" + StringUtils.getInt(taming)
                             + "  taming = taming+" + StringUtils.getInt(taming)
@@ -186,7 +186,7 @@ public class SQLConversionTask extends BukkitRunnable {
                             + ", acrobatics = acrobatics+" + StringUtils.getInt(acrobatics)
                             + ", acrobatics = acrobatics+" + StringUtils.getInt(acrobatics)
                             + ", fishing = fishing+" + StringUtils.getInt(fishing)
                             + ", fishing = fishing+" + StringUtils.getInt(fishing)
                             + " WHERE user_id = " + id);
                             + " WHERE user_id = " + id);
-                    DatabaseManager.write("UPDATE "
+                    SQLDatabaseManager.write("UPDATE "
                             + tablePrefix
                             + tablePrefix
                             + "experience SET "
                             + "experience SET "
                             + "  taming = " + StringUtils.getInt(tamingXP)
                             + "  taming = " + StringUtils.getInt(tamingXP)
@@ -207,24 +207,24 @@ public class SQLConversionTask extends BukkitRunnable {
                     theCount++;
                     theCount++;
 
 
                     // Create the user in the DB
                     // Create the user in the DB
-                    DatabaseManager.write("INSERT INTO "
+                    SQLDatabaseManager.write("INSERT INTO "
                             + tablePrefix
                             + tablePrefix
                             + "users (user, lastlogin) VALUES ('"
                             + "users (user, lastlogin) VALUES ('"
                             + playerName + "',"
                             + playerName + "',"
                             + System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR + ")");
                             + System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR + ")");
-                    id = DatabaseManager.getInt("SELECT id FROM "
+                    id = SQLDatabaseManager.getInt("SELECT id FROM "
                             + tablePrefix
                             + tablePrefix
                             + "users WHERE user = '"
                             + "users WHERE user = '"
                             + playerName + "'");
                             + playerName + "'");
-                    DatabaseManager.write("INSERT INTO "
+                    SQLDatabaseManager.write("INSERT INTO "
                             + tablePrefix
                             + tablePrefix
                             + "skills (user_id) VALUES (" + id + ")");
                             + "skills (user_id) VALUES (" + id + ")");
-                    DatabaseManager.write("INSERT INTO "
+                    SQLDatabaseManager.write("INSERT INTO "
                             + tablePrefix
                             + tablePrefix
                             + "experience (user_id) VALUES (" + id
                             + "experience (user_id) VALUES (" + id
                             + ")");
                             + ")");
                     // Update the skill values
                     // Update the skill values
-                    DatabaseManager.write("UPDATE "
+                    SQLDatabaseManager.write("UPDATE "
                             + tablePrefix
                             + tablePrefix
                             + "users SET lastlogin = " + 0
                             + "users SET lastlogin = " + 0
                             + " WHERE id = " + id);
                             + " WHERE id = " + id);
@@ -234,7 +234,7 @@ public class SQLConversionTask extends BukkitRunnable {
                             + "users SET party = '" + party
                             + "users SET party = '" + party
                             + "' WHERE id = " + id);
                             + "' WHERE id = " + id);
                     */
                     */
-                    DatabaseManager.write("UPDATE "
+                    SQLDatabaseManager.write("UPDATE "
                             + tablePrefix
                             + tablePrefix
                             + "skills SET "
                             + "skills SET "
                             + "  taming = taming+" + StringUtils.getInt(taming)
                             + "  taming = taming+" + StringUtils.getInt(taming)
@@ -250,7 +250,7 @@ public class SQLConversionTask extends BukkitRunnable {
                             + ", acrobatics = acrobatics+" + StringUtils.getInt(acrobatics)
                             + ", acrobatics = acrobatics+" + StringUtils.getInt(acrobatics)
                             + ", fishing = fishing+" + StringUtils.getInt(fishing)
                             + ", fishing = fishing+" + StringUtils.getInt(fishing)
                             + " WHERE user_id = " + id);
                             + " WHERE user_id = " + id);
-                    DatabaseManager.write("UPDATE "
+                    SQLDatabaseManager.write("UPDATE "
                             + tablePrefix
                             + tablePrefix
                             + "experience SET "
                             + "experience SET "
                             + "  taming = " + StringUtils.getInt(tamingXP)
                             + "  taming = " + StringUtils.getInt(tamingXP)

+ 2 - 2
src/main/java/com/gmail/nossr50/runnables/database/SQLReconnectTask.java

@@ -4,13 +4,13 @@ import org.bukkit.entity.Player;
 import org.bukkit.scheduler.BukkitRunnable;
 import org.bukkit.scheduler.BukkitRunnable;
 
 
 import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.mcMMO;
-import com.gmail.nossr50.database.DatabaseManager;
+import com.gmail.nossr50.database.SQLDatabaseManager;
 import com.gmail.nossr50.util.player.UserManager;
 import com.gmail.nossr50.util.player.UserManager;
 
 
 public class SQLReconnectTask extends BukkitRunnable {
 public class SQLReconnectTask extends BukkitRunnable {
     @Override
     @Override
     public void run() {
     public void run() {
-        if (DatabaseManager.checkConnected()) {
+        if (SQLDatabaseManager.checkConnected()) {
             UserManager.saveAll();  // Save all profiles
             UserManager.saveAll();  // Save all profiles
             UserManager.clearAll(); // Clear the profiles
             UserManager.clearAll(); // Clear the profiles
 
 

+ 4 - 14
src/main/java/com/gmail/nossr50/runnables/database/UserPurgeTask.java

@@ -2,26 +2,16 @@ package com.gmail.nossr50.runnables.database;
 
 
 import org.bukkit.scheduler.BukkitRunnable;
 import org.bukkit.scheduler.BukkitRunnable;
 
 
+import com.gmail.nossr50.mcMMO;
 import com.gmail.nossr50.config.Config;
 import com.gmail.nossr50.config.Config;
-import com.gmail.nossr50.database.DatabaseManager;
-import com.gmail.nossr50.database.LeaderboardManager;
 
 
 public class UserPurgeTask extends BukkitRunnable {
 public class UserPurgeTask extends BukkitRunnable {
     @Override
     @Override
     public void run() {
     public void run() {
-        if (Config.getInstance().getUseMySQL()) {
-            DatabaseManager.purgePowerlessSQL();
+        mcMMO.databaseManager.purgePowerlessUsers();
 
 
-            if (Config.getInstance().getOldUsersCutoff() != -1) {
-                DatabaseManager.purgeOldSQL();
-            }
-        }
-        else {
-            LeaderboardManager.purgePowerlessFlatfile();
-
-            if (Config.getInstance().getOldUsersCutoff() != -1) {
-                LeaderboardManager.purgeOldFlatfile();
-            }
+        if (Config.getInstance().getOldUsersCutoff() != -1) {
+            mcMMO.databaseManager.purgeOldUsers();
         }
         }
     }
     }
 }
 }

+ 5 - 2
src/main/java/com/gmail/nossr50/runnables/player/PlayerProfileSaveTask.java

@@ -1,5 +1,6 @@
 package com.gmail.nossr50.runnables.player;
 package com.gmail.nossr50.runnables.player;
 
 
+import org.bukkit.entity.Player;
 import org.bukkit.scheduler.BukkitRunnable;
 import org.bukkit.scheduler.BukkitRunnable;
 
 
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
 import com.gmail.nossr50.datatypes.player.McMMOPlayer;
@@ -19,8 +20,10 @@ public class PlayerProfileSaveTask extends BukkitRunnable {
     public void run() {
     public void run() {
         playerProfile.save();
         playerProfile.save();
 
 
-        if (!mcMMOPlayer.getPlayer().isOnline()) {
-            UserManager.remove(playerProfile.getPlayerName());
+        Player player = mcMMOPlayer.getPlayer();
+
+        if (!player.isOnline()) {
+            UserManager.remove(player.getName());
         }
         }
     }
     }
 }
 }

+ 0 - 16
src/main/java/com/gmail/nossr50/util/player/UserManager.java

@@ -1,7 +1,5 @@
 package com.gmail.nossr50.util.player;
 package com.gmail.nossr50.util.player;
 
 
-import java.io.File;
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -17,20 +15,6 @@ public final class UserManager {
 
 
     private UserManager() {};
     private UserManager() {};
 
 
-    /**
-     * Load users.
-     */
-    public static void loadUsers() {
-        new File(mcMMO.getFlatFileDirectory()).mkdir();
-
-        try {
-            new File(mcMMO.getUsersFilePath()).createNewFile();
-        }
-        catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
     /**
     /**
      * Add a new user.
      * Add a new user.
      *
      *

+ 1 - 3
src/main/java/net/shatteredlands/shatt/backup/ZipLibrary.java

@@ -48,10 +48,8 @@ public class ZipLibrary {
 
 
         // Create the Source List, and add directories/etc to the file.
         // Create the Source List, and add directories/etc to the file.
         List<File> sources = new ArrayList<File>();
         List<File> sources = new ArrayList<File>();
-        if (!Config.getInstance().getUseMySQL()) {
-            sources.add(FlatFileDirectory);
-        }
 
 
+        sources.add(FlatFileDirectory);
         sources.add(ConfigFile);
         sources.add(ConfigFile);
         sources.add(TreasuresFile);
         sources.add(TreasuresFile);
         sources.add(AdvancedConfigFile);
         sources.add(AdvancedConfigFile);