From 23729f45ee4e46eb482c928694850c12cb68274f Mon Sep 17 00:00:00 2001 From: riking Date: Fri, 28 Jun 2013 15:02:58 -0700 Subject: [PATCH] Pull changes from dev-dbman (commit f63c5e3) --- Changelog.txt | 2 + .../com/gmail/nossr50/api/ExperienceAPI.java | 3 +- .../commands/database/McremoveCommand.java | 3 +- .../commands/database/MmoshowdbCommand.java | 42 + .../commands/database/MmoupdateCommand.java | 113 ++- .../experience/ExperienceCommand.java | 3 +- .../experience/SkillresetCommand.java | 3 +- .../commands/player/InspectCommand.java | 3 +- .../commands/player/McrankCommand.java | 3 +- .../nossr50/database/DatabaseManager.java | 24 +- .../database/DatabaseManagerFactory.java | 57 ++ .../database/FlatfileDatabaseManager.java | 724 +++++++++++------- .../nossr50/database/SQLDatabaseManager.java | 382 ++++----- .../nossr50/datatypes/player/McMMOPlayer.java | 4 +- .../datatypes/player/PlayerProfile.java | 91 +-- .../runnables/PlayerUpdateInventoryTask.java | 1 + .../runnables/database/ConversionTask.java | 41 + .../runnables/database/SQLConversionTask.java | 37 - .../commands/CommandRegistrationManager.java | 15 +- .../resources/locale/locale_en_US.properties | 13 +- src/main/resources/plugin.yml | 7 +- 21 files changed, 969 insertions(+), 602 deletions(-) create mode 100644 src/main/java/com/gmail/nossr50/commands/database/MmoshowdbCommand.java create mode 100644 src/main/java/com/gmail/nossr50/runnables/database/ConversionTask.java delete mode 100644 src/main/java/com/gmail/nossr50/runnables/database/SQLConversionTask.java diff --git a/Changelog.txt b/Changelog.txt index a51b31a67..9a0be239c 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -29,6 +29,8 @@ Version 1.4.06-dev + Added information about /party itemshare and /party expshare to the party help page + Added option to use scoreboards for power level display instead of Spout. + Added permission node to prevent inspecting hidden players + + Added SQL to Flatfile database conversion + + Added ability to use custom database managers and convert to/from them = Fixed bug which could cause the server to hang for a minute when checking for updates. (Thanks to Riking) = Fixed bug where spawned arrows could throw ArrayIndexOutOfBoundsException = Fixed bug where custom Spout titles were overwritten by mcMMO. diff --git a/src/main/java/com/gmail/nossr50/api/ExperienceAPI.java b/src/main/java/com/gmail/nossr50/api/ExperienceAPI.java index 87a068c60..fca322259 100644 --- a/src/main/java/com/gmail/nossr50/api/ExperienceAPI.java +++ b/src/main/java/com/gmail/nossr50/api/ExperienceAPI.java @@ -4,6 +4,7 @@ import java.util.Set; import org.bukkit.entity.Player; +import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.api.exceptions.InvalidPlayerException; import com.gmail.nossr50.api.exceptions.InvalidSkillException; import com.gmail.nossr50.config.Config; @@ -552,7 +553,7 @@ public final class ExperienceAPI { } private static PlayerProfile getOfflineProfile(String playerName) { - PlayerProfile profile = new PlayerProfile(playerName, false); + PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(playerName, false); if (!profile.isLoaded()) { throw new InvalidPlayerException(); diff --git a/src/main/java/com/gmail/nossr50/commands/database/McremoveCommand.java b/src/main/java/com/gmail/nossr50/commands/database/McremoveCommand.java index fab2166a6..4d3fd3cd3 100644 --- a/src/main/java/com/gmail/nossr50/commands/database/McremoveCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/database/McremoveCommand.java @@ -10,7 +10,6 @@ import org.bukkit.command.TabExecutor; import org.bukkit.util.StringUtil; import com.gmail.nossr50.mcMMO; -import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.locale.LocaleLoader; import com.gmail.nossr50.util.commands.CommandUtils; import com.gmail.nossr50.util.player.UserManager; @@ -22,7 +21,7 @@ public class McremoveCommand implements TabExecutor { public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { switch (args.length) { case 1: - if (UserManager.getPlayer(args[0]) == null && CommandUtils.unloadedProfile(sender, new PlayerProfile(args[0], false))) { + if (UserManager.getPlayer(args[0]) == null && CommandUtils.unloadedProfile(sender, mcMMO.getDatabaseManager().loadPlayerProfile(args[0], false))) { return true; } diff --git a/src/main/java/com/gmail/nossr50/commands/database/MmoshowdbCommand.java b/src/main/java/com/gmail/nossr50/commands/database/MmoshowdbCommand.java new file mode 100644 index 000000000..4f5f6c6cb --- /dev/null +++ b/src/main/java/com/gmail/nossr50/commands/database/MmoshowdbCommand.java @@ -0,0 +1,42 @@ +package com.gmail.nossr50.commands.database; + +import java.util.List; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; + +import com.gmail.nossr50.config.Config; +import com.gmail.nossr50.database.DatabaseManagerFactory; +import com.gmail.nossr50.locale.LocaleLoader; +import com.google.common.collect.ImmutableList; + +public class MmoshowdbCommand implements TabExecutor { + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (args.length != 0) { + return false; + } + else { + Class clazz = DatabaseManagerFactory.getCustomDatabaseManagerClass(); + if (clazz != null) { + sender.sendMessage(LocaleLoader.getString("Commands.mmoshowdb", clazz.getName())); + return true; + } + else { + if (Config.getInstance().getUseMySQL()) { + sender.sendMessage(LocaleLoader.getString("Commands.mmoshowdb", "sql")); + } + else { + sender.sendMessage(LocaleLoader.getString("Commands.mmoshowdb", "flatfile")); + } + return true; + } + } + } + + @Override + public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + return ImmutableList.of(); + } +} diff --git a/src/main/java/com/gmail/nossr50/commands/database/MmoupdateCommand.java b/src/main/java/com/gmail/nossr50/commands/database/MmoupdateCommand.java index cf76ee675..38c89d475 100644 --- a/src/main/java/com/gmail/nossr50/commands/database/MmoupdateCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/database/MmoupdateCommand.java @@ -9,41 +9,130 @@ import org.bukkit.entity.Player; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; +import com.gmail.nossr50.database.DatabaseManager; +import com.gmail.nossr50.database.DatabaseManagerFactory; +import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.locale.LocaleLoader; -import com.gmail.nossr50.runnables.database.SQLConversionTask; +import com.gmail.nossr50.runnables.database.ConversionTask; import com.gmail.nossr50.util.player.UserManager; import com.google.common.collect.ImmutableList; public class MmoupdateCommand implements TabExecutor { + @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (!Config.getInstance().getUseMySQL()) { - sender.sendMessage("SQL Mode is not enabled."); // TODO: Localize - return true; - } - switch (args.length) { - case 0: - sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.Start")); + case 1: + String argType = args[0]; + String oldType = validateName(sender, args[0]); + if (oldType == null) { + return true; + } + + String newType = getCurrentDb(); + + if (newType.equals(oldType)) { + sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.Same", argType)); + return true; + } + + DatabaseManager oldDb; + if (oldType == "sql") { + oldDb = DatabaseManagerFactory.createSQLDatabaseManager(); + } + else if (oldType == "flatfile") { + oldDb = DatabaseManagerFactory.createFlatfileDatabaseManager(); + } + else try { + @SuppressWarnings("unchecked") + Class clazz = (Class) Class.forName(oldType); + oldDb = DatabaseManagerFactory.createCustomDatabaseManager((Class) clazz); + + oldType = clazz.getSimpleName(); // For pretty-printing; we have the database now + } + catch (Throwable e) { + return false; + } + + sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.Start", oldType, newType)); + + // Convert the online players right away, without waiting + // first, flush out the current data UserManager.saveAll(); UserManager.clearAll(); - new SQLConversionTask().runTaskLaterAsynchronously(mcMMO.p, 1); for (Player player : mcMMO.p.getServer().getOnlinePlayers()) { + // Get the profile from the old database and save it in the new + PlayerProfile profile = oldDb.loadPlayerProfile(player.getName(), false); + if (profile.isLoaded()) { + mcMMO.getDatabaseManager().saveUser(profile); + } + + // Reload from the current database via UserManager UserManager.addUser(player); } - sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.Finish")); + // Schedule the task for all users + new ConversionTask(oldDb, sender, oldType, newType).runTaskAsynchronously(mcMMO.p); + return true; default: - return false; + break; + } + return false; + } + + /** + * @return null - if type not recognized / class not found + * empty string - if type is same as current + * normalized string - if type is recognized + */ + private String validateName(CommandSender sender, String type) { + if (type.equalsIgnoreCase("sql") || type.equalsIgnoreCase("mysql")) { + return "sql"; + } + + if (type.equalsIgnoreCase("flatfile") || type.equalsIgnoreCase("file")) { + return "flatfile"; + } + + try { + Class clazz = Class.forName(type); + + if (!DatabaseManager.class.isAssignableFrom(clazz)) { + sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.InvalidType", type)); + return null; + } + + return type; + } + catch (Exception e) { + sender.sendMessage(LocaleLoader.getString("Commands.mmoupdate.InvalidType", type)); + return null; + } + } + + private String getCurrentDb() { + if (DatabaseManagerFactory.getCustomDatabaseManagerClass() != null) { + return DatabaseManagerFactory.getCustomDatabaseManagerClass().getSimpleName(); + } + + if (Config.getInstance().getUseMySQL()) { + return "sql"; + } + else { + return "flatfile"; } } @Override public List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { - return ImmutableList.of(); + Class clazz = DatabaseManagerFactory.getCustomDatabaseManagerClass(); + if (clazz != null) { + return ImmutableList.of("flatfile", "sql", clazz.getName()); + } + return ImmutableList.of("flatfile", "sql"); } } diff --git a/src/main/java/com/gmail/nossr50/commands/experience/ExperienceCommand.java b/src/main/java/com/gmail/nossr50/commands/experience/ExperienceCommand.java index e7de5a510..e2c462696 100644 --- a/src/main/java/com/gmail/nossr50/commands/experience/ExperienceCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/experience/ExperienceCommand.java @@ -10,6 +10,7 @@ import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.util.StringUtil; +import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.skills.SkillType; @@ -68,7 +69,7 @@ public abstract class ExperienceCommand implements TabExecutor { // If the mcMMOPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process. if (mcMMOPlayer == null) { - profile = new PlayerProfile(args[0], false); + profile = mcMMO.getDatabaseManager().loadPlayerProfile(args[0], false); if (CommandUtils.unloadedProfile(sender, profile)) { return true; diff --git a/src/main/java/com/gmail/nossr50/commands/experience/SkillresetCommand.java b/src/main/java/com/gmail/nossr50/commands/experience/SkillresetCommand.java index 4fb74e656..2e4385c24 100644 --- a/src/main/java/com/gmail/nossr50/commands/experience/SkillresetCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/experience/SkillresetCommand.java @@ -5,7 +5,6 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import com.gmail.nossr50.mcMMO; -import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.events.experience.McMMOPlayerLevelUpEvent; import com.gmail.nossr50.locale.LocaleLoader; @@ -61,7 +60,7 @@ public class SkillresetCommand extends ExperienceCommand { // If the mcMMOPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process. if (mcMMOPlayer == null) { - profile = new PlayerProfile(args[0], false); + profile = mcMMO.getDatabaseManager().loadPlayerProfile(args[0], false); if (CommandUtils.unloadedProfile(sender, profile)) { return true; diff --git a/src/main/java/com/gmail/nossr50/commands/player/InspectCommand.java b/src/main/java/com/gmail/nossr50/commands/player/InspectCommand.java index 3a84ff163..6ee00822a 100644 --- a/src/main/java/com/gmail/nossr50/commands/player/InspectCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/player/InspectCommand.java @@ -10,6 +10,7 @@ import org.bukkit.command.TabExecutor; import org.bukkit.entity.Player; import org.bukkit.util.StringUtil; +import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; import com.gmail.nossr50.datatypes.player.McMMOPlayer; import com.gmail.nossr50.datatypes.player.PlayerProfile; @@ -35,7 +36,7 @@ public class InspectCommand implements TabExecutor { // If the mcMMOPlayer doesn't exist, create a temporary profile and check if it's present in the database. If it's not, abort the process. if (mcMMOPlayer == null) { - PlayerProfile profile = new PlayerProfile(args[0], false); // Temporary Profile + PlayerProfile profile = mcMMO.getDatabaseManager().loadPlayerProfile(args[0], false); // Temporary Profile if (CommandUtils.inspectOffline(sender, profile, Permissions.inspectOffline(sender))) { return true; diff --git a/src/main/java/com/gmail/nossr50/commands/player/McrankCommand.java b/src/main/java/com/gmail/nossr50/commands/player/McrankCommand.java index 4ec06d233..d69311509 100644 --- a/src/main/java/com/gmail/nossr50/commands/player/McrankCommand.java +++ b/src/main/java/com/gmail/nossr50/commands/player/McrankCommand.java @@ -13,7 +13,6 @@ import org.bukkit.util.StringUtil; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; import com.gmail.nossr50.datatypes.player.McMMOPlayer; -import com.gmail.nossr50.datatypes.player.PlayerProfile; import com.gmail.nossr50.runnables.commands.McrankCommandAsyncTask; import com.gmail.nossr50.util.Permissions; import com.gmail.nossr50.util.commands.CommandUtils; @@ -61,7 +60,7 @@ public class McrankCommand implements TabExecutor { return true; } } - else if (CommandUtils.inspectOffline(sender, new PlayerProfile(playerName, false), Permissions.mcrankOffline(sender))) { + else if (CommandUtils.inspectOffline(sender, mcMMO.getDatabaseManager().loadPlayerProfile(playerName, false), Permissions.mcrankOffline(sender))) { return true; } diff --git a/src/main/java/com/gmail/nossr50/database/DatabaseManager.java b/src/main/java/com/gmail/nossr50/database/DatabaseManager.java index bbe3ad7e8..996db720f 100644 --- a/src/main/java/com/gmail/nossr50/database/DatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/DatabaseManager.java @@ -8,6 +8,7 @@ import com.gmail.nossr50.datatypes.database.PlayerStat; import com.gmail.nossr50.datatypes.player.PlayerProfile; public interface DatabaseManager { + // One month in milliseconds public final long PURGE_TIME = 2630000000L * Config.getInstance().getOldUsersCutoff(); /** @@ -64,16 +65,25 @@ public interface DatabaseManager { * Load a player from the database. * * @param playerName The name of the player to load from the database - * @return The player's data + * @param createNew Whether to create a new record if the player is not + * found + * @return The player's data, or an unloaded PlayerProfile if not found + * and createNew is false */ - public List loadPlayerData(String playerName); + public PlayerProfile loadPlayerProfile(String playerName, boolean createNew); /** - * Convert player data to a different storage format. + * Get all users currently stored in the database. * - * @param data The player's data - * @return true if the conversion was successful, false otherwise - * @throws Exception + * @return list of playernames */ - public boolean convert(String[] data) throws Exception; + public List getStoredUsers(); + + /** + * Convert all users from this database to the provided database using + * {@link #saveUser(PlayerProfile)}. + * + * @param the DatabaseManager to save to + */ + public void convertUsers(DatabaseManager destination); } diff --git a/src/main/java/com/gmail/nossr50/database/DatabaseManagerFactory.java b/src/main/java/com/gmail/nossr50/database/DatabaseManagerFactory.java index 9f9b12ab6..a798af736 100644 --- a/src/main/java/com/gmail/nossr50/database/DatabaseManagerFactory.java +++ b/src/main/java/com/gmail/nossr50/database/DatabaseManagerFactory.java @@ -1,9 +1,66 @@ package com.gmail.nossr50.database; +import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; public class DatabaseManagerFactory { + private static Class customManager = null; + public static DatabaseManager getDatabaseManager() { + if (customManager != null) { + try { + return createCustomDatabaseManager(customManager); + } catch (Exception e) { + mcMMO.p.debug("Could not create custom database manager"); + e.printStackTrace(); + } catch (Throwable e) { + mcMMO.p.debug("Failed to create custom database manager"); + e.printStackTrace(); + } + mcMMO.p.debug("Falling back on " + (Config.getInstance().getUseMySQL() ? "SQL" : "Flatfile") + " database"); + } return Config.getInstance().getUseMySQL() ? new SQLDatabaseManager() : new FlatfileDatabaseManager(); } + + /** + * Sets the custom DatabaseManager class for McMMO to use. This should be + * called prior to mcMMO enabling. + *

+ * The provided class must have an empty constructor, which is the one + * that will be used. + *

+ * This method is intended for API use, but it should not be considered + * stable. This method is subject to change and/or removal in future + * versions. + * + * @param man the DatabaseManager class to use + * @throws IllegalArgumentException if the provided class does not have + * an empty constructor + */ + public static void setCustomDatabaseManagerClass(Class clazz) { + try { + clazz.getConstructor((Class) null); + } catch (Throwable e) { + throw new IllegalArgumentException("Provided database manager class must have an empty constructor", e); + } + customManager = clazz; + } + + public static Class getCustomDatabaseManagerClass() { + return customManager; + } + + // For data conversion purposes + + public static FlatfileDatabaseManager createFlatfileDatabaseManager() { + return new FlatfileDatabaseManager(); + } + + public static SQLDatabaseManager createSQLDatabaseManager() { + return new SQLDatabaseManager(); + } + + public static DatabaseManager createCustomDatabaseManager(Class clazz) throws Throwable { + return customManager.getConstructor((Class) null).newInstance((Object[]) null); + } } diff --git a/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java index f32724f77..861997ec0 100644 --- a/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/FlatfileDatabaseManager.java @@ -2,6 +2,7 @@ package com.gmail.nossr50.database; import java.io.BufferedReader; import java.io.BufferedWriter; +import java.io.Closeable; import java.io.File; import java.io.FileReader; import java.io.FileWriter; @@ -13,6 +14,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import com.gmail.nossr50.mcMMO; @@ -32,6 +34,7 @@ public final class FlatfileDatabaseManager implements DatabaseManager { private final long UPDATE_WAIT_TIME = 600000L; // 10 minutes private final File usersFile; + private static final Object fileWritingLock = new Object(); protected FlatfileDatabaseManager() { usersFile = new File(mcMMO.getUsersFilePath()); @@ -44,9 +47,49 @@ public final class FlatfileDatabaseManager implements DatabaseManager { mcMMO.p.getLogger().info("Purging powerless users..."); - for (PlayerStat stat : powerLevels) { - if (stat.statVal == 0 && mcMMO.p.getServer().getPlayerExact(stat.name) == null && removeUser(stat.name)) { - purgedUsers++; + BufferedReader in = null; + FileWriter out = null; + String usersFilePath = mcMMO.getUsersFilePath(); + + // This code is O(n) instead of O(n²) + synchronized (fileWritingLock) { + try { + in = new BufferedReader(new FileReader(usersFilePath)); + StringBuilder writer = new StringBuilder(); + String line = ""; + + while ((line = in.readLine()) != null) { + String[] character = line.split(":"); + Map skills = getSkillMapFromLine(character); + + boolean powerless = true; + for (int skill : skills.values()) { + if (skill != 0) { + powerless = false; + break; + } + } + + // If they're still around, rewrite them to the file. + if (!powerless) { + writer.append(line).append("\r\n"); + } + else { + purgedUsers++; + Misc.profileCleanup(character[0]); + } + } + + // Write the new file + out = new FileWriter(usersFilePath); + out.write(writer.toString()); + } + catch (IOException e) { + mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString()); + } + finally { + tryClose(in); + tryClose(out); } } @@ -59,9 +102,46 @@ public final class FlatfileDatabaseManager implements DatabaseManager { mcMMO.p.getLogger().info("Purging old users..."); - for (OfflinePlayer player : mcMMO.p.getServer().getOfflinePlayers()) { - if (!player.isOnline() && (currentTime - player.getLastPlayed() > PURGE_TIME && removeUser(player.getName()))) { - removedPlayers++; + + BufferedReader in = null; + FileWriter out = null; + String usersFilePath = mcMMO.getUsersFilePath(); + + // This code is O(n) instead of O(n²) + synchronized (fileWritingLock) { + try { + in = new BufferedReader(new FileReader(usersFilePath)); + StringBuilder writer = new StringBuilder(); + String line = ""; + + while ((line = in.readLine()) != null) { + String[] character = line.split(":"); + String name = character[0]; + OfflinePlayer player = Bukkit.getOfflinePlayer(name); + boolean old = true; + if (player != null) { + old = (currentTime - player.getLastPlayed()) > PURGE_TIME; + } + + if (!old) { + writer.append(line).append("\r\n"); + } + else { + removedPlayers++; + Misc.profileCleanup(name); + } + } + + // Write the new file + out = new FileWriter(usersFilePath); + out.write(writer.toString()); + } + catch (IOException e) { + mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString()); + } + finally { + tryClose(in); + tryClose(out); } } @@ -75,45 +155,32 @@ public final class FlatfileDatabaseManager implements DatabaseManager { FileWriter out = null; String usersFilePath = mcMMO.getUsersFilePath(); - try { - in = new BufferedReader(new FileReader(usersFilePath)); - StringBuilder writer = new StringBuilder(); - String line = ""; + synchronized (fileWritingLock) { + try { + in = new BufferedReader(new FileReader(usersFilePath)); + StringBuilder writer = new StringBuilder(); + String line = ""; - while ((line = in.readLine()) != null) { - // Write out the same file but when we get to the player we want to remove, we skip his line. - if (!worked && line.split(":")[0].equalsIgnoreCase(playerName)) { - mcMMO.p.getLogger().info("User found, removing..."); - worked = true; - continue; // Skip the player + while ((line = in.readLine()) != null) { + // Write out the same file but when we get to the player we want to remove, we skip his line. + if (!worked && line.split(":")[0].equalsIgnoreCase(playerName)) { + mcMMO.p.getLogger().info("User found, removing..."); + worked = true; + continue; // Skip the player + } + + writer.append(line).append("\r\n"); } - writer.append(line).append("\r\n"); + out = new FileWriter(usersFilePath); // Write out the new file + out.write(writer.toString()); } - - out = new FileWriter(usersFilePath); // Write out the new file - out.write(writer.toString()); - } - catch (Exception e) { - mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString()); - } - finally { - if (in != null) { - try { - in.close(); - } - catch (IOException ex) { - ex.printStackTrace(); - } + catch (Exception e) { + mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString()); } - - if (out != null) { - try { - out.close(); - } - catch (IOException ex) { - ex.printStackTrace(); - } + finally { + tryClose(in); + tryClose(out); } } @@ -129,89 +196,76 @@ public final class FlatfileDatabaseManager implements DatabaseManager { FileWriter out = null; String usersFilePath = mcMMO.getUsersFilePath(); - try { - // Open the file - in = new BufferedReader(new FileReader(usersFilePath)); - StringBuilder writer = new StringBuilder(); - String line; + synchronized (fileWritingLock) { + try { + // Open the file + in = new BufferedReader(new FileReader(usersFilePath)); + 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(profile.getSkillLevel(SkillType.MINING)).append(":"); - writer.append(":"); - writer.append(":"); - writer.append(profile.getSkillXpLevel(SkillType.MINING)).append(":"); - writer.append(profile.getSkillLevel(SkillType.WOODCUTTING)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.WOODCUTTING)).append(":"); - writer.append(profile.getSkillLevel(SkillType.REPAIR)).append(":"); - writer.append(profile.getSkillLevel(SkillType.UNARMED)).append(":"); - writer.append(profile.getSkillLevel(SkillType.HERBALISM)).append(":"); - writer.append(profile.getSkillLevel(SkillType.EXCAVATION)).append(":"); - writer.append(profile.getSkillLevel(SkillType.ARCHERY)).append(":"); - writer.append(profile.getSkillLevel(SkillType.SWORDS)).append(":"); - writer.append(profile.getSkillLevel(SkillType.AXES)).append(":"); - writer.append(profile.getSkillLevel(SkillType.ACROBATICS)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.REPAIR)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.UNARMED)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.HERBALISM)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.EXCAVATION)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.ARCHERY)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.SWORDS)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.AXES)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.ACROBATICS)).append(":"); - writer.append(":"); - writer.append(profile.getSkillLevel(SkillType.TAMING)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.TAMING)).append(":"); - writer.append((int) profile.getSkillDATS(AbilityType.BERSERK)).append(":"); - writer.append((int) profile.getSkillDATS(AbilityType.GIGA_DRILL_BREAKER)).append(":"); - writer.append((int) profile.getSkillDATS(AbilityType.TREE_FELLER)).append(":"); - writer.append((int) profile.getSkillDATS(AbilityType.GREEN_TERRA)).append(":"); - writer.append((int) profile.getSkillDATS(AbilityType.SERRATED_STRIKES)).append(":"); - writer.append((int) profile.getSkillDATS(AbilityType.SKULL_SPLITTER)).append(":"); - writer.append((int) profile.getSkillDATS(AbilityType.SUPER_BREAKER)).append(":"); - HudType hudType = profile.getHudType(); - writer.append(hudType == null ? "STANDARD" : hudType.toString()).append(":"); - writer.append(profile.getSkillLevel(SkillType.FISHING)).append(":"); - writer.append(profile.getSkillXpLevel(SkillType.FISHING)).append(":"); - writer.append((int) profile.getSkillDATS(AbilityType.BLAST_MINING)).append(":"); - writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":"); - MobHealthbarType mobHealthbarType = profile.getMobHealthbarType(); - writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":"); - writer.append("\r\n"); + // 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(profile.getSkillLevel(SkillType.MINING)).append(":"); + writer.append(":"); + writer.append(":"); + writer.append(profile.getSkillXpLevel(SkillType.MINING)).append(":"); + writer.append(profile.getSkillLevel(SkillType.WOODCUTTING)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.WOODCUTTING)).append(":"); + writer.append(profile.getSkillLevel(SkillType.REPAIR)).append(":"); + writer.append(profile.getSkillLevel(SkillType.UNARMED)).append(":"); + writer.append(profile.getSkillLevel(SkillType.HERBALISM)).append(":"); + writer.append(profile.getSkillLevel(SkillType.EXCAVATION)).append(":"); + writer.append(profile.getSkillLevel(SkillType.ARCHERY)).append(":"); + writer.append(profile.getSkillLevel(SkillType.SWORDS)).append(":"); + writer.append(profile.getSkillLevel(SkillType.AXES)).append(":"); + writer.append(profile.getSkillLevel(SkillType.ACROBATICS)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.REPAIR)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.UNARMED)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.HERBALISM)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.EXCAVATION)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.ARCHERY)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.SWORDS)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.AXES)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.ACROBATICS)).append(":"); + writer.append(":"); + writer.append(profile.getSkillLevel(SkillType.TAMING)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.TAMING)).append(":"); + writer.append((int) profile.getSkillDATS(AbilityType.BERSERK)).append(":"); + writer.append((int) profile.getSkillDATS(AbilityType.GIGA_DRILL_BREAKER)).append(":"); + writer.append((int) profile.getSkillDATS(AbilityType.TREE_FELLER)).append(":"); + writer.append((int) profile.getSkillDATS(AbilityType.GREEN_TERRA)).append(":"); + writer.append((int) profile.getSkillDATS(AbilityType.SERRATED_STRIKES)).append(":"); + writer.append((int) profile.getSkillDATS(AbilityType.SKULL_SPLITTER)).append(":"); + writer.append((int) profile.getSkillDATS(AbilityType.SUPER_BREAKER)).append(":"); + HudType hudType = profile.getHudType(); + writer.append(hudType == null ? "STANDARD" : hudType.toString()).append(":"); + writer.append(profile.getSkillLevel(SkillType.FISHING)).append(":"); + writer.append(profile.getSkillXpLevel(SkillType.FISHING)).append(":"); + writer.append((int) profile.getSkillDATS(AbilityType.BLAST_MINING)).append(":"); + writer.append(System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR).append(":"); + MobHealthbarType mobHealthbarType = profile.getMobHealthbarType(); + writer.append(mobHealthbarType == null ? Config.getInstance().getMobHealthbarDefault().toString() : mobHealthbarType.toString()).append(":"); + writer.append("\r\n"); + } } + + // Write the new file + out = new FileWriter(usersFilePath); + out.write(writer.toString()); } - - // Write the new file - out = new FileWriter(usersFilePath); - out.write(writer.toString()); - } - catch (Exception e) { - e.printStackTrace(); - } - finally { - if (in != null) { - try { - in.close(); - } - catch (IOException ex) { - ex.printStackTrace(); - } + catch (Exception e) { + e.printStackTrace(); } - - if (out != null) { - try { - out.close(); - } - catch (IOException ex) { - ex.printStackTrace(); - } + finally { + tryClose(in); + tryClose(out); } } } @@ -239,145 +293,168 @@ public final class FlatfileDatabaseManager implements DatabaseManager { } public void newUser(String playerName) { - try { - // Open the file to write the player - BufferedWriter out = new BufferedWriter(new FileWriter(mcMMO.getUsersFilePath(), true)); + BufferedWriter out = null; + synchronized (fileWritingLock) { + try { + // Open the file to write the player + 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("STANDARD").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(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD + // 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("STANDARD").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(Config.getInstance().getMobHealthbarDefault().toString()).append(":"); // Mob Healthbar HUD - // Add more in the same format as the line above + // Add more in the same format as the line above - out.newLine(); - out.close(); - } - catch (Exception e) { - e.printStackTrace(); - } - } - - public List loadPlayerData(String playerName) { - List playerData = new ArrayList(); - 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; - } - - // Skill levels - playerData.add(character[24]); // Taming - playerData.add(character[1]); // Mining - playerData.add(character[7]); // Repair - playerData.add(character[5]); // Woodcutting - playerData.add(character[8]); // Unarmed - playerData.add(character[9]); // Herbalism - playerData.add(character[10]); // Excavation - playerData.add(character[11]); // Archery - playerData.add(character[12]); // Swords - playerData.add(character[13]); // Axes - playerData.add(character[14]); // Acrobatics - playerData.add(character[34]); // Fishing - - // Experience - playerData.add(character[25]); // Taming - playerData.add(character[4]); // Mining - playerData.add(character[15]); // Repair - playerData.add(character[6]); // Woodcutting - playerData.add(character[16]); // Unarmed - playerData.add(character[17]); // Herbalism - playerData.add(character[18]); // Excavation - playerData.add(character[19]); // Archery - playerData.add(character[20]); // Swords - playerData.add(character[21]); // Axes - playerData.add(character[22]); // Acrobatics - playerData.add(character[35]); // Fishing - - // Cooldowns - playerData.add(null); // Taming - playerData.add(character[32]); // SuperBreaker - playerData.add(null); // Repair - playerData.add(character[28]); // Tree Feller - playerData.add(character[26]); // Beserk - playerData.add(character[29]); // Green Terra - playerData.add(character[27]); // Giga Drill Breaker - playerData.add(null); // Archery - playerData.add(character[30]); // Serrated Strikes - playerData.add(character[31]); // Skull Splitter - playerData.add(null); // Acrobatics - playerData.add(character[36]); // Blast Mining - - playerData.add(character.length > 33 ? character[33] : null); // HudType - playerData.add(character.length > 38 ? character[38] : null); // MobHealthBar + out.newLine(); + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + tryClose(out); } - - in.close(); } - catch (Exception e) { - e.printStackTrace(); - } - - return playerData; } - public boolean convert(String[] character) throws Exception { - // Not implemented - return false; + public PlayerProfile loadPlayerProfile(String playerName, boolean create) { + BufferedReader in = null; + String usersFilePath = mcMMO.getUsersFilePath(); + + synchronized (fileWritingLock) { + try { + // Open the user file + in = new BufferedReader(new FileReader(usersFilePath)); + 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; + } + + PlayerProfile p = loadFromLine(character); + in.close(); + return p; + } + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + tryClose(in); + } + } + + if (create) { + newUser(playerName); + return new PlayerProfile(playerName, true); + } + return new PlayerProfile(playerName); + } + + public void convertUsers(DatabaseManager destination) { + BufferedReader in = null; + String usersFilePath = mcMMO.getUsersFilePath(); + + synchronized (fileWritingLock) { + try { + // Open the user file + in = new BufferedReader(new FileReader(usersFilePath)); + String line; + + while ((line = in.readLine()) != null) { + String[] character = line.split(":"); + + try { + destination.saveUser(loadFromLine(character)); + } + catch (Exception e) { + e.printStackTrace(); + } + } + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + tryClose(in); + } + } } public boolean checkConnected() { // Not implemented - return false; + return true; + } + + public List getStoredUsers() { + ArrayList users = new ArrayList(); + BufferedReader in = null; + String usersFilePath = mcMMO.getUsersFilePath(); + + synchronized (fileWritingLock) { + try { + // Open the user file + in = new BufferedReader(new FileReader(usersFilePath)); + String line; + + while ((line = in.readLine()) != null) { + String[] character = line.split(":"); + users.add(character[0]); + } + } + catch (Exception e) { + e.printStackTrace(); + } + finally { + tryClose(in); + } + } + return users; } /** -* Update the leader boards. -*/ + * Update the leader boards. + */ private void updateLeaderboards() { // Only update FFS leaderboards every 10 minutes.. this puts a lot of strain on the server (depending on the size of the database) and should not be done frequently if (System.currentTimeMillis() < lastUpdate + UPDATE_WAIT_TIME) { @@ -402,43 +479,50 @@ public final class FlatfileDatabaseManager implements DatabaseManager { List taming = new ArrayList(); List fishing = new ArrayList(); + BufferedReader in = null; // Read from the FlatFile database and fill our arrays with information - try { - BufferedReader in = new BufferedReader(new FileReader(usersFilePath)); - String line = ""; - ArrayList players = new ArrayList(); + synchronized (fileWritingLock) { + try { + in = new BufferedReader(new FileReader(usersFilePath)); + String line = ""; + ArrayList players = new ArrayList(); - while ((line = in.readLine()) != null) { - String[] data = line.split(":"); - String playerName = data[0]; - int powerLevel = 0; + while ((line = in.readLine()) != null) { + String[] data = line.split(":"); + String playerName = data[0]; + int powerLevel = 0; - // Prevent the same player from being added multiple times (I'd like to note that this shouldn't happen...) - if (players.contains(playerName)) { - continue; + // Prevent the same player from being added multiple times (I'd like to note that this shouldn't happen...) + if (players.contains(playerName)) { + continue; + } + + players.add(playerName); + + Map skills = getSkillMapFromLine(data); + + powerLevel += putStat(acrobatics, playerName, skills.get(SkillType.ACROBATICS)); + powerLevel += putStat(archery, playerName, skills.get(SkillType.ARCHERY)); + powerLevel += putStat(axes, playerName, skills.get(SkillType.AXES)); + powerLevel += putStat(excavation, playerName, skills.get(SkillType.EXCAVATION)); + powerLevel += putStat(fishing, playerName, skills.get(SkillType.FISHING)); + powerLevel += putStat(herbalism, playerName, skills.get(SkillType.HERBALISM)); + powerLevel += putStat(mining, playerName, skills.get(SkillType.MINING)); + powerLevel += putStat(repair, playerName, skills.get(SkillType.REPAIR)); + powerLevel += putStat(swords, playerName, skills.get(SkillType.SWORDS)); + powerLevel += putStat(taming, playerName, skills.get(SkillType.TAMING)); + powerLevel += putStat(unarmed, playerName, skills.get(SkillType.UNARMED)); + powerLevel += putStat(woodcutting, playerName, skills.get(SkillType.WOODCUTTING)); + + putStat(powerLevels, playerName, powerLevel); } - - players.add(playerName); - - 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)); } - in.close(); - } - catch (Exception e) { - mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString()); + catch (Exception e) { + mcMMO.p.getLogger().severe("Exception while reading " + usersFilePath + " (Are you sure you formatted it correctly?)" + e.toString()); + } + finally { + tryClose(in); + } } SkillComparator c = new SkillComparator(); @@ -487,6 +571,15 @@ public final class FlatfileDatabaseManager implements DatabaseManager { } } + private void tryClose(Closeable c) { + if (c == null) return; + try { + c.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + private Integer getPlayerRank(String playerName, List statsList) { if (statsList == null) { return null; @@ -505,14 +598,8 @@ public final class FlatfileDatabaseManager implements DatabaseManager { return null; } - private int loadStat(List statList, String playerName, String[] data, int dataIndex) { - if (data.length <= dataIndex) { - return 0; - } - - int statValue = Integer.parseInt(data[dataIndex]); + private int putStat(List statList, String playerName, int statValue) { statList.add(new PlayerStat(playerName, statValue)); - return statValue; } @@ -522,4 +609,75 @@ public final class FlatfileDatabaseManager implements DatabaseManager { return (o2.statVal - o1.statVal); } } + + private PlayerProfile loadFromLine(String[] character) throws Exception { + Map skills = getSkillMapFromLine(character); // Skill levels + Map skillsXp = new HashMap(); // Skill & XP + Map skillsDATS = new HashMap(); // Ability & Cooldown + HudType hudType; + MobHealthbarType mobHealthbarType; + + // TODO on updates, put new values in a try{} ? + + skillsXp.put(SkillType.TAMING, (float) Integer.valueOf(character[25])); + skillsXp.put(SkillType.MINING, (float) Integer.valueOf(character[4])); + skillsXp.put(SkillType.REPAIR, (float) Integer.valueOf(character[15])); + skillsXp.put(SkillType.WOODCUTTING, (float) Integer.valueOf(character[6])); + skillsXp.put(SkillType.UNARMED, (float) Integer.valueOf(character[16])); + skillsXp.put(SkillType.HERBALISM, (float) Integer.valueOf(character[17])); + skillsXp.put(SkillType.EXCAVATION, (float) Integer.valueOf(character[18])); + skillsXp.put(SkillType.ARCHERY, (float) Integer.valueOf(character[19])); + skillsXp.put(SkillType.SWORDS, (float) Integer.valueOf(character[20])); + skillsXp.put(SkillType.AXES, (float) Integer.valueOf(character[21])); + skillsXp.put(SkillType.ACROBATICS, (float) Integer.valueOf(character[22])); + skillsXp.put(SkillType.FISHING, (float) Integer.valueOf(character[35])); + + // Taming - Unused + skillsDATS.put(AbilityType.SUPER_BREAKER, Integer.valueOf(character[32])); + // Repair - Unused + skillsDATS.put(AbilityType.TREE_FELLER, Integer.valueOf(character[28])); + skillsDATS.put(AbilityType.BERSERK, Integer.valueOf(character[26])); + skillsDATS.put(AbilityType.GREEN_TERRA, Integer.valueOf(character[29])); + skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, Integer.valueOf(character[27])); + // Archery - Unused + skillsDATS.put(AbilityType.SERRATED_STRIKES, Integer.valueOf(character[30])); + skillsDATS.put(AbilityType.SKULL_SPLITTER, Integer.valueOf(character[31])); + // Acrobatics - Unused + skillsDATS.put(AbilityType.BLAST_MINING, Integer.valueOf(character[36])); + + try { + hudType = HudType.valueOf(character[33]); + } + catch (Exception e) { + hudType = HudType.STANDARD; // Shouldn't happen unless database is being tampered with + } + + try { + mobHealthbarType = MobHealthbarType.valueOf(character[38]); + } + catch (Exception e) { + mobHealthbarType = Config.getInstance().getMobHealthbarDefault(); + } + + return new PlayerProfile(character[0], skills, skillsXp, skillsDATS, hudType, mobHealthbarType); + } + + private Map getSkillMapFromLine(String[] character) { + Map skills = new HashMap(); // Skill & Level + + skills.put(SkillType.TAMING, Integer.valueOf(character[24])); + skills.put(SkillType.MINING, Integer.valueOf(character[1])); + skills.put(SkillType.REPAIR, Integer.valueOf(character[7])); + skills.put(SkillType.WOODCUTTING, Integer.valueOf(character[5])); + skills.put(SkillType.UNARMED, Integer.valueOf(character[8])); + skills.put(SkillType.HERBALISM, Integer.valueOf(character[9])); + skills.put(SkillType.EXCAVATION, Integer.valueOf(character[10])); + skills.put(SkillType.ARCHERY, Integer.valueOf(character[11])); + skills.put(SkillType.SWORDS, Integer.valueOf(character[12])); + skills.put(SkillType.AXES, Integer.valueOf(character[13])); + skills.put(SkillType.ACROBATICS, Integer.valueOf(character[14])); + skills.put(SkillType.FISHING, Integer.valueOf(character[34])); + + return skills; + } } diff --git a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java index d5a59cdbf..1244ff8d8 100644 --- a/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java +++ b/src/main/java/com/gmail/nossr50/database/SQLDatabaseManager.java @@ -5,12 +5,14 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.logging.Level; import com.gmail.nossr50.mcMMO; import com.gmail.nossr50.config.Config; @@ -23,7 +25,6 @@ import com.gmail.nossr50.datatypes.skills.SkillType; import com.gmail.nossr50.datatypes.spout.huds.HudType; import com.gmail.nossr50.runnables.database.SQLReconnectTask; import com.gmail.nossr50.util.Misc; -import com.gmail.nossr50.util.StringUtils; public final class SQLDatabaseManager implements DatabaseManager { private String connectionString; @@ -102,7 +103,16 @@ public final class SQLDatabaseManager implements DatabaseManager { } public void saveUser(PlayerProfile profile) { + checkConnected(); int userId = readId(profile.getPlayerName()); + if (userId == -1) { + newUser(profile.getPlayerName()); + userId = readId(profile.getPlayerName()); + if (userId == -1) { + mcMMO.p.getLogger().log(Level.WARNING, "Failed to save user " + profile.getPlayerName()); + return; + } + } MobHealthbarType mobHealthbarType = profile.getMobHealthbarType(); HudType hudType = profile.getHudType(); @@ -297,7 +307,8 @@ public final class SQLDatabaseManager implements DatabaseManager { statement.setLong(2, System.currentTimeMillis() / Misc.TIME_CONVERSION_FACTOR); statement.execute(); - writeMissingRows(readId(playerName)); + int id = statement.getGeneratedKeys().getInt("id"); + writeMissingRows(id); } catch (SQLException ex) { printErrors(ex); @@ -314,8 +325,7 @@ public final class SQLDatabaseManager implements DatabaseManager { } } - public List loadPlayerData(String playerName) { - List playerData = null; + public PlayerProfile loadPlayerProfile(String playerName, boolean create) { PreparedStatement statement = null; try { @@ -333,22 +343,17 @@ public final class SQLDatabaseManager implements DatabaseManager { + "WHERE u.user = ?"); statement.setString(1, playerName); - playerData = readRow(statement); + ResultSet result = statement.executeQuery(); - if (playerData == null || playerData.size() == 0) { - int userId = readId(playerName); - - // Check if user doesn't exist - if (userId == 0) { - return playerData; + if (result.next()) { + try { + PlayerProfile ret = loadFromResult(playerName, result); + result.close(); + return ret; } - - // Write missing table rows - writeMissingRows(userId); - - // Re-read data - playerData = loadPlayerData(playerName); + catch (SQLException e) {} } + result.close(); } catch (SQLException ex) { printErrors(ex); @@ -363,108 +368,74 @@ public final class SQLDatabaseManager implements DatabaseManager { } } } - return playerData; + + // Problem, nothing was returned + + // First, read User Id - this is to check for orphans + + int id = readId(playerName); + + if (id == -1) { + // There is no such user + if (create) { + newUser(playerName); + return new PlayerProfile(playerName, true); + } + else { + return new PlayerProfile(playerName, false); + } + } + // There is such a user + writeMissingRows(id); + // Retry, and abort on re-failure + return loadPlayerProfile(playerName, false); } - public boolean convert(String[] data) throws Exception { - String playerName = data[0]; + public void convertUsers(DatabaseManager destination) { + PreparedStatement statement = null; - // Check for things we don't want put in the DB - if (playerName == null || playerName.equalsIgnoreCase("null") || playerName.length() > 16) { - return false; + try { + statement = connection.prepareStatement( + "SELECT " + + "s.taming, s.mining, s.repair, s.woodcutting, s.unarmed, s.herbalism, s.excavation, s.archery, s.swords, s.axes, s.acrobatics, s.fishing, " + + "e.taming, e.mining, e.repair, e.woodcutting, e.unarmed, e.herbalism, e.excavation, e.archery, e.swords, e.axes, e.acrobatics, e.fishing, " + + "c.taming, c.mining, c.repair, c.woodcutting, c.unarmed, c.herbalism, c.excavation, c.archery, c.swords, c.axes, c.acrobatics, c.blast_mining, " + + "h.hudtype, h.mobhealthbar " + + "FROM " + tablePrefix + "users u " + + "JOIN " + tablePrefix + "skills s ON (u.id = s.user_id) " + + "JOIN " + tablePrefix + "experience e ON (u.id = e.user_id) " + + "JOIN " + tablePrefix + "cooldowns c ON (u.id = c.user_id) " + + "JOIN " + tablePrefix + "huds h ON (u.id = h.user_id) " + + "WHERE u.user = ?"); + List usernames = getStoredUsers(); + ResultSet result = null; + for (String playerName : usernames) { + statement.setString(1, playerName); + try { + result = statement.executeQuery(); + result.next(); + destination.saveUser(loadFromResult(playerName, result)); + result.close(); + } + catch (SQLException e) { + // Ignore + } + } + } + catch (SQLException e) { + printErrors(e); + } + finally { + if (statement != null) { + try { + statement.close(); + } catch (SQLException e) { + // Ignore + } + } } - String mining = (data.length > 1) ? data[1] : null; - String woodcutting = (data.length > 5) ? data[5] : null; - String repair = (data.length > 7) ? data[7] : null; - String unarmed = (data.length > 8) ? data[8] : null; - String herbalism = (data.length > 9) ? data[9] : null; - String excavation = (data.length > 10) ? data[10] : null; - String archery = (data.length > 11) ? data[11] : null; - String swords = (data.length > 12) ? data[12] : null; - String axes = (data.length > 13) ? data[13] : null; - String acrobatics = (data.length > 14) ? data[14] : null; - String taming = (data.length > 24) ? data[24] : null; - String fishing = (data.length > 34) ? data[34] : null; - - String miningXP = (data.length > 4) ? data[4] : null; - String woodCuttingXP = (data.length > 6) ? data[6] : null;; - String repairXP = (data.length > 15) ? data[15] : null; - String unarmedXP = (data.length > 16) ? data[16] : null; - String herbalismXP = (data.length > 17) ? data[17] : null; - String excavationXP = (data.length > 18) ? data[18] : null; - String archeryXP = (data.length > 19) ? data[19] : null; - String swordsXP = (data.length > 20) ? data[20] : null; - String axesXP = (data.length > 21) ? data[21] : null; - String acrobaticsXP = (data.length > 22) ? data[22] : null; - String tamingXP = (data.length > 25) ? data[25] : null; - String fishingXP = (data.length > 35) ? data[35] : null; - - String superBreakerCooldown = (data.length > 32) ? data[32] : null; - String treeFellerCooldown = (data.length > 28) ? data[28] : null; - String berserkCooldown = (data.length > 26) ? data[26] : null; - String greenTerraCooldown = (data.length > 29) ? data[29] : null; - String gigaDrillBreakerCooldown = (data.length > 27) ? data[27] : null; - String serratedStrikesCooldown = (data.length > 30) ? data[30] : null; - String skullSplitterCooldown = (data.length > 31) ? data[31] : null; - String blastMiningCooldown = (data.length > 36) ? data[36] : null; - - String hudType = (data.length > 33) ? data[33] : null; - String mobHealthbarType = (data.length > 38 ? data[38] : null); - long lastLogin = mcMMO.p.getServer().getOfflinePlayer(playerName).getLastPlayed(); - - int id = readId(playerName); // Check to see if the user is in the DB - - // Create the user if they don't exist - if (id == 0) { - newUser(playerName); - id = readId(playerName); - } - - saveLogin(id, lastLogin); - saveIntegers( - "UPDATE " + tablePrefix + "skills SET " - + " taming = ?, mining = ?, repair = ?, woodcutting = ?" - + ", unarmed = ?, herbalism = ?, excavation = ?" - + ", archery = ?, swords = ?, axes = ?, acrobatics = ?" - + ", fishing = ? WHERE user_id = ?", - StringUtils.getInt(taming), StringUtils.getInt(mining), - StringUtils.getInt(repair), StringUtils.getInt(woodcutting), - StringUtils.getInt(unarmed), StringUtils.getInt(herbalism), - StringUtils.getInt(excavation), StringUtils.getInt(archery), - StringUtils.getInt(swords), StringUtils.getInt(axes), - StringUtils.getInt(acrobatics), StringUtils.getInt(fishing), - id); - saveIntegers( - "UPDATE " + tablePrefix + "experience SET " - + " taming = ?, mining = ?, repair = ?, woodcutting = ?" - + ", unarmed = ?, herbalism = ?, excavation = ?" - + ", archery = ?, swords = ?, axes = ?, acrobatics = ?" - + ", fishing = ? WHERE user_id = ?", - StringUtils.getInt(tamingXP), StringUtils.getInt(miningXP), - StringUtils.getInt(repairXP), StringUtils.getInt(woodCuttingXP), - StringUtils.getInt(unarmedXP), StringUtils.getInt(herbalismXP), - StringUtils.getInt(excavationXP), StringUtils.getInt(archeryXP), - StringUtils.getInt(swordsXP), StringUtils.getInt(axesXP), - StringUtils.getInt(acrobaticsXP), StringUtils.getInt(fishingXP), - id); - saveLongs( - "UPDATE " + tablePrefix + "cooldowns SET " - + " taming = ?, mining = ?, repair = ?, woodcutting = ?" - + ", unarmed = ?, herbalism = ?, excavation = ?" - + ", archery = ?, swords = ?, axes = ?, acrobatics = ?" - + ", blast_mining = ? WHERE user_id = ?", - id, - StringUtils.getLong(null), StringUtils.getLong(superBreakerCooldown), - StringUtils.getLong(null), StringUtils.getInt(treeFellerCooldown), - StringUtils.getLong(berserkCooldown), StringUtils.getLong(greenTerraCooldown), - StringUtils.getLong(gigaDrillBreakerCooldown), StringUtils.getLong(null), - StringUtils.getLong(serratedStrikesCooldown), StringUtils.getLong(skullSplitterCooldown), - StringUtils.getLong(null), StringUtils.getLong(blastMiningCooldown)); - saveHuds(id, hudType, mobHealthbarType); - return true; } - /** * Check connection status and re-establish if dead or stale. * @@ -555,9 +526,35 @@ public final class SQLDatabaseManager implements DatabaseManager { return false; } + public List getStoredUsers() { + ArrayList users = new ArrayList(); + Statement stmt = null; + try { + stmt = connection.createStatement(); + ResultSet result = stmt.executeQuery("SELECT user FROM " + tablePrefix + "users"); + while (result.next()) { + users.add(result.getString("user")); + } + result.close(); + } + catch (SQLException e) { + printErrors(e); + } + finally { + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException e) { + // Ignore + } + } + } + return users; + } + /** - * Attempt to connect to the mySQL database. - */ + * Attempt to connect to the mySQL database. + */ private void connect() { connectionString = "jdbc:mysql://" + Config.getInstance().getMySQLServerName() + ":" + Config.getInstance().getMySQLServerPort() + "/" + Config.getInstance().getMySQLDatabaseName(); @@ -592,8 +589,8 @@ public final class SQLDatabaseManager implements DatabaseManager { } /** - * Attempt to create the database structure. - */ + * Attempt to create the database structure. + */ private void createStructure() { write("CREATE TABLE IF NOT EXISTS `" + tablePrefix + "users` (" + "`id` int(10) unsigned NOT NULL AUTO_INCREMENT," @@ -662,10 +659,10 @@ public final class SQLDatabaseManager implements DatabaseManager { } /** - * Check database structure for missing values. - * - * @param update Type of data to check updates for - */ + * Check database structure for missing values. + * + * @param update Type of data to check updates for + */ private void checkDatabaseStructure(DatabaseUpdateType update) { String sql = ""; @@ -793,11 +790,11 @@ public final class SQLDatabaseManager implements DatabaseManager { } /** - * Attempt to write the SQL query. - * - * @param sql Query to write. - * @return true if the query was successfully written, false otherwise. - */ + * Attempt to write the SQL query. + * + * @param sql Query to write. + * @return true if the query was successfully written, false otherwise. + */ private boolean write(String sql) { if (!checkConnected()) { return false; @@ -828,11 +825,11 @@ public final class SQLDatabaseManager implements DatabaseManager { } /** - * 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 - */ + * 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 + */ private int update(String sql) { int rows = 0; @@ -862,11 +859,11 @@ public final class SQLDatabaseManager implements DatabaseManager { } /** - * Read SQL query. - * - * @param sql SQL query to read - * @return the rows in this SQL query - */ + * Read SQL query. + * + * @param sql SQL query to read + * @return the rows in this SQL query + */ private HashMap> read(String sql) { HashMap> rows = new HashMap>(); @@ -906,45 +903,12 @@ public final class SQLDatabaseManager implements DatabaseManager { return rows; } - private ArrayList readRow(PreparedStatement statement) { - ArrayList playerData = new ArrayList(); - - if (checkConnected()) { - ResultSet resultSet = null; - - try { - resultSet = statement.executeQuery(); - - if (resultSet.next()) { - for (int i = 1; i <= resultSet.getMetaData().getColumnCount(); i++) { - playerData.add(resultSet.getString(i)); - } - } - } - catch (SQLException ex) { - printErrors(ex); - } - finally { - if (statement != null) { - try { - statement.close(); - } - catch (SQLException e) { - // Ignore - } - } - } - } - - return playerData; - } - /** - * Get the Integer. Only return first row / first field. - * - * @param sql SQL query to execute - * @return the value in the first row / first field - */ + * Get the Integer. Only return first row / first field. + * + * @param sql SQL query to execute + * @return the value in the first row / first field + */ private int readInt(PreparedStatement statement) { int result = 0; @@ -1079,7 +1043,7 @@ public final class SQLDatabaseManager implements DatabaseManager { } private int readId(String playerName) { - int id = 0; + int id = -1; try { PreparedStatement statement = connection.prepareStatement("SELECT id FROM " + tablePrefix + "users WHERE user = ?"); @@ -1142,6 +1106,74 @@ public final class SQLDatabaseManager implements DatabaseManager { } } + private PlayerProfile loadFromResult(String playerName, ResultSet result) throws SQLException { + Map skills = new HashMap(); // Skill & Level + Map skillsXp = new HashMap(); // Skill & XP + Map skillsDATS = new HashMap(); // Ability & Cooldown + HudType hudType; + MobHealthbarType mobHealthbarType; + + final int OFFSET_SKILLS = 0; // TODO update these numbers when the query changes (a new skill is added) + final int OFFSET_XP = 12; + final int OFFSET_DATS = 24; + final int OFFSET_OTHER = 36; + + skills.put(SkillType.TAMING, result.getInt(OFFSET_SKILLS + 1)); + skills.put(SkillType.MINING, result.getInt(OFFSET_SKILLS + 2)); + skills.put(SkillType.REPAIR, result.getInt(OFFSET_SKILLS + 3)); + skills.put(SkillType.WOODCUTTING, result.getInt(OFFSET_SKILLS + 4)); + skills.put(SkillType.UNARMED, result.getInt(OFFSET_SKILLS + 5)); + skills.put(SkillType.HERBALISM, result.getInt(OFFSET_SKILLS + 6)); + skills.put(SkillType.EXCAVATION, result.getInt(OFFSET_SKILLS + 7)); + skills.put(SkillType.ARCHERY, result.getInt(OFFSET_SKILLS + 8)); + skills.put(SkillType.SWORDS, result.getInt(OFFSET_SKILLS + 9)); + skills.put(SkillType.AXES, result.getInt(OFFSET_SKILLS + 10)); + skills.put(SkillType.ACROBATICS, result.getInt(OFFSET_SKILLS + 11)); + skills.put(SkillType.FISHING, result.getInt(OFFSET_SKILLS + 12)); + + skillsXp.put(SkillType.TAMING, result.getFloat(OFFSET_XP + 1)); + skillsXp.put(SkillType.MINING, result.getFloat(OFFSET_XP + 2)); + skillsXp.put(SkillType.REPAIR, result.getFloat(OFFSET_XP + 3)); + skillsXp.put(SkillType.WOODCUTTING, result.getFloat(OFFSET_XP + 4)); + skillsXp.put(SkillType.UNARMED, result.getFloat(OFFSET_XP + 5)); + skillsXp.put(SkillType.HERBALISM, result.getFloat(OFFSET_XP + 6)); + skillsXp.put(SkillType.EXCAVATION, result.getFloat(OFFSET_XP + 7)); + skillsXp.put(SkillType.ARCHERY, result.getFloat(OFFSET_XP + 8)); + skillsXp.put(SkillType.SWORDS, result.getFloat(OFFSET_XP + 9)); + skillsXp.put(SkillType.AXES, result.getFloat(OFFSET_XP + 10)); + skillsXp.put(SkillType.ACROBATICS, result.getFloat(OFFSET_XP + 11)); + skillsXp.put(SkillType.FISHING, result.getFloat(OFFSET_XP + 12)); + + // Taming - Unused - result.getInt(OFFSET_DATS + 1) + skillsDATS.put(AbilityType.SUPER_BREAKER, result.getInt(OFFSET_DATS + 2)); + // Repair - Unused - result.getInt(OFFSET_DATS + 3) + skillsDATS.put(AbilityType.TREE_FELLER, result.getInt(OFFSET_DATS + 4)); + skillsDATS.put(AbilityType.BERSERK, result.getInt(OFFSET_DATS + 5)); + skillsDATS.put(AbilityType.GREEN_TERRA, result.getInt(OFFSET_DATS + 6)); + skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, result.getInt(OFFSET_DATS + 7)); + // Archery - Unused - result.getInt(OFFSET_DATS + 8) + skillsDATS.put(AbilityType.SERRATED_STRIKES, result.getInt(OFFSET_DATS + 9)); + skillsDATS.put(AbilityType.SKULL_SPLITTER, result.getInt(OFFSET_DATS + 10)); + // Acrobatics - Unused - result.getInt(OFFSET_DATS + 11) + skillsDATS.put(AbilityType.BLAST_MINING, result.getInt(OFFSET_DATS + 12)); + + try { + hudType = HudType.valueOf(result.getString(OFFSET_OTHER + 1)); + } + catch (Exception e) { + hudType = HudType.STANDARD; // Shouldn't happen unless database is being tampered with + } + + try { + mobHealthbarType = MobHealthbarType.valueOf(result.getString(OFFSET_OTHER + 2)); + } + catch (Exception e) { + mobHealthbarType = Config.getInstance().getMobHealthbarDefault(); + } + + return new PlayerProfile(playerName, skills, skillsXp, skillsDATS, hudType, mobHealthbarType); + } + private void printErrors(SQLException ex) { mcMMO.p.getLogger().severe("SQLException: " + ex.getMessage()); mcMMO.p.getLogger().severe("SQLState: " + ex.getSQLState()); diff --git a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java index c6b784666..d3b5b9920 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java @@ -92,10 +92,10 @@ public class McMMOPlayer { String playerName = player.getName(); this.player = player; - profile = new PlayerProfile(playerName, true); + profile = mcMMO.getDatabaseManager().loadPlayerProfile(playerName, true); party = PartyManager.getPlayerParty(playerName); - /* + /* * I'm using this method because it makes code shorter and safer (we don't have to add all SkillTypes manually), * but I actually have no idea about the performance impact, if there is any. * If in the future someone wants to remove this, don't forget to also remove what is in the SkillType enum. - bm01 diff --git a/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java b/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java index 6caa04731..8e89f7786 100644 --- a/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java +++ b/src/main/java/com/gmail/nossr50/datatypes/player/PlayerProfile.java @@ -1,7 +1,6 @@ package com.gmail.nossr50.datatypes.player; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; @@ -32,7 +31,7 @@ public class PlayerProfile { private final Map skillsXp = new HashMap(); // Skill & XP private final Map skillsDATS = new HashMap(); // Ability & Cooldown - public PlayerProfile(String playerName, boolean addNew) { + public PlayerProfile(String playerName) { this.playerName = playerName; hudType = mcMMO.isSpoutEnabled() ? SpoutConfig.getInstance().getDefaultHudType() : HudType.DISABLED; @@ -46,11 +45,27 @@ public class PlayerProfile { skills.put(skillType, 0); skillsXp.put(skillType, 0F); } + } - if (!loadPlayer() && addNew) { - mcMMO.getDatabaseManager().newUser(playerName); - loaded = true; - } + public PlayerProfile(String playerName, boolean isLoaded) { + this(playerName); + this.loaded = isLoaded; + } + + /** + * Calling this constructor is considered loading the profile. + */ + public PlayerProfile(String playerName, Map argSkills, Map argSkillsXp, Map argSkillsDats, HudType hudType, MobHealthbarType mobHealthbarType) { + this(playerName, true); + + this.hudType = hudType; + this.mobHealthbarType = mobHealthbarType; + + this.skills.putAll(argSkills); + this.skillsXp.putAll(argSkillsXp); + this.skillsDATS.putAll(argSkillsDats); + + loaded = true; } public void save() { @@ -254,68 +269,4 @@ public class PlayerProfile { return sum / parents.size(); } - - private boolean loadPlayer() { - List playerData = mcMMO.getDatabaseManager().loadPlayerData(playerName); - - if (playerData == null || playerData.isEmpty()) { - return false; - } - - skills.put(SkillType.TAMING, Integer.valueOf(playerData.get(0))); - skills.put(SkillType.MINING, Integer.valueOf(playerData.get(1))); - skills.put(SkillType.REPAIR, Integer.valueOf(playerData.get(2))); - skills.put(SkillType.WOODCUTTING, Integer.valueOf(playerData.get(3))); - skills.put(SkillType.UNARMED, Integer.valueOf(playerData.get(4))); - skills.put(SkillType.HERBALISM, Integer.valueOf(playerData.get(5))); - skills.put(SkillType.EXCAVATION, Integer.valueOf(playerData.get(6))); - skills.put(SkillType.ARCHERY, Integer.valueOf(playerData.get(7))); - skills.put(SkillType.SWORDS, Integer.valueOf(playerData.get(8))); - skills.put(SkillType.AXES, Integer.valueOf(playerData.get(9))); - skills.put(SkillType.ACROBATICS, Integer.valueOf(playerData.get(10))); - skills.put(SkillType.FISHING, Integer.valueOf(playerData.get(11))); - - skillsXp.put(SkillType.TAMING, (float) Integer.valueOf(playerData.get(12))); - skillsXp.put(SkillType.MINING, (float) Integer.valueOf(playerData.get(13))); - skillsXp.put(SkillType.REPAIR, (float) Integer.valueOf(playerData.get(14))); - skillsXp.put(SkillType.WOODCUTTING, (float) Integer.valueOf(playerData.get(15))); - skillsXp.put(SkillType.UNARMED, (float) Integer.valueOf(playerData.get(16))); - skillsXp.put(SkillType.HERBALISM, (float) Integer.valueOf(playerData.get(17))); - skillsXp.put(SkillType.EXCAVATION, (float) Integer.valueOf(playerData.get(18))); - skillsXp.put(SkillType.ARCHERY, (float) Integer.valueOf(playerData.get(19))); - skillsXp.put(SkillType.SWORDS, (float) Integer.valueOf(playerData.get(20))); - skillsXp.put(SkillType.AXES, (float) Integer.valueOf(playerData.get(21))); - skillsXp.put(SkillType.ACROBATICS, (float) Integer.valueOf(playerData.get(22))); - skillsXp.put(SkillType.FISHING, (float) Integer.valueOf(playerData.get(23))); - - // Taming 24 - Unused - skillsDATS.put(AbilityType.SUPER_BREAKER, Integer.valueOf(playerData.get(25))); - // Repair 26 - Unused - skillsDATS.put(AbilityType.TREE_FELLER, Integer.valueOf(playerData.get(27))); - skillsDATS.put(AbilityType.BERSERK, Integer.valueOf(playerData.get(28))); - skillsDATS.put(AbilityType.GREEN_TERRA, Integer.valueOf(playerData.get(29))); - skillsDATS.put(AbilityType.GIGA_DRILL_BREAKER, Integer.valueOf(playerData.get(30))); - // Archery 31 - Unused - skillsDATS.put(AbilityType.SERRATED_STRIKES, Integer.valueOf(playerData.get(32))); - skillsDATS.put(AbilityType.SKULL_SPLITTER, Integer.valueOf(playerData.get(33))); - // Acrobatics 34 - Unused - skillsDATS.put(AbilityType.BLAST_MINING, Integer.valueOf(playerData.get(35))); - - try { - hudType = HudType.valueOf(playerData.get(36)); - } - catch (Exception e) { - hudType = HudType.STANDARD; // Shouldn't happen unless database is being tampered with - } - - try { - mobHealthbarType = MobHealthbarType.valueOf(playerData.get(37)); - } - catch (Exception e) { - mobHealthbarType = Config.getInstance().getMobHealthbarDefault(); - } - - loaded = true; - return true; - } } diff --git a/src/main/java/com/gmail/nossr50/runnables/PlayerUpdateInventoryTask.java b/src/main/java/com/gmail/nossr50/runnables/PlayerUpdateInventoryTask.java index 56c56a5c9..acf5043bf 100644 --- a/src/main/java/com/gmail/nossr50/runnables/PlayerUpdateInventoryTask.java +++ b/src/main/java/com/gmail/nossr50/runnables/PlayerUpdateInventoryTask.java @@ -3,6 +3,7 @@ package com.gmail.nossr50.runnables; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; +@SuppressWarnings("deprecation") public class PlayerUpdateInventoryTask extends BukkitRunnable { private Player player; diff --git a/src/main/java/com/gmail/nossr50/runnables/database/ConversionTask.java b/src/main/java/com/gmail/nossr50/runnables/database/ConversionTask.java new file mode 100644 index 000000000..c20f291c0 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/runnables/database/ConversionTask.java @@ -0,0 +1,41 @@ +package com.gmail.nossr50.runnables.database; + +import java.util.logging.Level; + +import org.bukkit.command.CommandSender; +import org.bukkit.scheduler.BukkitRunnable; + +import com.gmail.nossr50.mcMMO; +import com.gmail.nossr50.database.DatabaseManager; +import com.gmail.nossr50.locale.LocaleLoader; + +public class ConversionTask extends BukkitRunnable { + private final DatabaseManager sourceDb; + private final CommandSender sender; + private final String message; + + public ConversionTask(DatabaseManager from, CommandSender sendback, String oldType, String newType) { + sourceDb = from; + sender = sendback; + message = LocaleLoader.getString("Commands.mmoupdate.Finish", oldType, newType); + } + + @Override + public void run() { + sourceDb.convertUsers(mcMMO.getDatabaseManager()); + + // Announce completeness + mcMMO.p.getServer().getScheduler().runTask(mcMMO.p, new CompleteAnnouncement()); + } + + public class CompleteAnnouncement implements Runnable { + @Override + public void run() { + try { + sender.sendMessage(message); + } catch (Exception e) { + mcMMO.p.getLogger().log(Level.WARNING, "Exception sending database conversion completion message to " + sender.getName(), e); + } + } + } +} diff --git a/src/main/java/com/gmail/nossr50/runnables/database/SQLConversionTask.java b/src/main/java/com/gmail/nossr50/runnables/database/SQLConversionTask.java deleted file mode 100644 index 722a496cc..000000000 --- a/src/main/java/com/gmail/nossr50/runnables/database/SQLConversionTask.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.gmail.nossr50.runnables.database; - -import java.io.BufferedReader; -import java.io.FileReader; - -import org.bukkit.scheduler.BukkitRunnable; - -import com.gmail.nossr50.mcMMO; - -public class SQLConversionTask extends BukkitRunnable { - - @Override - public void run() { - String location = mcMMO.getUsersFilePath(); - - try { - BufferedReader in = new BufferedReader(new FileReader(location)); - String line = ""; - int converted = 0; - - while ((line = in.readLine()) != null) { - - // Find if the line contains the player we want. - String[] playerData = line.split(":"); - if (mcMMO.getDatabaseManager().convert(playerData)) { - converted++; - } - } - - mcMMO.p.getLogger().info("MySQL Updated from users file, " + converted + " items added/updated to MySQL DB"); - in.close(); - } - catch (Exception e) { - mcMMO.p.getLogger().severe("Exception while reading " + location + " (Are you sure you formatted it correctly?)" + e.toString()); - } - } -} diff --git a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java index cf4b18638..971fccbf3 100644 --- a/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java +++ b/src/main/java/com/gmail/nossr50/util/commands/CommandRegistrationManager.java @@ -19,6 +19,7 @@ import com.gmail.nossr50.commands.chat.AdminChatCommand; import com.gmail.nossr50.commands.chat.PartyChatCommand; import com.gmail.nossr50.commands.database.McpurgeCommand; import com.gmail.nossr50.commands.database.McremoveCommand; +import com.gmail.nossr50.commands.database.MmoshowdbCommand; import com.gmail.nossr50.commands.database.MmoupdateCommand; import com.gmail.nossr50.commands.experience.AddlevelsCommand; import com.gmail.nossr50.commands.experience.AddxpCommand; @@ -276,11 +277,20 @@ public final class CommandRegistrationManager { PluginCommand command = mcMMO.p.getCommand("mmoupdate"); command.setDescription(LocaleLoader.getString("Commands.Description.mmoupdate")); command.setPermission("mcmmo.commands.mmoupdate"); - command.setPermissionMessage(permissionsMessage); - command.setUsage(LocaleLoader.getString("Commands.Usage.0", "mmoupdate")); + command.setPermissionMessage(LocaleLoader.getString("Commands.mmoupdate.OpOnly")); + command.setUsage(LocaleLoader.getString("Commands.Usage.1", "mmoupdate", "")); command.setExecutor(new MmoupdateCommand()); } + private static void registerMmoshowdbCommand() { + PluginCommand command = mcMMO.p.getCommand("mmoshowdb"); + command.setDescription(LocaleLoader.getString("Commands.Description.mmoshowdb")); + command.setPermission("mcmmo.commands.mmoshowdb"); + command.setPermissionMessage(permissionsMessage); + command.setUsage(LocaleLoader.getString("Commands.Usage.0", "mmoshowdb")); + command.setExecutor(new MmoshowdbCommand()); + } + private static void registerAdminChatCommand() { PluginCommand command = mcMMO.p.getCommand("adminchat"); command.setDescription(LocaleLoader.getString("Commands.Description.adminchat")); @@ -421,6 +431,7 @@ public final class CommandRegistrationManager { registerMcpurgeCommand(); registerMcremoveCommand(); registerMmoupdateCommand(); + registerMmoshowdbCommand(); // Experience Commands registerAddlevelsCommand(); diff --git a/src/main/resources/locale/locale_en_US.properties b/src/main/resources/locale/locale_en_US.properties index 563f91049..67d7ff569 100644 --- a/src/main/resources/locale/locale_en_US.properties +++ b/src/main/resources/locale/locale_en_US.properties @@ -442,8 +442,11 @@ Commands.mmoedit=[player] [[RED]] - Modify target Commands.mmoedit.AllSkills.1=[[GREEN]]Your level in all skills was set to {0}! Commands.mmoedit.Modified.1=[[GREEN]]Your level in {0} was set to {1}! Commands.mmoedit.Modified.2=[[RED]]{0} has been modified for {1}. -Commands.mmoupdate.Start=[[GRAY]]Starting conversion... -Commands.mmoupdate.Finish=[[GREEN]]Conversion finished! +Commands.mmoupdate.Same=[[RED]]You are already using the {0} database! +Commands.mmoupdate.InvalidType=[[RED]]{0} is not a valid database type. +Commands.mmoupdate.Start=[[GRAY]]Starting conversion from {0} to {1}... +Commands.mmoupdate.Finish=[[GRAY]]Database migration complete; the {1} database now has all data from the {0} database. +Commands.mmoshowdb=[[YELLOW]]The currently used database is [[GREEN]]{0} Commands.ModDescription=[[RED]]- Read brief mod description Commands.NoConsole=This command does not support console usage. Commands.Notifications.Off=Ability notifications toggled [[RED]]off @@ -505,6 +508,7 @@ Commands.Usage.0=[[RED]]Proper usage is /{0} Commands.Usage.1=[[RED]]Proper usage is /{0} {1} Commands.Usage.2=[[RED]]Proper usage is /{0} {1} {2} Commands.Usage.3=[[RED]]Proper usage is /{0} {1} {2} {3} +Commands.Usage.FullClassName=classname Commands.Usage.Level=level Commands.Usage.Message=message Commands.Usage.Page=page @@ -751,7 +755,8 @@ Commands.Description.mcremove=Remove a user from the mcMMO database Commands.Description.mcstats=Show your mcMMO levels and XP Commands.Description.mctop=Show mcMMO leader boards Commands.Description.mmoedit=Edit mcMMO levels for a user -Commands.Description.mmoupdate=Convert mcMMO database from Flatfile to MySQL +Commands.Description.mmoupdate=Migrate mcMMO database from an old database into the current one +Commands.Description.mmoshowdb=Show the name of the current database type (for later use with /mmoupdate) Commands.Description.party=Control various mcMMO party settings Commands.Description.partychat=Toggle mcMMO party chat on/off or send party chat messages Commands.Description.ptp=Teleport to an mcMMO party member @@ -774,4 +779,4 @@ Scoreboard.Misc.PowerLevel=Power Level Scoreboard.Misc.Level=Level Scoreboard.Misc.CurrentXP=Current XP Scoreboard.Misc.RemainingXP=Remaining XP -Scoreboard.Misc.Overall=Overall \ No newline at end of file +Scoreboard.Misc.Overall=Overall diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 66d105419..210eeae8a 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -58,7 +58,9 @@ commands: inspect: description: View detailed mcMMO info on another player mmoupdate: - description: Convert from Flat File to MySQL + description: Migrate mcMMO database from an old database type to the current + mmoshowdb: + description: Show the name of the current database type (for later use with /mmoupdate) partychat: aliases: [pc, p] description: Toggle Party chat or send party chat messages @@ -717,6 +719,7 @@ permissions: mcmmo.commands.mmoedit: true mcmmo.commands.mmoedit.others: true mcmmo.commands.mmoupdate: true + mcmmo.commands.mmoshowdb: true mcmmo.commands.ptp.world.all: true mcmmo.commands.skillreset.all: true mcmmo.commands.vampirism.all: true @@ -908,6 +911,8 @@ permissions: description: Allows access to the mmoedit command for other players mcmmo.commands.mmoupdate: description: Allows access to the mmoupdate command + mcmmo.commands.mmoshowdb: + description: Allows access to the mmoshowdb command mcmmo.commands.mobhealth: default: true description: Allows access to the mobhealth command